diff --git a/run.json b/run.json index efff030de..53f5ff064 100644 --- a/run.json +++ b/run.json @@ -507,7 +507,7 @@ "kind": "running" }, "status_updated_at": "2026-06-01T19:59:20.096172Z", - "last_event_at": "2026-06-01T20:48:58.822684Z", + "last_event_at": "2026-06-01T20:49:08.437838Z", "pending_control": null, "checkpoints": [ { @@ -1455,9 +1455,9 @@ } }, { - "seq": 0, + "seq": 1133, "checkpoint": { - "timestamp": "2026-06-01T20:49:04.350701Z", + "timestamp": "2026-06-01T20:49:08.431908Z", "current_node": "fixup", "completed_nodes": [ "start", @@ -1472,11 +1472,261 @@ ], "node_retries": {}, "context_values": { + "last_stage": "fixup", + "thread.preflight_compile.current_node": "preflight_lint", + "graph.goal": "# Plan: Make run actors and provenance total\n\n## Context\n\nThis is a greenfield app. Backward compatibility with old serialized runs, old API clients, old generated models, and old tests is not a constraint. Prefer the clean invariant and remove all traces of the placeholder shape.\n\n`Principal::Anonymous` currently represents \"no authenticated actor on this request\" inside auth middleware. That is auth state, not an actor. A `Principal` should only mean \"who acted.\"\n\nLikewise, a persisted run should always have a creator. `Run.created_by`, `RunSpec.provenance`, `RunProvenance.subject`, and `run.created` event provenance should all be total. No `Option`, no nullable OpenAPI fields, no legacy deserialization defaults, and no fallback creator in projection code.\n\nTwo commits, in order.\n\n---\n\n## Commit 1 - Remove `Principal::Anonymous`\n\nBreaking cleanup. `Principal` becomes actor-only. Missing/invalid auth is represented as absent request principal, not as an anonymous principal variant.\n\n### Rust\n\n`lib/crates/fabro-types/src/principal.rs`:\n- Drop `Anonymous`.\n- Drop `Anonymous` arms in `kind()` and `display()`.\n- Delete anonymous serialization/round-trip test coverage.\n\n`lib/crates/fabro-server/src/principal_middleware.rs`:\n- `RequestAuthContext.principal: Principal` -> `Option`.\n- `RequestAuthLogContext.principal: Principal` -> `Option`.\n- `initial()` and `rejected()` set `principal: None`.\n- `authenticated(...)`, `authenticated_worker(...)`, and `authenticated_user(...)` set `principal: Some(...)`.\n- Update `principal_without_log_unused_fields` to preserve `None` and strip user avatar data only inside `Some(Principal::User(...))`.\n- Update all gate helpers to match `Option`:\n - `require_user`\n - `require_authenticated_user`\n - `require_run_management_actor`\n - `require_worker_or_user_for_run`\n - `require_run_management_target`\n- `None` routes to the existing `auth_rejection(context.auth_status, context.auth_error_code)` behavior.\n- `Some(Principal::Worker { .. })` keeps the current forbidden-vs-auth-rejection distinctions.\n- Update tests that assert the initial/rejected principal to assert `None`.\n\n`lib/crates/fabro-server/src/server.rs` HTTP logging:\n- Keep the `principal_kind` field on every HTTP log line.\n- Compute `principal_kind` as `auth_context.principal.as_ref().map(Principal::kind).unwrap_or(\"none\")`.\n- Match `auth_context.principal` as an `Option`:\n - `Some(User(...))`, `Some(Worker { ... })`, `Some(Webhook { ... })`, `Some(Slack { ... })` keep their extra fields.\n - `None | Some(Agent { .. } | System { .. })` emits only the common HTTP fields.\n\n`docs/internal/logging-strategy.md`:\n- Replace the `anonymous` HTTP caller category guidance with `none` for requests that have no principal.\n- Keep `auth_status` as the field that distinguishes missing, invalid, expired, and authenticated auth state.\n\n### OpenAPI and generated clients\n\n`docs/public/api-reference/fabro-api.yaml`:\n- Remove `PrincipalAnonymous` from the `Principal` `oneOf`.\n- Remove `anonymous` from the `Principal` discriminator mapping.\n- Delete the `PrincipalAnonymous` schema.\n\nRegenerate:\n- `cargo build -p fabro-api`\n- `cd lib/packages/fabro-api-client && bun run generate`\n\nExpected generated cleanup:\n- `lib/packages/fabro-api-client/src/models/principal-anonymous.ts` disappears.\n- `Principal` union no longer includes `{ kind: \"anonymous\" }`.\n- `lib/packages/fabro-api-client/src/models/index.ts` no longer exports `principal-anonymous`.\n\n### Frontend\n\n`apps/fabro-web/app/lib/principal-display.tsx`:\n- Remove the `\"anonymous\"` switch case and unused icon import.\n\n`apps/fabro-web/app/components/run-summary-panel.test.tsx` and API-client exhaustiveness tests:\n- Remove anonymous principal cases.\n\n### Documentation sweep\n\nRemove anonymous-principal references from product/API docs and tests. Be careful not to touch unrelated uses of \"anonymous\" such as telemetry anonymous IDs or Git's `remote_anonymous` API.\n\nUseful sweep:\n- `rg -n \"Principal::Anonymous|PrincipalAnonymous|kind: 'anonymous'|kind: \\\"anonymous\\\"|anonymous actor|anonymous subject|principal_kind.*anonymous|\\\"anonymous\\\"\" lib/crates apps/fabro-web lib/packages/fabro-api-client docs/public docs/internal`\n\n### Verification\n\n- `cargo +nightly-2026-04-14 fmt --check --all`\n- `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings`\n- `cargo build --workspace`\n- `cargo nextest run --workspace`\n- `cd apps/fabro-web && bun run typecheck && bun test`\n- Manual: start `fabro server start`, hit a protected endpoint without a token, confirm 401 and an HTTP log with `principal_kind=\"none\"` and `auth_status=\"missing\"`.\n\n---\n\n## Commit 2 - Make run provenance and creator non-optional\n\nFull-chain invariant. Every persisted run has exactly one creator principal. No nullable schema fields, no legacy defaults, no projection fallbacks.\n\n### Core type changes\n\n`lib/crates/fabro-types/src/run_summary.rs`:\n- `Run.created_by: Option` -> `Principal`.\n- Drop `#[serde(default)]`.\n\n`lib/crates/fabro-types/src/run.rs`:\n- `RunProvenance.subject: Option` -> `Principal`.\n- Drop `#[serde(default, skip_serializing_if = \"Option::is_none\")]`.\n- Drop `Default` derive on `RunProvenance`.\n- `RunSpec.provenance: Option` -> `RunProvenance`.\n- Drop `#[serde(default, skip_serializing_if = \"Option::is_none\")]` on `RunSpec.provenance`.\n\n`lib/crates/fabro-types/src/run_event/run.rs`:\n- `RunCreatedProps.provenance: Option` -> `RunProvenance`.\n- Drop default/skip serialization attributes for provenance.\n\n`lib/crates/fabro-workflow/src/event/events.rs`:\n- `Event::RunCreated.provenance: Option` -> `RunProvenance`.\n- Drop default/skip serialization attributes for provenance.\n\n### Creation and retry flow\n\n`lib/crates/fabro-workflow/src/operations/create.rs`:\n- `CreateRunInput.provenance: Option` -> `RunProvenance`.\n- `PersistCreateOptions.provenance: Option` -> `RunProvenance`.\n- `RunSpec { provenance }` stores the total provenance directly.\n- `Event::RunCreated { provenance }` emits total provenance directly.\n\n`lib/crates/fabro-server/src/server/handler/runs.rs`:\n- `run_provenance(headers, subject)` returns `RunProvenance { subject: subject.clone(), ... }`.\n- Build provenance before creating `CreateRunInput`.\n\n`lib/crates/fabro-server/src/run_manifest.rs`:\n- Change `create_run_input(...)` to accept `provenance: RunProvenance` and set it directly, or stop using the helper for the final `CreateRunInput` construction. Do not create a temporary input with missing provenance.\n\n`lib/crates/fabro-workflow/src/operations/retry.rs`:\n- `RetryRunInput.provenance: Option` -> `RunProvenance`.\n- `retry_run(...)` writes the new run's `run.created` event with total provenance.\n\n`lib/crates/fabro-server/src/server/handler/lifecycle.rs`:\n- Pass `run_provenance(&headers, &actor)` directly into `RetryRunInput`.\n\n### Event conversion and projections\n\n`lib/crates/fabro-workflow/src/event/convert.rs`:\n- Convert `Event::RunCreated.provenance` into `RunCreatedProps.provenance` directly.\n- Remove `Some(...)` wrapping for run-created provenance.\n\n`lib/crates/fabro-workflow/src/event/stored_fields.rs`:\n- `Event::RunCreated { provenance, .. }` sets `actor: Some(provenance.subject.clone())`.\n\n`lib/crates/fabro-store/src/run_state.rs`:\n- `projection_from_created(...)` builds `RunSpec { provenance: props.provenance.clone(), ... }`.\n- `build_summary(...)` sets `created_by: state.spec.provenance.subject.clone()`.\n- Delete or rewrite tests that deserialize projections with `\"provenance\": null`.\n\n`lib/crates/fabro-types/src/run_projection.rs` and projection tests:\n- Replace all test `RunSpec` literals with total provenance.\n- Remove tests whose only purpose is legacy/null provenance tolerance.\n\n### OpenAPI\n\n`docs/public/api-reference/fabro-api.yaml`:\n- `Run.created_by` references `Principal` directly. Remove `oneOf [..., null]`.\n- `RunProvenance.required` includes `subject`.\n- `RunProvenance.subject` references `Principal` directly. Remove `oneOf [..., null]`.\n- `RunSpec.required` includes `provenance`.\n- `RunSpec.provenance` references `RunProvenance` directly. Remove `oneOf [..., null]`.\n- If `run.created` event properties are represented separately in the spec, make that event provenance required and non-nullable too.\n\nRegenerate:\n- `cargo build -p fabro-api`\n- `cd lib/packages/fabro-api-client && bun run generate`\n\nDo not hand-edit generated client files.\n\n### Demo mode\n\n`lib/crates/fabro-server/src/demo/mod.rs`:\n- Add a clearly synthetic demo principal using `AuthMethod::DevToken`, not GitHub:\n ```rust\n static DEMO_PRINCIPAL: LazyLock = LazyLock::new(|| {\n Principal::user(\n IdpIdentity::new(\"fabro:demo\", \"demo\").unwrap(),\n \"demo\".to_string(),\n AuthMethod::DevToken,\n )\n });\n ```\n- Replace `created_by: None` with `created_by: DEMO_PRINCIPAL.clone()`.\n- If demo creates any full `RunSpec` or `run.created` event data, give it `RunProvenance { subject: DEMO_PRINCIPAL.clone(), ... }`.\n\n### Test support\n\nDo not add fake auth helpers to `fabro_types::fixtures`; that module is run-id constants.\n\nUse the existing `fabro-types` `test-support` feature:\n- Add `#[cfg(any(test, feature = \"test-support\"))] pub mod test_support;` in `lib/crates/fabro-types/src/lib.rs` if it does not already exist.\n- Add `lib/crates/fabro-types/src/test_support.rs` with:\n - `test_principal() -> Principal`\n - `test_run_provenance() -> RunProvenance`\n- Use an obviously fake dev-token identity, e.g. issuer `fabro:test`, subject `test-user`, login `test`.\n- In crates that need the helper from integration tests or cross-crate tests, dual-list `fabro-types` in `dev-dependencies` with `features = [\"test-support\"]`, following existing repo patterns.\n\nUpdate all constructors:\n- Replace `provenance: None` in `RunSpec`, `CreateRunInput`, `RetryRunInput`, `Event::RunCreated`, and `RunCreatedProps` literals with `test_run_provenance()` or a locally meaningful provenance.\n- Replace `subject: Some(...)` with `subject: ...`.\n- Replace `subject: None` only when it is actually `RunProvenance.subject`; leave unrelated todo/commit/message `subject` fields alone.\n- Replace `created_by: None` / `created_by: null` with `test_principal()` or a frontend TS principal fixture.\n- Delete tests that assert nullable or omitted creator/provenance behavior.\n\nRepresentative Rust areas:\n- `lib/crates/fabro-store/src/run_state.rs`\n- `lib/crates/fabro-store/tests/serializable_projection.rs`\n- `lib/crates/fabro-workflow/src/operations/{create,retry,start}.rs`\n- `lib/crates/fabro-workflow/src/event/{convert,sink,stored_fields}.rs`\n- `lib/crates/fabro-workflow/src/handler/**`\n- `lib/crates/fabro-workflow/src/pipeline/**`\n- `lib/crates/fabro-workflow/src/run_{lookup,metadata}.rs`\n- `lib/crates/fabro-server/src/server/tests.rs`\n- `lib/crates/fabro-server/src/server/handler/**`\n- `lib/crates/fabro-server/tests/it/**`\n- `lib/crates/fabro-cli/tests/it/support/mod.rs`\n- `lib/crates/fabro-dump/src/lib.rs`\n- `lib/crates/fabro-tool/src/{common,create,interact,search}.rs`\n- `lib/crates/fabro-api/tests/{principal_round_trip,run_summary_round_trip,run_projection_round_trip,run_event_round_trip}.rs`\n- `lib/crates/fabro-types/tests/{run_spec_serde,run_spec_methods,run_event_serde}.rs`\n\nRepresentative TypeScript areas:\n- `apps/fabro-web/app/**` tests with `created_by: null`\n- `apps/fabro-web/app/data/runs.ts`\n- `apps/fabro-web/app/components/run-summary-panel.tsx`\n- `apps/fabro-web/app/components/runs-list/**`\n- `lib/packages/fabro-api-client/tests/principal-exhaustive.ts`\n\nUseful sweep after edits:\n- `rg -n \"Principal::Anonymous|PrincipalAnonymous|principal-anonymous|kind: ['\\\"]anonymous|created_by:\\\\s*(None|null)|provenance:\\\\s*None|subject:\\\\s*Some\\\\(|subject:\\\\s*None\" lib/crates apps/fabro-web lib/packages/fabro-api-client docs/public docs/internal`\n\nReview each hit. The only acceptable remaining matches should be unrelated uses of \"anonymous\" and unrelated non-principal `subject` fields.\n\n### Frontend\n\n`apps/fabro-web/app/components/run-summary-panel.tsx`:\n- `run?.created_by` may still be guarded by `run` loading state, but `created_by` itself is non-null once `run` exists.\n- Pass `run.created_by` directly to `principalDisplay(...)` inside loaded-run branches.\n\n`apps/fabro-web/app/data/runs.ts` and run-list components:\n- Treat `createdBy` as a total principal in UI data derived from a loaded API run.\n- Remove empty/fallback rendering that only existed for missing creator data.\n\n### Verification\n\n- `cargo +nightly-2026-04-14 fmt --check --all`\n- `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings`\n- `cargo build --workspace`\n- `cargo nextest run --workspace`\n- `cargo nextest run -p fabro-server`\n- `cd apps/fabro-web && bun run typecheck && bun test && bun run build`\n- Manual end-to-end:\n - `fabro server start`\n - `cd apps/fabro-web && bun run dev`\n - Authenticate and create a run through the UI.\n - Confirm `/api/v1/runs/:id` has non-null `created_by`.\n - Confirm `/api/v1/runs/:id/state` has non-null `spec.provenance.subject`.\n - Retry a failed run and confirm the retried run has the retrying user as creator.\n - Hit demo mode with `X-Fabro-Demo: 1` and confirm the run summary renders the synthetic `demo` dev-token user.\n", + "current_node": "fixup", + "thread.simplify_opus.current_node": "simplify_gpt", + "internal.work_dir": "/home/daytona/workspace/fabro", + "internal.node_visit_count": 1, + "response.fixup": "All verification steps pass. Summary:\n\n**Root cause**: The verify script failed at `git merge origin/main` because the repository's git config only fetches the feature branch (`feat/settings-environments-crud`), not `main`. The plain `git fetch origin main` only updates `FETCH_HEAD`, leaving `origin/main` undefined.\n\n**Fixes applied**:\n1. Fetched main explicitly with `git fetch origin main:refs/remotes/origin/main` and merged it cleanly.\n2. The merge revealed a compile failure in `lib/crates/fabro-workflow/src/test_support.rs`: the local `simplify_opus` version imported `fabro_types::test_support::test_run_provenance_with`, but that module is feature-gated and `fabro-workflow`'s `src/test_support.rs` is compiled unconditionally during library builds. Replaced the helper call with the inline `RunProvenance { server: None, client: None, subject: ... }` literal (matching the version on `origin/main`), removing the offending cross-crate `test_support` import.\n\n**Verification results** (all green):\n- `cargo +nightly-2026-04-14 fmt --check --all` ✓\n- forbidden-symbol sweep ✓\n- `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings` ✓\n- `cargo nextest run --workspace --profile ci`: 6494 passed, 181 skipped ✓\n- `cargo dev docs check` ✓\n- `bun install --frozen-lockfile` ✓\n- `apps/fabro-web` typecheck ✓ and 508 tests passed ✓\n- `lib/packages/fabro-api-client` typecheck ✓\n- `cargo dev build -- -p fabro-cli --release` ✓", + "thread.verify.current_node": "fixup", + "thread.simplify_gpt.current_node": "verify", + "last_response": "All verification steps pass. Summary:\n\n**Root cause**: The verify script failed at `git merge origin/main` because the repository's git config only fetches the feature branch (`feat/settings-environme", + "command.output": "blob://sha256/892028c98394b32e6bfcb04ebd4e24b953ae664fd34c494130103eea91b73118", "failure_class": "", + "internal.retry_count.preflight_compile": 0, + "internal.retry_count.simplify_opus": 0, + "thread.preflight_lint.current_node": "implement", + "graph.rankdir": "LR", + "failure_signature": "", + "thread.toolchain.current_node": "preflight_compile", + "internal.retry_count.fixup": 0, + "internal.fidelity": "compact", + "internal.retry_count.implement": 0, + "internal.retry_count.verify": 0, + "thread.start.current_node": "toolchain", + "response.simplify_opus": "## Summary\n\nReviewed the PR diff (vs origin/main) with three parallel agents covering reuse, quality, and efficiency. The PR is largely clean. Fixed the genuine issues:\n\n**High-priority regressions (Agent 3):**\n1. **`principal_middleware.rs::log_snapshot`** — re-introduced an avatar-stripping helper so HTTP middleware doesn't allocate a `String` for `avatar_url` on every authenticated request. The previous code (deleted in this PR) had this optimization; it's now restored via a small `principal_for_log` helper.\n2. **`run-table-row.tsx`** — gated `principalDisplay(run.createdBy)` behind `show(\"created_by\")`. Previously the call ran on every row regardless of column visibility, causing wasted React element construction.\n\n**Quality cleanup (Agent 2 #3/#4, Agent 1 #1):**\n3. Added `test_run_provenance_with(subject)` helper in `fabro-types::test_support` so callers don't have to hand-build the full `RunProvenance` struct just to override the subject.\n4. Replaced the inline `RunProvenance { server: None, client: None, subject: Principal::System {…} }` literal in `fabro-workflow::test_support` with `test_run_provenance_with(Principal::System {…})`, eliminating the silent drift risk.\n\n**Findings reviewed and skipped (false positives or out of scope):**\n- Cargo version downgrade & `environment-form.tsx` (Agent 2 #12/#13): rebase artifacts — the branch base is older than current `origin/main`, both will resolve at merge.\n- Hand-written JSON in `inspect.rs` (Agent 1 #5): false positive — those are insta inline snapshot string literals (expected stdout), not constructed `json!()` values.\n- Local `test_user_principal()` in `principal_middleware.rs` (Agent 1 #3): intentionally GitHub/octocat-shaped to match JWT fixtures elsewhere in the same file.\n- Infallible `IdpIdentity` constructor, `Principal::system/worker/engine` constructors, `RunSpec::test_default` builder (Agent 2 #1/#2/#9): out of scope — broader API/refactor work.\n\n**Verification:**\n- `cargo +nightly-2026-04-14 fmt --check --all` ✓\n- `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings` ✓\n- `cargo nextest run -p fabro-server -p fabro-types -p fabro-workflow` ✓ (3 pre-existing SVG-render env failures unrelated to this PR)\n- `bun run typecheck && bun test` ✓ (508/508)", + "internal.retry_count.preflight_lint": 0, + "internal.retry_count.toolchain": 0, + "internal.run_id": "01KT2C9EGA92BZRZRWW9Y4SYE4", + "outcome": "succeeded", + "internal.retry_count.simplify_gpt": 0, + "internal.thread_id": "verify", + "internal.retry_count.start": 0, + "thread.implement.current_node": "simplify_opus", + "graph.model_stylesheet": "\n * { model: claude-opus-4-7; }\n " + }, + "node_outcomes": { + "start": { + "status": "succeeded", + "usage": null + }, + "preflight_compile": { + "status": "succeeded", + "context_updates": { + "command.output": "blob://sha256/12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126" + }, + "notes": "Script completed: cargo check -q --workspace 2>&1", + "usage": null, + "timing": { + "wall_time_ms": 0, + "inference_time_ms": 0, + "tool_time_ms": 129862, + "active_time_ms": 129862 + } + }, + "verify": { + "status": "failed", + "context_updates": { + "command.output": "blob://sha256/892028c98394b32e6bfcb04ebd4e24b953ae664fd34c494130103eea91b73118" + }, + "failure": { + "message": "Script failed with exit code: 1\n\n## output\nFrom https://github.com/fabro-sh/fabro\n * branch main -> FETCH_HEAD\nmerge: origin/main - not something we can merge\n", + "category": "deterministic" + }, + "usage": null, + "timing": { + "wall_time_ms": 0, + "inference_time_ms": 0, + "tool_time_ms": 1460, + "active_time_ms": 1460 + } + }, + "simplify_gpt": { + "status": "failed", + "failure": { + "message": "LLM error: Authentication error for openai: Your authentication token has been invalidated. Please try signing in again.", + "category": "deterministic", + "signature": "api_deterministic|openai|authentication" + }, + "usage": null + }, + "toolchain": { + "status": "succeeded", + "context_updates": { + "command.output": "blob://sha256/fc14b2ba2d770e5cd3169df7a29525c962adfc4cfa3097b9098c63ebd61a748c" + }, + "notes": "Script completed: 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", + "usage": null, + "timing": { + "wall_time_ms": 0, + "inference_time_ms": 0, + "tool_time_ms": 1759, + "active_time_ms": 1759 + } + }, + "implement": { + "status": "failed", + "failure": { + "message": "LLM error: Authentication error for openai: Encountered invalidated oauth token for user, failing request", + "category": "deterministic", + "signature": "api_deterministic|openai|authentication" + }, + "usage": null + }, + "fixup": { + "status": "succeeded", + "context_updates": { + "last_response": "All verification steps pass. Summary:\n\n**Root cause**: The verify script failed at `git merge origin/main` because the repository's git config only fetches the feature branch (`feat/settings-environme", + "last_stage": "fixup", + "response.fixup": "All verification steps pass. Summary:\n\n**Root cause**: The verify script failed at `git merge origin/main` because the repository's git config only fetches the feature branch (`feat/settings-environments-crud`), not `main`. The plain `git fetch origin main` only updates `FETCH_HEAD`, leaving `origin/main` undefined.\n\n**Fixes applied**:\n1. Fetched main explicitly with `git fetch origin main:refs/remotes/origin/main` and merged it cleanly.\n2. The merge revealed a compile failure in `lib/crates/fabro-workflow/src/test_support.rs`: the local `simplify_opus` version imported `fabro_types::test_support::test_run_provenance_with`, but that module is feature-gated and `fabro-workflow`'s `src/test_support.rs` is compiled unconditionally during library builds. Replaced the helper call with the inline `RunProvenance { server: None, client: None, subject: ... }` literal (matching the version on `origin/main`), removing the offending cross-crate `test_support` import.\n\n**Verification results** (all green):\n- `cargo +nightly-2026-04-14 fmt --check --all` ✓\n- forbidden-symbol sweep ✓\n- `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings` ✓\n- `cargo nextest run --workspace --profile ci`: 6494 passed, 181 skipped ✓\n- `cargo dev docs check` ✓\n- `bun install --frozen-lockfile` ✓\n- `apps/fabro-web` typecheck ✓ and 508 tests passed ✓\n- `lib/packages/fabro-api-client` typecheck ✓\n- `cargo dev build -- -p fabro-cli --release` ✓" + }, + "notes": "Stage completed: fixup", + "usage": { + "input": { + "usage": { + "model": { + "provider": "anthropic", + "model_id": "claude-opus-4-7" + }, + "tokens": { + "input_tokens": 47744, + "output_tokens": 11804, + "reasoning_tokens": 0, + "cache_read_tokens": 1650510, + "cache_write_tokens": 218991 + } + }, + "facts": { + "algorithm": "anthropic", + "cache_write_5m_tokens": 218991, + "cache_write_1h_tokens": 0 + } + }, + "total_usd_micros": 2727768 + }, + "files_touched": [ + "/home/daytona/workspace/fabro/lib/crates/fabro-workflow/src/test_support.rs" + ], + "timing": { + "wall_time_ms": 0, + "inference_time_ms": 193035, + "tool_time_ms": 693326, + "active_time_ms": 886361 + } + }, + "preflight_lint": { + "status": "succeeded", + "context_updates": { + "command.output": "blob://sha256/12ae32cb1ec02d01eda3581b127c1fee3b0dc53572ed6baf239721a03d82e126" + }, + "notes": "Script completed: cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1", + "usage": null, + "timing": { + "wall_time_ms": 0, + "inference_time_ms": 0, + "tool_time_ms": 144378, + "active_time_ms": 144378 + } + }, + "simplify_opus": { + "status": "succeeded", + "context_updates": { + "response.simplify_opus": "## Summary\n\nReviewed the PR diff (vs origin/main) with three parallel agents covering reuse, quality, and efficiency. The PR is largely clean. Fixed the genuine issues:\n\n**High-priority regressions (Agent 3):**\n1. **`principal_middleware.rs::log_snapshot`** — re-introduced an avatar-stripping helper so HTTP middleware doesn't allocate a `String` for `avatar_url` on every authenticated request. The previous code (deleted in this PR) had this optimization; it's now restored via a small `principal_for_log` helper.\n2. **`run-table-row.tsx`** — gated `principalDisplay(run.createdBy)` behind `show(\"created_by\")`. Previously the call ran on every row regardless of column visibility, causing wasted React element construction.\n\n**Quality cleanup (Agent 2 #3/#4, Agent 1 #1):**\n3. Added `test_run_provenance_with(subject)` helper in `fabro-types::test_support` so callers don't have to hand-build the full `RunProvenance` struct just to override the subject.\n4. Replaced the inline `RunProvenance { server: None, client: None, subject: Principal::System {…} }` literal in `fabro-workflow::test_support` with `test_run_provenance_with(Principal::System {…})`, eliminating the silent drift risk.\n\n**Findings reviewed and skipped (false positives or out of scope):**\n- Cargo version downgrade & `environment-form.tsx` (Agent 2 #12/#13): rebase artifacts — the branch base is older than current `origin/main`, both will resolve at merge.\n- Hand-written JSON in `inspect.rs` (Agent 1 #5): false positive — those are insta inline snapshot string literals (expected stdout), not constructed `json!()` values.\n- Local `test_user_principal()` in `principal_middleware.rs` (Agent 1 #3): intentionally GitHub/octocat-shaped to match JWT fixtures elsewhere in the same file.\n- Infallible `IdpIdentity` constructor, `Principal::system/worker/engine` constructors, `RunSpec::test_default` builder (Agent 2 #1/#2/#9): out of scope — broader API/refactor work.\n\n**Verification:**\n- `cargo +nightly-2026-04-14 fmt --check --all` ✓\n- `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings` ✓\n- `cargo nextest run -p fabro-server -p fabro-types -p fabro-workflow` ✓ (3 pre-existing SVG-render env failures unrelated to this PR)\n- `bun run typecheck && bun test` ✓ (508/508)", + "last_stage": "simplify_opus", + "last_response": "## Summary\n\nReviewed the PR diff (vs origin/main) with three parallel agents covering reuse, quality, and efficiency. The PR is largely clean. Fixed the genuine issues:\n\n**High-priority regressions (A" + }, + "notes": "Stage completed: simplify_opus", + "usage": { + "input": { + "usage": { + "model": { + "provider": "anthropic", + "model_id": "claude-opus-4-7" + }, + "tokens": { + "input_tokens": 81512, + "output_tokens": 22456, + "reasoning_tokens": 0, + "cache_read_tokens": 4289672, + "cache_write_tokens": 433678 + } + }, + "facts": { + "algorithm": "anthropic", + "cache_write_5m_tokens": 433678, + "cache_write_1h_tokens": 0 + } + }, + "total_usd_micros": 5824283 + }, + "files_touched": [ + "/home/daytona/workspace/fabro/apps/fabro-web/app/components/runs-list/run-table-row.tsx", + "/home/daytona/workspace/fabro/lib/crates/fabro-server/src/principal_middleware.rs", + "/home/daytona/workspace/fabro/lib/crates/fabro-types/src/test_support.rs", + "/home/daytona/workspace/fabro/lib/crates/fabro-workflow/src/test_support.rs" + ], + "timing": { + "wall_time_ms": 0, + "inference_time_ms": 992467, + "tool_time_ms": 677393, + "active_time_ms": 1669860 + } + } + }, + "next_node_id": "verify", + "git_commit_sha": "2f2b32d4bb9e35212894cb84cc3758b4d5075778", + "loop_failure_signatures": { + "simplify_gpt|deterministic|api_deterministic|openai|authentication": 1, + "verify|deterministic|script failed with exit code: ## output from https://github.com/fabro-sh/fabro * branch main -> fetch_head merge: origin/main - not something we can merge": 1, + "implement|deterministic|api_deterministic|openai|authentication": 1 + }, + "node_visits": { + "preflight_lint": 1, + "start": 1, + "simplify_gpt": 1, + "fixup": 1, + "implement": 1, + "verify": 1, + "preflight_compile": 1, + "simplify_opus": 1, + "toolchain": 1 + } + }, + "diff": { + "patch": "diff --git a/Cargo.lock b/Cargo.lock\nindex 187bd3181..3d8895cd1 100644\n--- a/Cargo.lock\n+++ b/Cargo.lock\n@@ -1660,7 +1660,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-acp\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"agent-client-protocol\",\n \"agent-client-protocol-tokio\",\n@@ -1679,7 +1679,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-agent\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"anyhow\",\n \"async-trait\",\n@@ -1721,7 +1721,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-api\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"chrono\",\n \"fabro-automation\",\n@@ -1744,7 +1744,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-auth\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"anyhow\",\n \"async-trait\",\n@@ -1768,7 +1768,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-automation\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"croner\",\n \"hex\",\n@@ -1783,11 +1783,11 @@ dependencies = [\n \n [[package]]\n name = \"fabro-build-support\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n \n [[package]]\n name = \"fabro-checkpoint\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"chrono\",\n \"fabro-config\",\n@@ -1803,7 +1803,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-cli\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"anyhow\",\n \"assert_cmd\",\n@@ -1905,7 +1905,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-client\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"anyhow\",\n \"bytes\",\n@@ -1934,7 +1934,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-config\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"anyhow\",\n \"chrono\",\n@@ -1963,7 +1963,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-core\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"async-trait\",\n \"fabro-types\",\n@@ -1978,7 +1978,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-dev\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"anyhow\",\n \"assert_cmd\",\n@@ -1997,7 +1997,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-dump\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"anyhow\",\n \"bytes\",\n@@ -2011,7 +2011,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-environment\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"fabro-config\",\n \"fabro-types\",\n@@ -2027,7 +2027,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-github\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"anyhow\",\n \"base64\",\n@@ -2049,7 +2049,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-graphviz\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"anyhow\",\n \"fabro-types\",\n@@ -2063,7 +2063,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-hooks\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"async-trait\",\n \"fabro-agent\",\n@@ -2087,7 +2087,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-http\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"fabro-static\",\n \"http\",\n@@ -2097,7 +2097,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-install\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"anyhow\",\n \"base64\",\n@@ -2113,7 +2113,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-interview\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"async-trait\",\n \"dialoguer\",\n@@ -2128,7 +2128,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-llm\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"anyhow\",\n \"async-trait\",\n@@ -2163,7 +2163,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-macros\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"clap\",\n \"fabro-options-metadata\",\n@@ -2174,7 +2174,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-manifest\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"anyhow\",\n \"fabro-api\",\n@@ -2192,7 +2192,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-mcp\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"anyhow\",\n \"axum\",\n@@ -2212,7 +2212,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-mcp-server\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"anyhow\",\n \"chrono\",\n@@ -2239,7 +2239,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-model\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"fabro-static\",\n \"http\",\n@@ -2255,7 +2255,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-oauth\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"anyhow\",\n \"axum\",\n@@ -2277,7 +2277,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-options-metadata\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"serde\",\n \"serde_json\",\n@@ -2285,7 +2285,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-proc\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"cc\",\n \"libc\",\n@@ -2294,7 +2294,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-redact\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"aho-corasick\",\n \"ref-cast\",\n@@ -2310,7 +2310,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-sandbox\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"anyhow\",\n \"async-trait\",\n@@ -2356,7 +2356,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-server\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"anyhow\",\n \"async-trait\",\n@@ -2445,7 +2445,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-slack\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"fabro-http\",\n \"fabro-interview\",\n@@ -2467,18 +2467,18 @@ dependencies = [\n \n [[package]]\n name = \"fabro-spa\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"rust-embed\",\n ]\n \n [[package]]\n name = \"fabro-static\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n \n [[package]]\n name = \"fabro-store\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"async-trait\",\n \"bytes\",\n@@ -2505,7 +2505,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-telemetry\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"anyhow\",\n \"base64\",\n@@ -2531,7 +2531,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-template\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"anyhow\",\n \"fabro-types\",\n@@ -2545,7 +2545,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-test\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"assert_cmd\",\n \"axum\",\n@@ -2568,7 +2568,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-tool\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"anyhow\",\n \"async-trait\",\n@@ -2589,7 +2589,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-tracker\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"anyhow\",\n \"async-trait\",\n@@ -2603,7 +2603,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-types\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"chrono\",\n \"clap\",\n@@ -2624,7 +2624,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-util\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"anyhow\",\n \"console 0.15.11\",\n@@ -2644,7 +2644,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-validate\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"fabro-acp\",\n \"fabro-graphviz\",\n@@ -2657,7 +2657,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-variable\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"chrono\",\n \"fabro-types\",\n@@ -2669,7 +2669,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-vault\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"chrono\",\n \"fabro-static\",\n@@ -2682,7 +2682,7 @@ dependencies = [\n \n [[package]]\n name = \"fabro-workflow\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"anyhow\",\n \"assert_cmd\",\n@@ -7494,7 +7494,7 @@ dependencies = [\n \n [[package]]\n name = \"twin-github\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"axum\",\n \"base64\",\n@@ -7513,7 +7513,7 @@ dependencies = [\n \n [[package]]\n name = \"twin-openai\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n dependencies = [\n \"anyhow\",\n \"async-stream\",\ndiff --git a/Cargo.toml b/Cargo.toml\nindex 6cbe3767b..062736ab1 100644\n--- a/Cargo.toml\n+++ b/Cargo.toml\n@@ -5,7 +5,7 @@ resolver = \"2\"\n \n [workspace.package]\n edition = \"2021\"\n-version = \"0.247.0-nightly.0\"\n+version = \"0.250.0-nightly.0\"\n license = \"MIT\"\n \n [workspace.dependencies]\ndiff --git a/lib/crates/fabro-workflow/src/test_support.rs b/lib/crates/fabro-workflow/src/test_support.rs\nindex af43dc5b8..1acf410a4 100644\n--- a/lib/crates/fabro-workflow/src/test_support.rs\n+++ b/lib/crates/fabro-workflow/src/test_support.rs\n@@ -10,7 +10,6 @@ use fabro_graphviz::graph::Graph as GvGraph;\n use fabro_interview::AutoApproveInterviewer;\n use fabro_model::Catalog;\n use fabro_store::{ArtifactStore, Database, RunProjection};\n-use fabro_types::test_support::test_run_provenance_with;\n use object_store::local::LocalFileSystem;\n \n use crate::artifact_upload::ArtifactSink;\n@@ -176,9 +175,13 @@ async fn initialized(\n workflow_slug: run_options.workflow_slug.clone(),\n automation: None,\n db_prefix: None,\n- provenance: test_run_provenance_with(fabro_types::Principal::System {\n- system_kind: fabro_types::SystemActorKind::Engine,\n- }),\n+ provenance: fabro_types::RunProvenance {\n+ server: None,\n+ client: None,\n+ subject: fabro_types::Principal::System {\n+ system_kind: fabro_types::SystemActorKind::Engine,\n+ },\n+ },\n manifest_blob: None,\n git: run_options.pre_run_git.clone(),\n fork_source_ref: run_options.fork_source_ref.clone(),\n", + "summary": { + "files_changed": 5, + "additions": 77, + "deletions": 56 + } + } + }, + { + "seq": 0, + "checkpoint": { + "timestamp": "2026-06-01T20:50:10.640545Z", + "current_node": "verify", + "completed_nodes": [ + "start", + "toolchain", + "preflight_compile", + "preflight_lint", + "implement", + "simplify_opus", + "simplify_gpt", + "verify", + "fixup", + "verify" + ], + "node_retries": {}, + "context_values": { + "failure_class": "transient_infra", "last_stage": "fixup", "thread.simplify_gpt.current_node": "verify", "graph.rankdir": "LR", - "internal.node_visit_count": 1, + "internal.node_visit_count": 2, "internal.retry_count.preflight_lint": 0, "thread.preflight_lint.current_node": "implement", "thread.toolchain.current_node": "preflight_compile", @@ -1489,7 +1739,7 @@ "last_response": "All verification steps pass. Summary:\n\n**Root cause**: The verify script failed at `git merge origin/main` because the repository's git config only fetches the feature branch (`feat/settings-environme", "internal.retry_count.simplify_opus": 0, "response.fixup": "All verification steps pass. Summary:\n\n**Root cause**: The verify script failed at `git merge origin/main` because the repository's git config only fetches the feature branch (`feat/settings-environments-crud`), not `main`. The plain `git fetch origin main` only updates `FETCH_HEAD`, leaving `origin/main` undefined.\n\n**Fixes applied**:\n1. Fetched main explicitly with `git fetch origin main:refs/remotes/origin/main` and merged it cleanly.\n2. The merge revealed a compile failure in `lib/crates/fabro-workflow/src/test_support.rs`: the local `simplify_opus` version imported `fabro_types::test_support::test_run_provenance_with`, but that module is feature-gated and `fabro-workflow`'s `src/test_support.rs` is compiled unconditionally during library builds. Replaced the helper call with the inline `RunProvenance { server: None, client: None, subject: ... }` literal (matching the version on `origin/main`), removing the offending cross-crate `test_support` import.\n\n**Verification results** (all green):\n- `cargo +nightly-2026-04-14 fmt --check --all` ✓\n- forbidden-symbol sweep ✓\n- `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings` ✓\n- `cargo nextest run --workspace --profile ci`: 6494 passed, 181 skipped ✓\n- `cargo dev docs check` ✓\n- `bun install --frozen-lockfile` ✓\n- `apps/fabro-web` typecheck ✓ and 508 tests passed ✓\n- `lib/packages/fabro-api-client` typecheck ✓\n- `cargo dev build -- -p fabro-cli --release` ✓", - "internal.thread_id": "verify", + "internal.thread_id": "fixup", "internal.fidelity": "compact", "graph.goal": "# Plan: Make run actors and provenance total\n\n## Context\n\nThis is a greenfield app. Backward compatibility with old serialized runs, old API clients, old generated models, and old tests is not a constraint. Prefer the clean invariant and remove all traces of the placeholder shape.\n\n`Principal::Anonymous` currently represents \"no authenticated actor on this request\" inside auth middleware. That is auth state, not an actor. A `Principal` should only mean \"who acted.\"\n\nLikewise, a persisted run should always have a creator. `Run.created_by`, `RunSpec.provenance`, `RunProvenance.subject`, and `run.created` event provenance should all be total. No `Option`, no nullable OpenAPI fields, no legacy deserialization defaults, and no fallback creator in projection code.\n\nTwo commits, in order.\n\n---\n\n## Commit 1 - Remove `Principal::Anonymous`\n\nBreaking cleanup. `Principal` becomes actor-only. Missing/invalid auth is represented as absent request principal, not as an anonymous principal variant.\n\n### Rust\n\n`lib/crates/fabro-types/src/principal.rs`:\n- Drop `Anonymous`.\n- Drop `Anonymous` arms in `kind()` and `display()`.\n- Delete anonymous serialization/round-trip test coverage.\n\n`lib/crates/fabro-server/src/principal_middleware.rs`:\n- `RequestAuthContext.principal: Principal` -> `Option`.\n- `RequestAuthLogContext.principal: Principal` -> `Option`.\n- `initial()` and `rejected()` set `principal: None`.\n- `authenticated(...)`, `authenticated_worker(...)`, and `authenticated_user(...)` set `principal: Some(...)`.\n- Update `principal_without_log_unused_fields` to preserve `None` and strip user avatar data only inside `Some(Principal::User(...))`.\n- Update all gate helpers to match `Option`:\n - `require_user`\n - `require_authenticated_user`\n - `require_run_management_actor`\n - `require_worker_or_user_for_run`\n - `require_run_management_target`\n- `None` routes to the existing `auth_rejection(context.auth_status, context.auth_error_code)` behavior.\n- `Some(Principal::Worker { .. })` keeps the current forbidden-vs-auth-rejection distinctions.\n- Update tests that assert the initial/rejected principal to assert `None`.\n\n`lib/crates/fabro-server/src/server.rs` HTTP logging:\n- Keep the `principal_kind` field on every HTTP log line.\n- Compute `principal_kind` as `auth_context.principal.as_ref().map(Principal::kind).unwrap_or(\"none\")`.\n- Match `auth_context.principal` as an `Option`:\n - `Some(User(...))`, `Some(Worker { ... })`, `Some(Webhook { ... })`, `Some(Slack { ... })` keep their extra fields.\n - `None | Some(Agent { .. } | System { .. })` emits only the common HTTP fields.\n\n`docs/internal/logging-strategy.md`:\n- Replace the `anonymous` HTTP caller category guidance with `none` for requests that have no principal.\n- Keep `auth_status` as the field that distinguishes missing, invalid, expired, and authenticated auth state.\n\n### OpenAPI and generated clients\n\n`docs/public/api-reference/fabro-api.yaml`:\n- Remove `PrincipalAnonymous` from the `Principal` `oneOf`.\n- Remove `anonymous` from the `Principal` discriminator mapping.\n- Delete the `PrincipalAnonymous` schema.\n\nRegenerate:\n- `cargo build -p fabro-api`\n- `cd lib/packages/fabro-api-client && bun run generate`\n\nExpected generated cleanup:\n- `lib/packages/fabro-api-client/src/models/principal-anonymous.ts` disappears.\n- `Principal` union no longer includes `{ kind: \"anonymous\" }`.\n- `lib/packages/fabro-api-client/src/models/index.ts` no longer exports `principal-anonymous`.\n\n### Frontend\n\n`apps/fabro-web/app/lib/principal-display.tsx`:\n- Remove the `\"anonymous\"` switch case and unused icon import.\n\n`apps/fabro-web/app/components/run-summary-panel.test.tsx` and API-client exhaustiveness tests:\n- Remove anonymous principal cases.\n\n### Documentation sweep\n\nRemove anonymous-principal references from product/API docs and tests. Be careful not to touch unrelated uses of \"anonymous\" such as telemetry anonymous IDs or Git's `remote_anonymous` API.\n\nUseful sweep:\n- `rg -n \"Principal::Anonymous|PrincipalAnonymous|kind: 'anonymous'|kind: \\\"anonymous\\\"|anonymous actor|anonymous subject|principal_kind.*anonymous|\\\"anonymous\\\"\" lib/crates apps/fabro-web lib/packages/fabro-api-client docs/public docs/internal`\n\n### Verification\n\n- `cargo +nightly-2026-04-14 fmt --check --all`\n- `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings`\n- `cargo build --workspace`\n- `cargo nextest run --workspace`\n- `cd apps/fabro-web && bun run typecheck && bun test`\n- Manual: start `fabro server start`, hit a protected endpoint without a token, confirm 401 and an HTTP log with `principal_kind=\"none\"` and `auth_status=\"missing\"`.\n\n---\n\n## Commit 2 - Make run provenance and creator non-optional\n\nFull-chain invariant. Every persisted run has exactly one creator principal. No nullable schema fields, no legacy defaults, no projection fallbacks.\n\n### Core type changes\n\n`lib/crates/fabro-types/src/run_summary.rs`:\n- `Run.created_by: Option` -> `Principal`.\n- Drop `#[serde(default)]`.\n\n`lib/crates/fabro-types/src/run.rs`:\n- `RunProvenance.subject: Option` -> `Principal`.\n- Drop `#[serde(default, skip_serializing_if = \"Option::is_none\")]`.\n- Drop `Default` derive on `RunProvenance`.\n- `RunSpec.provenance: Option` -> `RunProvenance`.\n- Drop `#[serde(default, skip_serializing_if = \"Option::is_none\")]` on `RunSpec.provenance`.\n\n`lib/crates/fabro-types/src/run_event/run.rs`:\n- `RunCreatedProps.provenance: Option` -> `RunProvenance`.\n- Drop default/skip serialization attributes for provenance.\n\n`lib/crates/fabro-workflow/src/event/events.rs`:\n- `Event::RunCreated.provenance: Option` -> `RunProvenance`.\n- Drop default/skip serialization attributes for provenance.\n\n### Creation and retry flow\n\n`lib/crates/fabro-workflow/src/operations/create.rs`:\n- `CreateRunInput.provenance: Option` -> `RunProvenance`.\n- `PersistCreateOptions.provenance: Option` -> `RunProvenance`.\n- `RunSpec { provenance }` stores the total provenance directly.\n- `Event::RunCreated { provenance }` emits total provenance directly.\n\n`lib/crates/fabro-server/src/server/handler/runs.rs`:\n- `run_provenance(headers, subject)` returns `RunProvenance { subject: subject.clone(), ... }`.\n- Build provenance before creating `CreateRunInput`.\n\n`lib/crates/fabro-server/src/run_manifest.rs`:\n- Change `create_run_input(...)` to accept `provenance: RunProvenance` and set it directly, or stop using the helper for the final `CreateRunInput` construction. Do not create a temporary input with missing provenance.\n\n`lib/crates/fabro-workflow/src/operations/retry.rs`:\n- `RetryRunInput.provenance: Option` -> `RunProvenance`.\n- `retry_run(...)` writes the new run's `run.created` event with total provenance.\n\n`lib/crates/fabro-server/src/server/handler/lifecycle.rs`:\n- Pass `run_provenance(&headers, &actor)` directly into `RetryRunInput`.\n\n### Event conversion and projections\n\n`lib/crates/fabro-workflow/src/event/convert.rs`:\n- Convert `Event::RunCreated.provenance` into `RunCreatedProps.provenance` directly.\n- Remove `Some(...)` wrapping for run-created provenance.\n\n`lib/crates/fabro-workflow/src/event/stored_fields.rs`:\n- `Event::RunCreated { provenance, .. }` sets `actor: Some(provenance.subject.clone())`.\n\n`lib/crates/fabro-store/src/run_state.rs`:\n- `projection_from_created(...)` builds `RunSpec { provenance: props.provenance.clone(), ... }`.\n- `build_summary(...)` sets `created_by: state.spec.provenance.subject.clone()`.\n- Delete or rewrite tests that deserialize projections with `\"provenance\": null`.\n\n`lib/crates/fabro-types/src/run_projection.rs` and projection tests:\n- Replace all test `RunSpec` literals with total provenance.\n- Remove tests whose only purpose is legacy/null provenance tolerance.\n\n### OpenAPI\n\n`docs/public/api-reference/fabro-api.yaml`:\n- `Run.created_by` references `Principal` directly. Remove `oneOf [..., null]`.\n- `RunProvenance.required` includes `subject`.\n- `RunProvenance.subject` references `Principal` directly. Remove `oneOf [..., null]`.\n- `RunSpec.required` includes `provenance`.\n- `RunSpec.provenance` references `RunProvenance` directly. Remove `oneOf [..., null]`.\n- If `run.created` event properties are represented separately in the spec, make that event provenance required and non-nullable too.\n\nRegenerate:\n- `cargo build -p fabro-api`\n- `cd lib/packages/fabro-api-client && bun run generate`\n\nDo not hand-edit generated client files.\n\n### Demo mode\n\n`lib/crates/fabro-server/src/demo/mod.rs`:\n- Add a clearly synthetic demo principal using `AuthMethod::DevToken`, not GitHub:\n ```rust\n static DEMO_PRINCIPAL: LazyLock = LazyLock::new(|| {\n Principal::user(\n IdpIdentity::new(\"fabro:demo\", \"demo\").unwrap(),\n \"demo\".to_string(),\n AuthMethod::DevToken,\n )\n });\n ```\n- Replace `created_by: None` with `created_by: DEMO_PRINCIPAL.clone()`.\n- If demo creates any full `RunSpec` or `run.created` event data, give it `RunProvenance { subject: DEMO_PRINCIPAL.clone(), ... }`.\n\n### Test support\n\nDo not add fake auth helpers to `fabro_types::fixtures`; that module is run-id constants.\n\nUse the existing `fabro-types` `test-support` feature:\n- Add `#[cfg(any(test, feature = \"test-support\"))] pub mod test_support;` in `lib/crates/fabro-types/src/lib.rs` if it does not already exist.\n- Add `lib/crates/fabro-types/src/test_support.rs` with:\n - `test_principal() -> Principal`\n - `test_run_provenance() -> RunProvenance`\n- Use an obviously fake dev-token identity, e.g. issuer `fabro:test`, subject `test-user`, login `test`.\n- In crates that need the helper from integration tests or cross-crate tests, dual-list `fabro-types` in `dev-dependencies` with `features = [\"test-support\"]`, following existing repo patterns.\n\nUpdate all constructors:\n- Replace `provenance: None` in `RunSpec`, `CreateRunInput`, `RetryRunInput`, `Event::RunCreated`, and `RunCreatedProps` literals with `test_run_provenance()` or a locally meaningful provenance.\n- Replace `subject: Some(...)` with `subject: ...`.\n- Replace `subject: None` only when it is actually `RunProvenance.subject`; leave unrelated todo/commit/message `subject` fields alone.\n- Replace `created_by: None` / `created_by: null` with `test_principal()` or a frontend TS principal fixture.\n- Delete tests that assert nullable or omitted creator/provenance behavior.\n\nRepresentative Rust areas:\n- `lib/crates/fabro-store/src/run_state.rs`\n- `lib/crates/fabro-store/tests/serializable_projection.rs`\n- `lib/crates/fabro-workflow/src/operations/{create,retry,start}.rs`\n- `lib/crates/fabro-workflow/src/event/{convert,sink,stored_fields}.rs`\n- `lib/crates/fabro-workflow/src/handler/**`\n- `lib/crates/fabro-workflow/src/pipeline/**`\n- `lib/crates/fabro-workflow/src/run_{lookup,metadata}.rs`\n- `lib/crates/fabro-server/src/server/tests.rs`\n- `lib/crates/fabro-server/src/server/handler/**`\n- `lib/crates/fabro-server/tests/it/**`\n- `lib/crates/fabro-cli/tests/it/support/mod.rs`\n- `lib/crates/fabro-dump/src/lib.rs`\n- `lib/crates/fabro-tool/src/{common,create,interact,search}.rs`\n- `lib/crates/fabro-api/tests/{principal_round_trip,run_summary_round_trip,run_projection_round_trip,run_event_round_trip}.rs`\n- `lib/crates/fabro-types/tests/{run_spec_serde,run_spec_methods,run_event_serde}.rs`\n\nRepresentative TypeScript areas:\n- `apps/fabro-web/app/**` tests with `created_by: null`\n- `apps/fabro-web/app/data/runs.ts`\n- `apps/fabro-web/app/components/run-summary-panel.tsx`\n- `apps/fabro-web/app/components/runs-list/**`\n- `lib/packages/fabro-api-client/tests/principal-exhaustive.ts`\n\nUseful sweep after edits:\n- `rg -n \"Principal::Anonymous|PrincipalAnonymous|principal-anonymous|kind: ['\\\"]anonymous|created_by:\\\\s*(None|null)|provenance:\\\\s*None|subject:\\\\s*Some\\\\(|subject:\\\\s*None\" lib/crates apps/fabro-web lib/packages/fabro-api-client docs/public docs/internal`\n\nReview each hit. The only acceptable remaining matches should be unrelated uses of \"anonymous\" and unrelated non-principal `subject` fields.\n\n### Frontend\n\n`apps/fabro-web/app/components/run-summary-panel.tsx`:\n- `run?.created_by` may still be guarded by `run` loading state, but `created_by` itself is non-null once `run` exists.\n- Pass `run.created_by` directly to `principalDisplay(...)` inside loaded-run branches.\n\n`apps/fabro-web/app/data/runs.ts` and run-list components:\n- Treat `createdBy` as a total principal in UI data derived from a loaded API run.\n- Remove empty/fallback rendering that only existed for missing creator data.\n\n### Verification\n\n- `cargo +nightly-2026-04-14 fmt --check --all`\n- `cargo +nightly-2026-04-14 clippy --workspace --all-targets -- -D warnings`\n- `cargo build --workspace`\n- `cargo nextest run --workspace`\n- `cargo nextest run -p fabro-server`\n- `cd apps/fabro-web && bun run typecheck && bun test && bun run build`\n- Manual end-to-end:\n - `fabro server start`\n - `cd apps/fabro-web && bun run dev`\n - Authenticate and create a run through the UI.\n - Confirm `/api/v1/runs/:id` has non-null `created_by`.\n - Confirm `/api/v1/runs/:id/state` has non-null `spec.provenance.subject`.\n - Retry a failed run and confirm the retried run has the retrying user as creator.\n - Hit demo mode with `X-Fabro-Demo: 1` and confirm the run summary renders the synthetic `demo` dev-token user.\n", "internal.retry_count.simplify_gpt": 0, @@ -1498,14 +1748,15 @@ "internal.retry_count.implement": 0, "thread.preflight_compile.current_node": "preflight_lint", "internal.retry_count.start": 0, - "failure_signature": "", + "failure_signature": "verify|transient_infra|failed to spawn script", "internal.retry_count.preflight_compile": 0, "graph.model_stylesheet": "\n * { model: claude-opus-4-7; }\n ", "thread.implement.current_node": "simplify_opus", - "current_node": "fixup", + "current_node": "verify", "internal.retry_count.fixup": 0, + "thread.fixup.current_node": "verify", "internal.work_dir": "/home/daytona/workspace/fabro", - "outcome": "succeeded" + "outcome": "failed" }, "node_outcomes": { "fixup": { @@ -1560,20 +1811,15 @@ }, "verify": { "status": "failed", - "context_updates": { - "command.output": "blob://sha256/892028c98394b32e6bfcb04ebd4e24b953ae664fd34c494130103eea91b73118" - }, "failure": { - "message": "Script failed with exit code: 1\n\n## output\nFrom https://github.com/fabro-sh/fabro\n * branch main -> FETCH_HEAD\nmerge: origin/main - not something we can merge\n", - "category": "deterministic" + "message": "Failed to spawn script", + "causes": [ + "Failed to get Daytona session command status", + "HTTP 502" + ], + "category": "transient_infra" }, - "usage": null, - "timing": { - "wall_time_ms": 0, - "inference_time_ms": 0, - "tool_time_ms": 1460, - "active_time_ms": 1460 - } + "usage": null }, "implement": { "status": "failed", @@ -1675,7 +1921,7 @@ } } }, - "next_node_id": "verify", + "next_node_id": "fixup", "node_visits": { "preflight_lint": 1, "preflight_compile": 1, @@ -1684,7 +1930,7 @@ "implement": 1, "fixup": 1, "simplify_opus": 1, - "verify": 1, + "verify": 2, "simplify_gpt": 1 } }, @@ -2278,7 +2524,12 @@ "first_event_seq": 829, "prompt": null, "response": null, - "completion": null, + "completion": { + "outcome": "succeeded", + "notes": "Stage completed: fixup", + "failure_reason": null, + "timestamp": "2026-06-01T20:49:04.348687Z" + }, "provider_used": { "mode": "agent", "provider": "anthropic", @@ -2291,6 +2542,12 @@ "output": null, "started_at": "2026-06-01T20:34:11.386042Z", "handler": "agent", + "timing": { + "wall_time_ms": 892958, + "inference_time_ms": 193035, + "tool_time_ms": 693326, + "active_time_ms": 886361 + }, "usage": { "input_tokens": 47744, "output_tokens": 11804, @@ -2499,7 +2756,7 @@ ], "warnings": [] }, - "state": "running" + "state": "succeeded" }, "toolchain@1": { "first_event_seq": 21, @@ -2844,6 +3101,33 @@ }, "state": "failed" }, + "verify@2": { + "first_event_seq": 1136, + "prompt": null, + "response": null, + "completion": null, + "provider_used": null, + "diff": null, + "script_invocation": { + "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", + "command": "exec 2>&1\ngit 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", + "language": "shell" + }, + "script_timing": null, + "parallel_results": null, + "output": null, + "started_at": "2026-06-01T20:49:08.437213Z", + "handler": "command", + "usage": { + "input_tokens": 0, + "output_tokens": 0, + "total_tokens": 0, + "reasoning_tokens": 0, + "cache_read_tokens": 0, + "cache_write_tokens": 0 + }, + "state": "running" + }, "verify@1": { "first_event_seq": 819, "prompt": null, diff --git a/stages/009-fixup@1/diff.patch b/stages/009-fixup@1/diff.patch new file mode 100644 index 000000000..b87f8d40d --- /dev/null +++ b/stages/009-fixup@1/diff.patch @@ -0,0 +1,478 @@ +diff --git a/Cargo.lock b/Cargo.lock +index 187bd3181..3d8895cd1 100644 +--- a/Cargo.lock ++++ b/Cargo.lock +@@ -1660,7 +1660,7 @@ dependencies = [ + + [[package]] + name = "fabro-acp" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "agent-client-protocol", + "agent-client-protocol-tokio", +@@ -1679,7 +1679,7 @@ dependencies = [ + + [[package]] + name = "fabro-agent" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "anyhow", + "async-trait", +@@ -1721,7 +1721,7 @@ dependencies = [ + + [[package]] + name = "fabro-api" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "chrono", + "fabro-automation", +@@ -1744,7 +1744,7 @@ dependencies = [ + + [[package]] + name = "fabro-auth" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "anyhow", + "async-trait", +@@ -1768,7 +1768,7 @@ dependencies = [ + + [[package]] + name = "fabro-automation" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "croner", + "hex", +@@ -1783,11 +1783,11 @@ dependencies = [ + + [[package]] + name = "fabro-build-support" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + + [[package]] + name = "fabro-checkpoint" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "chrono", + "fabro-config", +@@ -1803,7 +1803,7 @@ dependencies = [ + + [[package]] + name = "fabro-cli" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "anyhow", + "assert_cmd", +@@ -1905,7 +1905,7 @@ dependencies = [ + + [[package]] + name = "fabro-client" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "anyhow", + "bytes", +@@ -1934,7 +1934,7 @@ dependencies = [ + + [[package]] + name = "fabro-config" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "anyhow", + "chrono", +@@ -1963,7 +1963,7 @@ dependencies = [ + + [[package]] + name = "fabro-core" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "async-trait", + "fabro-types", +@@ -1978,7 +1978,7 @@ dependencies = [ + + [[package]] + name = "fabro-dev" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "anyhow", + "assert_cmd", +@@ -1997,7 +1997,7 @@ dependencies = [ + + [[package]] + name = "fabro-dump" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "anyhow", + "bytes", +@@ -2011,7 +2011,7 @@ dependencies = [ + + [[package]] + name = "fabro-environment" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "fabro-config", + "fabro-types", +@@ -2027,7 +2027,7 @@ dependencies = [ + + [[package]] + name = "fabro-github" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "anyhow", + "base64", +@@ -2049,7 +2049,7 @@ dependencies = [ + + [[package]] + name = "fabro-graphviz" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "anyhow", + "fabro-types", +@@ -2063,7 +2063,7 @@ dependencies = [ + + [[package]] + name = "fabro-hooks" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "async-trait", + "fabro-agent", +@@ -2087,7 +2087,7 @@ dependencies = [ + + [[package]] + name = "fabro-http" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "fabro-static", + "http", +@@ -2097,7 +2097,7 @@ dependencies = [ + + [[package]] + name = "fabro-install" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "anyhow", + "base64", +@@ -2113,7 +2113,7 @@ dependencies = [ + + [[package]] + name = "fabro-interview" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "async-trait", + "dialoguer", +@@ -2128,7 +2128,7 @@ dependencies = [ + + [[package]] + name = "fabro-llm" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "anyhow", + "async-trait", +@@ -2163,7 +2163,7 @@ dependencies = [ + + [[package]] + name = "fabro-macros" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "clap", + "fabro-options-metadata", +@@ -2174,7 +2174,7 @@ dependencies = [ + + [[package]] + name = "fabro-manifest" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "anyhow", + "fabro-api", +@@ -2192,7 +2192,7 @@ dependencies = [ + + [[package]] + name = "fabro-mcp" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "anyhow", + "axum", +@@ -2212,7 +2212,7 @@ dependencies = [ + + [[package]] + name = "fabro-mcp-server" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "anyhow", + "chrono", +@@ -2239,7 +2239,7 @@ dependencies = [ + + [[package]] + name = "fabro-model" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "fabro-static", + "http", +@@ -2255,7 +2255,7 @@ dependencies = [ + + [[package]] + name = "fabro-oauth" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "anyhow", + "axum", +@@ -2277,7 +2277,7 @@ dependencies = [ + + [[package]] + name = "fabro-options-metadata" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "serde", + "serde_json", +@@ -2285,7 +2285,7 @@ dependencies = [ + + [[package]] + name = "fabro-proc" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "cc", + "libc", +@@ -2294,7 +2294,7 @@ dependencies = [ + + [[package]] + name = "fabro-redact" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "aho-corasick", + "ref-cast", +@@ -2310,7 +2310,7 @@ dependencies = [ + + [[package]] + name = "fabro-sandbox" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "anyhow", + "async-trait", +@@ -2356,7 +2356,7 @@ dependencies = [ + + [[package]] + name = "fabro-server" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "anyhow", + "async-trait", +@@ -2445,7 +2445,7 @@ dependencies = [ + + [[package]] + name = "fabro-slack" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "fabro-http", + "fabro-interview", +@@ -2467,18 +2467,18 @@ dependencies = [ + + [[package]] + name = "fabro-spa" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "rust-embed", + ] + + [[package]] + name = "fabro-static" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + + [[package]] + name = "fabro-store" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "async-trait", + "bytes", +@@ -2505,7 +2505,7 @@ dependencies = [ + + [[package]] + name = "fabro-telemetry" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "anyhow", + "base64", +@@ -2531,7 +2531,7 @@ dependencies = [ + + [[package]] + name = "fabro-template" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "anyhow", + "fabro-types", +@@ -2545,7 +2545,7 @@ dependencies = [ + + [[package]] + name = "fabro-test" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "assert_cmd", + "axum", +@@ -2568,7 +2568,7 @@ dependencies = [ + + [[package]] + name = "fabro-tool" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "anyhow", + "async-trait", +@@ -2589,7 +2589,7 @@ dependencies = [ + + [[package]] + name = "fabro-tracker" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "anyhow", + "async-trait", +@@ -2603,7 +2603,7 @@ dependencies = [ + + [[package]] + name = "fabro-types" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "chrono", + "clap", +@@ -2624,7 +2624,7 @@ dependencies = [ + + [[package]] + name = "fabro-util" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "anyhow", + "console 0.15.11", +@@ -2644,7 +2644,7 @@ dependencies = [ + + [[package]] + name = "fabro-validate" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "fabro-acp", + "fabro-graphviz", +@@ -2657,7 +2657,7 @@ dependencies = [ + + [[package]] + name = "fabro-variable" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "chrono", + "fabro-types", +@@ -2669,7 +2669,7 @@ dependencies = [ + + [[package]] + name = "fabro-vault" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "chrono", + "fabro-static", +@@ -2682,7 +2682,7 @@ dependencies = [ + + [[package]] + name = "fabro-workflow" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "anyhow", + "assert_cmd", +@@ -7494,7 +7494,7 @@ dependencies = [ + + [[package]] + name = "twin-github" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "axum", + "base64", +@@ -7513,7 +7513,7 @@ dependencies = [ + + [[package]] + name = "twin-openai" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + dependencies = [ + "anyhow", + "async-stream", +diff --git a/Cargo.toml b/Cargo.toml +index 6cbe3767b..062736ab1 100644 +--- a/Cargo.toml ++++ b/Cargo.toml +@@ -5,7 +5,7 @@ resolver = "2" + + [workspace.package] + edition = "2021" +-version = "0.247.0-nightly.0" ++version = "0.250.0-nightly.0" + license = "MIT" + + [workspace.dependencies] +diff --git a/lib/crates/fabro-workflow/src/test_support.rs b/lib/crates/fabro-workflow/src/test_support.rs +index af43dc5b8..1acf410a4 100644 +--- a/lib/crates/fabro-workflow/src/test_support.rs ++++ b/lib/crates/fabro-workflow/src/test_support.rs +@@ -10,7 +10,6 @@ use fabro_graphviz::graph::Graph as GvGraph; + use fabro_interview::AutoApproveInterviewer; + use fabro_model::Catalog; + use fabro_store::{ArtifactStore, Database, RunProjection}; +-use fabro_types::test_support::test_run_provenance_with; + use object_store::local::LocalFileSystem; + + use crate::artifact_upload::ArtifactSink; +@@ -176,9 +175,13 @@ async fn initialized( + workflow_slug: run_options.workflow_slug.clone(), + automation: None, + db_prefix: None, +- provenance: test_run_provenance_with(fabro_types::Principal::System { +- system_kind: fabro_types::SystemActorKind::Engine, +- }), ++ provenance: fabro_types::RunProvenance { ++ server: None, ++ client: None, ++ subject: fabro_types::Principal::System { ++ system_kind: fabro_types::SystemActorKind::Engine, ++ }, ++ }, + manifest_blob: None, + git: run_options.pre_run_git.clone(), + fork_source_ref: run_options.fork_source_ref.clone(), diff --git a/stages/009-fixup@1/status.json b/stages/009-fixup@1/status.json new file mode 100644 index 000000000..c2c9c3797 --- /dev/null +++ b/stages/009-fixup@1/status.json @@ -0,0 +1,6 @@ +{ + "outcome": "succeeded", + "notes": "Stage completed: fixup", + "failure_reason": null, + "timestamp": "2026-06-01T20:49:04.348687Z" +} \ No newline at end of file diff --git a/stages/010-verify@2/script_invocation.json b/stages/010-verify@2/script_invocation.json new file mode 100644 index 000000000..7ad2687d7 --- /dev/null +++ b/stages/010-verify@2/script_invocation.json @@ -0,0 +1,5 @@ +{ + "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", + "command": "exec 2>&1\ngit 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", + "language": "shell" +} \ No newline at end of file