From 59a4afa18877930fbc3ee2c66526cc68582144ba Mon Sep 17 00:00:00 2001 From: "fabro-sh-0530[bot]" <281434857+fabro-sh-0530[bot]@users.noreply.github.com> Date: Sun, 31 May 2026 11:18:01 -0400 Subject: [PATCH] Make run actors and provenance total; remove Principal::Anonymous (#463) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Removes `Principal::Anonymous` as an actor variant and makes run creator/provenance non-optional across the full stack — Rust types, OpenAPI schema, generated TypeScript client, and frontend components. Every persisted run now has a mandatory creator; there are no nullable `created_by`, `provenance`, or `subject` fields anywhere in the system. As a separate cleanup, all `cargo` invocations in CI and dev tooling gain `--locked` to prevent unintended dependency resolution drift. ### Plan Summary - **Commit 1 – Remove `Principal::Anonymous`**: `RequestAuthContext.principal` becomes `Option`; `initial()` and `rejected()` set `None`; all auth gate helpers match on `Option`; HTTP log computes `principal_kind` as `principal.as_ref().map_or("none", Principal::kind)`; `PrincipalAnonymous` removed from the OpenAPI schema and generated client; frontend `principalDisplay` drops the `"anonymous"` case. - **Commit 2 – Total provenance**: `RunProvenance.subject`, `RunSpec.provenance`, `Run.created_by`, and `RunCreatedProps.provenance` all become non-optional. `Default` derive dropped from `RunProvenance`. Demo mode gets a synthetic `DEMO_PRINCIPAL` via `AuthMethod::DevToken`. A new `fabro-types` `test-support` feature exports `test_principal()` and `test_run_provenance()` used across all affected crates and TS tests. All `provenance: None` / `created_by: null` literals in tests are replaced with the test fixture. - **`--locked` CI hardening**: Every `cargo` invocation in GitHub Actions workflows and `fabro-dev` tooling commands now passes `--locked`. ### Key design decisions - `None` principal (unauthenticated / rejected requests) is now structurally distinct from any actor variant — no sentinel value that could accidentally pass actor checks. - `RunProvenance` no longer implements `Default` because a provenance with no subject is incoherent; callers must supply a real actor at construction time. - The `cancel_run` handler was refactored as part of this change to correctly handle in-process blocked runs: a new test (`cancel_durably_blocked_in_process_run_cancels_pending_interview_without_abort_signal`) covers the cancellation path that should let the workflow unwind rather than aborting it. - Test fixtures live under the existing `test-support` feature flag pattern; no fake-auth helpers were added to the `fixtures` module. ### Fabro Details
Ran 9 stages in 129m 57s for $53.54 | Stage | Duration | Cost | Retries | |---|---|---|---| | start | 0s | – | 0 | | toolchain | 1s | – | 0 | | preflight_compile | 2m 10s | – | 0 | | preflight_lint | 2m 22s | – | 0 | | implement | 76m 55s | $47.53 | 0 | | simplify_opus | 14m 46s | $3.21 | 0 | | simplify_gpt | 2m 26s | $1.04 | 0 | | verify | 14m 44s | – | 0 | | fixup | 15m 43s | $1.76 | 0 | | **Total** | **129m 57s** | **$53.54** | **0** |
Ran ImplementPlan.fabro (11 nodes and 14 edges) ```dot digraph ImplementPlan { graph [ goal="Implement and simplify", model_stylesheet=" * { model: claude-opus-4-7; } " ] rankdir=LR start [shape=Mdiamond, label="Start"] exit [shape=Msquare, label="Exit"] toolchain [label="Toolchain", shape=parallelogram, script="command -v cargo >/dev/null || { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && sudo ln -sf $HOME/.cargo/bin/* /usr/local/bin/; }; cargo --version 2>&1", max_retries=0] preflight_compile [label="Preflight Compile", shape=parallelogram, script="cargo check -q --workspace 2>&1", max_retries=0] preflight_lint [label="Preflight Lint", shape=parallelogram, script="cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1", max_retries=0] fix_lints [label="Fix Lints", prompt="The preflight lint step failed. Read the build output from context and fix all clippy lint warnings.", max_visits=3] implement [label="Implement", prompt="Read the plan file referenced in the goal and implement every step. Make all the code changes described in the plan. Use red/green TDD.", model="gpt-55", reasoning_effort="xhigh"] simplify_opus [label="Simplify (Opus)", prompt="@prompts/simplify.md"] simplify_gpt [label="Simplify (GPT-55)", prompt="@prompts/simplify.md", model="gpt-55"] verify [label="Verify", shape=parallelogram, script="git fetch origin main 2>&1 && git merge --no-edit --no-stat origin/main 2>&1 && cargo +nightly-2026-04-14 fmt --all 2>&1 && cargo dev docs refresh 2>&1 && cargo +nightly-2026-04-14 fmt --check --all 2>&1 && { command -v rg >/dev/null 2>&1 || { echo 'rg is required for verify'; exit 127; }; } && ! rg -n 'AuthMode::Disabled|RunAuthMethod|RunSubjectProvenance|\bActorRef\b|\bActorKind\b|AuthenticatedSubject|AuthenticatedService|AuthorizeRunScoped|AuthorizeRunBlob|AuthorizeStageArtifact|AuthorizeCommandLog|auth_method\s*==\s*\"disabled\"' lib/crates apps lib/packages docs/public/api-reference/fabro-api.yaml 2>&1 && cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings 2>&1 && cargo nextest run --workspace --status-level slow --profile ci 2>&1 && cargo dev docs check 2>&1 && bun install --frozen-lockfile 2>&1 && (cd apps/fabro-web && bun run typecheck) 2>&1 && (cd apps/fabro-web && bun run test) 2>&1 && (cd lib/packages/fabro-api-client && bun run typecheck) 2>&1 && cargo dev build -- -p fabro-cli --release 2>&1", goal_gate=true, retry_target="fixup"] fixup [label="Fixup", prompt="The verify step failed. Read the build output from context and fix all format, clippy, Rust test, docs, TypeScript typecheck/test, and build failures.", max_visits=3] start -> toolchain toolchain -> preflight_compile [condition="outcome=succeeded"] toolchain -> exit preflight_compile -> preflight_lint [condition="outcome=succeeded"] preflight_compile -> exit preflight_lint -> implement [condition="outcome=succeeded"] preflight_lint -> fix_lints fix_lints -> preflight_lint implement -> simplify_opus -> simplify_gpt -> verify verify -> exit [condition="outcome=succeeded"] verify -> fixup fixup -> verify } ```
⚒️ Generated with [Fabro](https://fabro.sh) --------- Co-authored-by: fabro-sh-0530[bot] <281434857+fabro-sh-0530[bot]@users.noreply.github.com> Co-authored-by: Fabro Co-authored-by: Bryan Helmkamp <19+brynary@users.noreply.github.com> Co-authored-by: Release Repro --- .github/workflows/nightly.yml | 2 +- .github/workflows/release.yml | 10 +- .github/workflows/rust.yml | 8 +- .github/workflows/typescript.yml | 2 +- Cargo.lock | 1 + .../app/components/run-summary-panel.test.tsx | 13 +-- .../app/components/run-summary-panel.tsx | 19 ++-- .../components/runs-list/run-table-row.tsx | 12 +-- apps/fabro-web/app/data/runs.test.ts | 3 +- apps/fabro-web/app/data/runs.ts | 2 +- apps/fabro-web/app/lib/principal-display.tsx | 6 -- apps/fabro-web/app/lib/run-actions.test.ts | 3 +- apps/fabro-web/app/lib/test-fixtures.ts | 8 ++ .../app/routes/automations-new.test.tsx | 3 +- apps/fabro-web/app/routes/automations.tsx | 4 +- apps/fabro-web/app/routes/run-detail.test.ts | 3 +- .../app/routes/run-files.render.test.tsx | 3 +- .../app/routes/runs.preferences.test.tsx | 3 +- apps/fabro-web/app/routes/runs.test.tsx | 3 +- docs/internal/events-strategy.md | 5 +- docs/internal/events.md | 15 ++- docs/internal/logging-strategy.md | 4 +- docs/public/api-reference/fabro-api.yaml | 26 ++---- docs/public/changelog/2026-05-02.mdx | 4 +- lib/crates/fabro-api/Cargo.toml | 3 + .../fabro-api/tests/principal_round_trip.rs | 5 +- .../fabro-api/tests/run_event_round_trip.rs | 8 +- .../tests/run_projection_round_trip.rs | 5 +- .../fabro-api/tests/run_summary_round_trip.rs | 16 +++- lib/crates/fabro-cli/Cargo.toml | 1 + .../fabro-cli/src/commands/run/attach.rs | 3 +- lib/crates/fabro-cli/tests/it/cmd/inspect.rs | 13 ++- lib/crates/fabro-cli/tests/it/cmd/support.rs | 3 + lib/crates/fabro-cli/tests/it/support/mod.rs | 3 +- .../fabro-dev/src/commands/docker_build.rs | 3 +- .../src/commands/docs_cli_reference.rs | 1 + lib/crates/fabro-dev/src/commands/release.rs | 2 + lib/crates/fabro-dev/tests/it/docker_build.rs | 6 +- lib/crates/fabro-dev/tests/it/release.rs | 4 +- lib/crates/fabro-dump/src/lib.rs | 3 +- lib/crates/fabro-server/src/auth/cli_flow.rs | 18 ++-- lib/crates/fabro-server/src/demo/mod.rs | 17 +++- .../fabro-server/src/principal_middleware.rs | 72 ++++++--------- lib/crates/fabro-server/src/run_files.rs | 4 +- lib/crates/fabro-server/src/run_manifest.rs | 7 +- lib/crates/fabro-server/src/server.rs | 17 ++-- .../fabro-server/src/server/handler/events.rs | 4 +- .../src/server/handler/lifecycle.rs | 81 +++++++++++------ .../fabro-server/src/server/handler/pair.rs | 4 +- .../fabro-server/src/server/handler/runs.rs | 5 +- .../src/server/handler/sandbox.rs | 3 +- .../src/server/handler/sessions.rs | 3 +- lib/crates/fabro-server/src/server/tests.rs | 91 ++++++++++++++++--- lib/crates/fabro-server/src/web_auth.rs | 2 +- .../fabro-server/tests/it/api/run_files.rs | 4 +- lib/crates/fabro-store/Cargo.toml | 1 + lib/crates/fabro-store/src/run_state.rs | 45 +++++---- lib/crates/fabro-store/src/slate/mod.rs | 6 +- lib/crates/fabro-store/src/slate/run_store.rs | 3 +- .../tests/serializable_projection.rs | 4 +- lib/crates/fabro-tool/Cargo.toml | 1 + lib/crates/fabro-tool/src/common.rs | 6 +- lib/crates/fabro-tool/src/create.rs | 4 +- lib/crates/fabro-tool/src/interact.rs | 4 +- lib/crates/fabro-tool/src/search.rs | 6 +- lib/crates/fabro-types/Cargo.toml | 1 + lib/crates/fabro-types/src/lib.rs | 2 + lib/crates/fabro-types/src/principal.rs | 20 +--- lib/crates/fabro-types/src/run.rs | 8 +- lib/crates/fabro-types/src/run_event/mod.rs | 6 +- lib/crates/fabro-types/src/run_event/run.rs | 3 +- lib/crates/fabro-types/src/run_projection.rs | 8 +- lib/crates/fabro-types/src/run_summary.rs | 3 +- lib/crates/fabro-types/src/test_support.rs | 19 ++++ .../fabro-types/tests/run_event_serde.rs | 8 +- .../fabro-types/tests/run_spec_methods.rs | 3 +- .../fabro-types/tests/run_spec_serde.rs | 6 +- .../fabro-workflow/src/billing_rollup.rs | 4 +- .../fabro-workflow/src/event/convert.rs | 38 ++++---- lib/crates/fabro-workflow/src/event/events.rs | 3 +- lib/crates/fabro-workflow/src/event/sink.rs | 3 +- .../fabro-workflow/src/event/stored_fields.rs | 2 +- lib/crates/fabro-workflow/src/git.rs | 4 +- .../fabro-workflow/src/handler/agent.rs | 4 +- .../fabro-workflow/src/handler/command.rs | 6 +- .../fabro-workflow/src/handler/llm/api.rs | 3 +- .../fabro-workflow/src/handler/parallel.rs | 4 +- .../fabro-workflow/src/handler/prompt.rs | 4 +- .../fabro-workflow/src/lifecycle/git.rs | 4 +- .../fabro-workflow/src/operations/archive.rs | 6 +- .../fabro-workflow/src/operations/create.rs | 28 +++--- .../fabro-workflow/src/operations/fork.rs | 4 +- .../fabro-workflow/src/operations/retry.rs | 36 +++----- .../fabro-workflow/src/operations/start.rs | 8 +- .../fabro-workflow/src/operations/timeline.rs | 3 +- .../src/pipeline/execute/tests.rs | 8 +- .../fabro-workflow/src/pipeline/finalize.rs | 6 +- .../fabro-workflow/src/pipeline/initialize.rs | 4 +- .../fabro-workflow/src/pipeline/persist.rs | 4 +- .../src/pipeline/pull_request.rs | 18 ++-- lib/crates/fabro-workflow/src/run_lookup.rs | 4 +- lib/crates/fabro-workflow/src/run_metadata.rs | 6 +- .../fabro-workflow/src/runtime_store.rs | 6 +- lib/crates/fabro-workflow/src/test_support.rs | 8 +- .../src/.openapi-generator/FILES | 1 - .../fabro-api-client/src/models/index.ts | 1 - .../src/models/principal-anonymous.ts | 25 ----- .../fabro-api-client/src/models/principal.ts | 5 +- .../src/models/run-provenance.ts | 2 +- .../fabro-api-client/src/models/run-spec.ts | 2 +- .../fabro-api-client/src/models/run.ts | 2 +- .../tests/principal-exhaustive.ts | 2 - 112 files changed, 577 insertions(+), 431 deletions(-) create mode 100644 apps/fabro-web/app/lib/test-fixtures.ts create mode 100644 lib/crates/fabro-types/src/test_support.rs delete mode 100644 lib/packages/fabro-api-client/src/models/principal-anonymous.ts diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index f63324d41..5dcc0ce3c 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -67,4 +67,4 @@ jobs: git remote set-url origin \ "https://x-access-token:${release_token}@github.com/${GITHUB_REPOSITORY}.git" unset release_token - cargo dev release --nightly + cargo --locked dev release --nightly diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 91519569b..4410f71f7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -84,7 +84,7 @@ jobs: - uses: taiki-e/install-action@773334c0e05d7e699e4d78234494308223f3a2cf # nextest - name: Refresh embedded SPA - run: cargo dev spa refresh + run: cargo --locked dev spa refresh - name: Test (x86_64-musl) # nextest still shells through cargo test for this target, so @@ -93,22 +93,22 @@ jobs: env: CC_x86_64_unknown_linux_musl: musl-gcc CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER: musl-gcc - run: cargo nextest run --workspace --target ${{ matrix.target }} --release --status-level slow --profile ci + run: cargo nextest run --locked --workspace --target ${{ matrix.target }} --release --status-level slow --profile ci - name: Test # aarch64-musl test runs have not been validated on the compile # runner yet; shipping binary is exercised via Docker smoke tests. # Re-enable after verifying the workspace passes on this target. if: matrix.target != 'aarch64-unknown-linux-musl' && matrix.target != 'x86_64-unknown-linux-musl' - run: cargo nextest run --workspace --target ${{ matrix.target }} --release --status-level slow --profile ci + run: cargo nextest run --locked --workspace --target ${{ matrix.target }} --release --status-level slow --profile ci - name: Build (musl via cargo-zigbuild) if: matrix.musl - run: cargo zigbuild --target ${{ matrix.target }} --release -p fabro-cli + run: cargo zigbuild --locked --target ${{ matrix.target }} --release -p fabro-cli - name: Build if: ${{ !matrix.musl }} - run: cargo build --target ${{ matrix.target }} --release -p fabro-cli + run: cargo build --locked --target ${{ matrix.target }} --release -p fabro-cli - name: Package run: | diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index cc3582ecf..c20fe22de 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -76,7 +76,7 @@ jobs: run: | ! rg -n 'AuthMode::Disabled|RunAuthMethod|RunSubjectProvenance|\bActorRef\b|\bActorKind\b|AuthenticatedSubject|AuthenticatedService|AuthorizeRunScoped|AuthorizeRunBlob|AuthorizeStageArtifact|AuthorizeCommandLog|auth_method\s*==\s*"disabled"' \ lib/crates apps lib/packages docs/public/api-reference/fabro-api.yaml - - run: cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings + - run: cargo +nightly-2026-04-14 clippy --locked --workspace --all-targets -- -D warnings generated-docs: name: Generated Docs @@ -91,7 +91,7 @@ jobs: - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2 with: cache-on-failure: true - - run: cargo dev docs check + - run: cargo --locked dev docs check test: name: Test (Linux) @@ -107,7 +107,7 @@ jobs: with: cache-on-failure: true - uses: taiki-e/install-action@773334c0e05d7e699e4d78234494308223f3a2cf # nextest - - run: cargo nextest run --workspace --status-level slow --profile ci + - run: cargo nextest run --locked --workspace --status-level slow --profile ci test-macos: name: Test (macOS) @@ -124,4 +124,4 @@ jobs: with: cache-on-failure: true - uses: taiki-e/install-action@773334c0e05d7e699e4d78234494308223f3a2cf # nextest - - run: cargo nextest run --workspace --status-level slow --profile ci + - run: cargo nextest run --locked --workspace --status-level slow --profile ci diff --git a/.github/workflows/typescript.yml b/.github/workflows/typescript.yml index c240e3837..e2f63a047 100644 --- a/.github/workflows/typescript.yml +++ b/.github/workflows/typescript.yml @@ -73,5 +73,5 @@ jobs: - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 - run: bun install --frozen-lockfile - uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable - - run: cargo dev build -- -p fabro-cli --release + - run: cargo --locked dev build -- --locked -p fabro-cli --release - run: wc -c < target/release/fabro diff --git a/Cargo.lock b/Cargo.lock index 19852f701..187bd3181 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2609,6 +2609,7 @@ dependencies = [ "clap", "dirs", "fabro-model", + "fabro-types", "fabro-util", "hex", "serde", diff --git a/apps/fabro-web/app/components/run-summary-panel.test.tsx b/apps/fabro-web/app/components/run-summary-panel.test.tsx index 52e79cf0f..018184802 100644 --- a/apps/fabro-web/app/components/run-summary-panel.test.tsx +++ b/apps/fabro-web/app/components/run-summary-panel.test.tsx @@ -6,6 +6,7 @@ import { RunSummaryPanelView, type RunSummaryPanelViewProps, } from "./run-summary-panel"; +import { TEST_PRINCIPAL } from "../lib/test-fixtures"; function instanceText(instance: TestRenderer.ReactTestInstance): string { const parts: string[] = []; @@ -53,7 +54,7 @@ function cellAfterLabel( function makeRun(overrides: Record = {}) { return { id: "run_1", - created_by: null, + created_by: TEST_PRINCIPAL, diff: null, billing: null, ...overrides, @@ -71,9 +72,9 @@ describe("RunSummaryPanelView", () => { } }); - test("shows unavailable copy for missing run fields after load", () => { + test("shows creator and unavailable copy for optional missing run fields after load", () => { const tree = render({ run: makeRun() }); - expect(instanceText(cellAfterLabel(tree, "Created by"))).toBe(EMPTY_VALUE); + expect(instanceText(cellAfterLabel(tree, "Created by"))).toBe("Ttest"); expect(instanceText(cellAfterLabel(tree, "Changes"))).toBe(EMPTY_VALUE); expect(instanceText(cellAfterLabel(tree, "Cost"))).toBe(EMPTY_VALUE); }); @@ -226,7 +227,7 @@ describe("RunSummaryPanelView", () => { kind: "user", identity: { issuer: "github", subject: "1" }, login: "brynary", - auth_method: "oauth", + auth_method: "github", }, }), }); @@ -240,7 +241,7 @@ describe("RunSummaryPanelView", () => { kind: "user", identity: { issuer: "github", subject: "1" }, login: "brynary", - auth_method: "oauth", + auth_method: "github", avatar_url: "https://example.com/brynary.png", }, }), @@ -252,7 +253,7 @@ describe("RunSummaryPanelView", () => { }); test("renders non-user actor with kind label", () => { - for (const kind of ["agent", "system", "slack", "webhook", "worker", "anonymous"]) { + for (const kind of ["agent", "system", "slack", "webhook", "worker"]) { const tree = render({ run: makeRun({ created_by: { kind } as any }) }); expect(instanceText(cellAfterLabel(tree, "Created by"))).toContain(kind); } diff --git a/apps/fabro-web/app/components/run-summary-panel.tsx b/apps/fabro-web/app/components/run-summary-panel.tsx index 20a08af5b..c3784a9ba 100644 --- a/apps/fabro-web/app/components/run-summary-panel.tsx +++ b/apps/fabro-web/app/components/run-summary-panel.tsx @@ -1,5 +1,6 @@ import type { ReactNode } from "react"; import type { + Principal, Run, SandboxResources, SandboxState, @@ -50,6 +51,16 @@ function Cell({ label, children }: { label: string; children: ReactNode }) { ); } +function CreatedByValue({ actor }: { actor: Principal }) { + const created = principalDisplay(actor); + return ( +
+ {created.glyph} + {created.label} +
+ ); +} + export interface RunSummaryPanelViewProps { run: Run | null; runLoading: boolean; @@ -116,7 +127,6 @@ export function RunSummaryPanelView({ artifactsCount, artifactsLoading, }: RunSummaryPanelViewProps) { - const created = run?.created_by ? principalDisplay(run.created_by) : null; const diff = run?.diff ?? null; const cost = formatUsdMicros(run?.billing?.total_usd_micros); const sandboxKind = sandboxLifecycleKind(run?.sandbox); @@ -127,11 +137,8 @@ export function RunSummaryPanelView({ {runLoading ? ( - ) : created ? ( -
- {created.glyph} - {created.label} -
+ ) : run ? ( + ) : ( )} diff --git a/apps/fabro-web/app/components/runs-list/run-table-row.tsx b/apps/fabro-web/app/components/runs-list/run-table-row.tsx index 4fdb82777..e367e1e6f 100644 --- a/apps/fabro-web/app/components/runs-list/run-table-row.tsx +++ b/apps/fabro-web/app/components/runs-list/run-table-row.tsx @@ -35,6 +35,7 @@ export function RunTableRow({ }) { const lifecycleLabel = listLifecycleStatusLabel(run); const statusDisplay = columnStatusDisplay[run.status]; + const creator = principalDisplay(run.createdBy); const show = (col: ToggleableColumn) => !hiddenColumns.has(col); return ( @@ -54,14 +55,9 @@ export function RunTableRow({ {show("created_by") && ( - {run.createdBy && (() => { - const display = principalDisplay(run.createdBy); - return ( - - {display.glyph} - - ); - })()} + + {creator.glyph} + )} {show("repo") && ( diff --git a/apps/fabro-web/app/data/runs.test.ts b/apps/fabro-web/app/data/runs.test.ts index 98586b2c4..feb2ba568 100644 --- a/apps/fabro-web/app/data/runs.test.ts +++ b/apps/fabro-web/app/data/runs.test.ts @@ -8,6 +8,7 @@ import { mapRunToRunItem, runStatusDisplay, } from "./runs"; +import { TEST_PRINCIPAL } from "../lib/test-fixtures"; function makeRun(overrides: Partial = {}): Run { return { @@ -17,7 +18,7 @@ function makeRun(overrides: Partial = {}): Run { workflow: { slug: "fix_build", name: "Fix Build", graph_name: "FixBuild", node_count: 0, edge_count: 0 }, automation: null, repository: { name: "myrepo", origin_url: null, provider: "unknown" }, - created_by: null, + created_by: TEST_PRINCIPAL, origin: { kind: "api" }, labels: {}, lifecycle: { diff --git a/apps/fabro-web/app/data/runs.ts b/apps/fabro-web/app/data/runs.ts index 2b277b6d0..3bc9779de 100644 --- a/apps/fabro-web/app/data/runs.ts +++ b/apps/fabro-web/app/data/runs.ts @@ -41,7 +41,7 @@ export interface RunItem { sandboxWorkingDirectory?: string; sourceDirectory?: string; createdAt?: string; - createdBy?: Principal | null; + createdBy: Principal; lastEventAt?: string; size?: RunSize; } diff --git a/apps/fabro-web/app/lib/principal-display.tsx b/apps/fabro-web/app/lib/principal-display.tsx index fa9d4ec16..666f0f64c 100644 --- a/apps/fabro-web/app/lib/principal-display.tsx +++ b/apps/fabro-web/app/lib/principal-display.tsx @@ -4,7 +4,6 @@ import { ChatBubbleLeftEllipsisIcon, Cog6ToothIcon, CpuChipIcon, - QuestionMarkCircleIcon, ServerIcon, } from "@heroicons/react/20/solid"; import type { Principal } from "@qltysh/fabro-api-client"; @@ -57,10 +56,5 @@ export function principalDisplay(actor: Principal): PrincipalDisplay { return { glyph: principalIconGlyph(), label: "webhook" }; case "worker": return { glyph: principalIconGlyph(), label: "worker" }; - case "anonymous": - return { - glyph: principalIconGlyph(), - label: "anonymous", - }; } } diff --git a/apps/fabro-web/app/lib/run-actions.test.ts b/apps/fabro-web/app/lib/run-actions.test.ts index ffb208aa7..d88fdd3af 100644 --- a/apps/fabro-web/app/lib/run-actions.test.ts +++ b/apps/fabro-web/app/lib/run-actions.test.ts @@ -24,6 +24,7 @@ import { unarchiveRuns, } from "./run-actions"; import { generatedAxios } from "./api-client"; +import { TEST_PRINCIPAL } from "./test-fixtures"; type StubResponseInit = { status: number; @@ -47,7 +48,7 @@ function makeRun(status: RunStatus, archived = false): Run { workflow: { slug: "fix_build", name: "Fix Build", graph_name: null, node_count: 0, edge_count: 0 }, automation: null, repository: null, - created_by: null, + created_by: TEST_PRINCIPAL, origin: { kind: "api" }, labels: {}, lifecycle: { diff --git a/apps/fabro-web/app/lib/test-fixtures.ts b/apps/fabro-web/app/lib/test-fixtures.ts new file mode 100644 index 000000000..c625ebe7d --- /dev/null +++ b/apps/fabro-web/app/lib/test-fixtures.ts @@ -0,0 +1,8 @@ +import type { Principal } from "@qltysh/fabro-api-client"; + +export const TEST_PRINCIPAL: Principal = { + kind: "user", + identity: { issuer: "fabro:test", subject: "test-user" }, + login: "test", + auth_method: "dev_token", +}; diff --git a/apps/fabro-web/app/routes/automations-new.test.tsx b/apps/fabro-web/app/routes/automations-new.test.tsx index 47f471abf..32a88dccc 100644 --- a/apps/fabro-web/app/routes/automations-new.test.tsx +++ b/apps/fabro-web/app/routes/automations-new.test.tsx @@ -4,6 +4,7 @@ import TestRenderer, { act } from "react-test-renderer"; import { createMemoryRouter, RouterProvider } from "react-router"; import { ToastProvider } from "../components/toast"; +import { TEST_PRINCIPAL } from "../lib/test-fixtures"; import { setupReactTestEnv } from "../lib/test-utils"; let currentRun: any = null; @@ -120,7 +121,7 @@ function makeRun(overrides: Record = {}) { origin_url: "https://github.com/fallback/repo.git", provider: "github", }, - created_by: null, + created_by: TEST_PRINCIPAL, origin: { kind: "api" }, labels: {}, lifecycle: { diff --git a/apps/fabro-web/app/routes/automations.tsx b/apps/fabro-web/app/routes/automations.tsx index dd388a690..03725287c 100644 --- a/apps/fabro-web/app/routes/automations.tsx +++ b/apps/fabro-web/app/routes/automations.tsx @@ -115,7 +115,7 @@ function AutomationCard({ onDelete: () => void; }) { const Icon = automation.icon; - const runDisabled = busy || !automation.apiEnabled; + const runDisabled = busy || running || !automation.apiEnabled; return (
@@ -153,7 +153,7 @@ function AutomationCard({