The workflow breadcrumb was previously demo-mode-only. Render it in
all modes and point it at the runs list filtered by that workflow,
which is now honored via URL params.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Search query, repo, workflow, created-time, archived toggle, and
columns/list view are now read from and written to URL search params,
so the selected state survives reloads and is shareable. Defaults are
omitted from the URL to keep links clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Route command stderr into stdout at execution time and expose a single output log across events, projections, API clients, and the web UI. Keep replay compatibility for older command.completed events that still contain split stdout/stderr fields.
Compute cheap diff stats on checkpoint and terminal events, roll them into run summaries, and use them for the Files Changed tab badge without fetching full file diffs.
Successful tool calls of the same tool that run back-to-back (e.g., five
Bash curls to the same endpoint) now collapse into a single Tool group row
labelled "Bash x5", summing durations and using the first call's start
time. Errored calls and tool-name boundaries break the run, so distinct
work is never hidden. Clicking the group opens a panel that lists each
child with its input preview and per-call duration; an inline accordion
reveals the full Tool use / Tool result for one child at a time.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Graph tab duplicated the Overview's diagram. Drop it and the
/runs/:id/graph route, and move the DOT source view to a dedicated
/runs/:id/source page reachable from the sidebar.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces native title attribute with a portal-based Tooltip component on
the events-feed elapsed times and the run header's last-event timestamp,
showing the full datetime (e.g. "04/24/2026, 1:23:40 PM") on hover.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The "Showing final patch only" banner fired for every finished run whose
sandbox had been reaped, which is the normal post-run state and just
adds noise. Other degraded reasons (provider_unsupported,
sandbox_unreachable) still show their banners.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Command nodes now show a "Logs" tab (in place of "Transcript") that fetches
the actual stdout/stderr bytes via the stage log endpoint, rather than the
blob:// refs carried in command.completed events. Exit code and duration
sit on the right side of the toolbar alongside the tab toggle. Agent stages
are unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tool/command rows show a clock icon next to the duration; agent rows
show a tokens icon next to the input/output token count. Adds left
padding between the metric column and the elapsed timestamp column so
the two values read as separate fields.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extracts the model from `stage.prompt`, `agent.session.activated`, and
`agent.cli.started` events that the stage already loads, and renders it
on the right side of the events toolbar with a CpuChipIcon.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds two new items to the run-detail Actions dropdown, always
present and disabled when unavailable. Send interrupt is wired to
POST /api/v1/runs/{id}/interrupt and gated on running status. Send
steering… focuses the bottom-dock steer textarea via a new
forwardRef handle on SteerBar; gated on running status with no
pending questions. Bumps the separator after the new pair to render
whenever any later group exists.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the two floating overlays with a single full-width bottom
panel that's always present on every run-detail tab and renders
either the InterviewDock (when there are pending questions) or the
SteerBar (otherwise). The dock has a top border and bg-page so it
sits flat against the page above instead of floating with a gradient.
Strips the outer fixed/gradient/rounded wrappers from both
InterviewDock and SteerBar so they render as inline content inside
the new dock. SteerBar gets a max-w-4xl centered form, an outlined
textarea field, and a new Interrupt checkbox button (with a visible
amber-fill checkbox indicator) between the input and Send. Send and
Interrupt aren't wired to the steer API yet.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a single-row text input + Send button that pins to the viewport
bottom on every run-detail tab as a placeholder for the future
steering composer (will replace the modal). Renders mutually
exclusive with InterviewDock: the dock takes precedence on blocked
runs with pending questions, otherwise the steer bar shows. Reuses
the existing --fabro-interview-dock-clearance variable so consumers
that already pad for the dock pick up the steer bar's clearance too.
Send is intentionally a no-op for now until the steering API is
swapped over from the modal.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Promote repo to its own column instead of sharing space with the
goal, add a workflow-slug column, and add a relative creation-time
column (with the ISO timestamp on hover) so the list view exposes
the same identifying info the board cards already show.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Promotes DebugRow to a button with the same padding and hover/selected
chrome as the transcript rows, and opens a side panel showing the
event name as the header and the full EventEnvelope JSON, pretty-
printed and syntax-highlighted via highlightJson. Refactors the panel
chrome into a shared DetailsPanel so EventDetailsPanel and the new
DebugEventDetailsPanel both reuse the slide-out animation, header,
and Esc-to-close behavior.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Transcript view is filtered to a single stage, so measure each
event's elapsed time from that stage's started_at instead of the
run's created_at. The previous behavior folded the run's
initialization wait (sandbox build, clone, devcontainer setup) into
every per-stage timestamp. Falls back to RunSummary.start_time and
then created_at for stages or runs that never ran.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Generalize the kind filter into a reusable multi-select and use it for
both Transcript (fixed kinds) and Debug (event-name-prefix categories).
Debug rows show a category pill, the full event name, and elapsed time.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps the row's vertical padding from py-1.5 to py-2.5 so the hover
and selected highlight band is taller and the rows breathe more, and
shows Agent token counts as input / output rather than a single total.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a metric column on the run stages Transcript view: token totals
for Agent messages (from agent.message billing.input_tokens +
output_tokens), and elapsed time for Tool calls (computed from the
paired started/completed timestamps) and Command runs (from
command.completed.duration_ms). Also tightens the column top padding
so the toolbar reads with balanced breathing on each side, and lets
the toolbar underline extend across the row in line with the run-tab
underline pattern.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a segmented toggle to the left of the type filter. Transcript
keeps the existing transcript view; Debug renders a blank panel for
now and hides the kind filter, search, count, and any open detail
panel so they don't suggest controls that aren't wired up yet.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors the toolbar pattern from the run logs view so users can narrow
down stage events by kind (System, Agent, Tool, Command) or by free-text
search across event content. Filter state persists across stage
selection; the open detail panel still resets per stage.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the plain CodeBlock for tool input and result with a JsonBlock
that parses the value, re-stringifies with indent 2, and applies a
small regex-based syntax highlighter (keys, strings, numbers,
booleans, null get distinct theme colors). Non-JSON results — file
contents from Read, error strings — fall through to plain text when
JSON.parse fails. No new dependencies.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bring back the Marked-based renderer for system prompt and assistant
message bodies in the event details panel so headings, lists, inline
code, and fenced blocks render as formatted prose instead of a single
preformatted block. Tool input/result and command scripts continue to
render as fixed-width code since they're JSON/shell. Same URL/HTML
sanitization policy as the prior markdown integration: protocol-
relative and non-http(s)/mailto links are rewritten to empty hrefs,
and raw HTML tokens are dropped.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The full-height shell used min-h-dvh, which only sets a minimum and
leaves height: auto. CSS percentage heights (h-full) don't resolve
against an auto parent, so every descendant that relied on h-full
collapsed to its content size — leaving the run stages column
separator, events list, and detail panel ending mid-page instead of
reaching the window bottom.
Switch the shell to h-dvh, make the run-detail outlet wrapper a flex
column, and replace h-full with flex-1 on the run-stages and
run-files roots so they grow via flex sizing within the column. The
height chain is now: shell h-dvh → main flex-1 → layout div h-full →
run-detail h-full → outlet wrapper flex-1 flex-col → page root flex-1
→ children fill via flex stretch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the fixed-position overlay panel with an in-flow flex sibling.
The panel now starts under the tab bar (not over the navbar) and the
events list contracts via flex-1 to make room for it instead of being
covered. Uses self-stretch on the panel wrapper so its height
propagates reliably; an inner absolute container right-anchored at
w-[28rem] gives the slide-in-from-right reveal as the wrapper width
animates from 0 to 28rem.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces the per-event SystemBlock / AssistantBlock / ToolBlock /
CommandBlock layout with a flat list of three-column rows (label pill,
truncated summary, elapsed time from run start) and a slide-out detail
panel that opens on row click. Tool names are humanized (read_file →
"Read", shell → "Bash", etc.). The vertical column separator now
extends to the actual window bottom via an absolutely positioned line
that bleeds 1.5rem past its flex parent's bottom edge into the layout's
bottom padding, sidestepping a calc(100% + 3rem) approach that wasn't
resolving reliably on a flex-1 ancestor.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The stage label and ticking duration were a redundant repeat of the
sidebar's selected entry. Drop the sticky header (and the now-unused
RunningStageDuration helper) so the right column focuses on the stage
activity.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a `last_event_at` timestamp to RunProjection (set in apply_event so
every event ticks the field) and surface it through RunSummary and the
RunListItem board response. Backed by an OpenAPI extension so both the
Rust and TypeScript clients pick up the new optional field.
In the web UI, the run-detail header gains a "Last activity Xm ago"
badge next to the elapsed-time chip, driven by a 30-second ticker so the
relative time stays current between event refreshes.
The fabro-server tests.rs hunk is incidental rustfmt drift surfaced by
running `cargo fmt --all` over the workspace; including it keeps CI's
fmt-check green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds an "All workflows" dropdown to the /runs toolbar between the
repo filter and the "Show archived" toggle, applied client-side.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds an "All time / Today / Last hour / Last day / Last 7 days /
Last 30 days" dropdown to the /runs toolbar, applied client-side
alongside the existing search and repo filters in both Board and
List views.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Prefix each run header chip with its icon (folder for repo, stack for
workflow, clock for elapsed) and surface the workflow name alongside
the repo so it's discoverable from the detail header.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extend GET /api/v1/boards/runs with include_archived=true (matching the
existing flag on listRuns), add an Archived BoardColumn that the server
appends only when the flag is set, and surface a "Show archived" toggle
on /runs that flips between request shapes. Default behavior is unchanged
— archived runs stay hidden.
Server: list_board_runs now takes ListRunsParams; board_column maps
RunStatus::Archived to BoardColumn::Archived; board_columns(include_archived)
appends the column conditionally. Two new handler tests cover the default
and flag-on paths.
Web: useBoardsRuns(includeArchived) keys requests so SWR refetches on
toggle; columnStatuses + columnStatusDisplay + columnStyles get an
"archived" entry; buildSkeletonColumns filters by the flag so the loading
state matches the eventual response. Two new buildBoardColumns tests cover
both column shapes.
Touched generated TS client files include unrelated whitespace drift from
openapi-generator-cli; including them keeps the working tree consistent
with what `bun run generate` produces.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the row of contextual buttons (Steer / Cancel / Archive / Unarchive
/ Preview) on the run detail page with a single Actions dropdown. Each
action's pending label ("Archiving…", "Cancelling…", etc.) now appears on
its menu item; the trigger shows a spinner while any mutation is in flight
and disables itself to prevent stacked calls. The menu is hidden entirely
when no actions apply.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a toolbar with a multi-select level filter (TRACE/DEBUG/INFO/WARN/ERROR)
and a typeahead search to /runs/{id}/logs. Filtering is record-aware so
multi-line entries (stack traces, indented continuations) stay together with
their parent log line. Combine the panel header into a single row with
filters on the left and size + copy on the right; round byte sizes to whole
units.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a three-dot kebab menu to each runs board column header (in both
column and list view). The menu exposes a single "Archive all" action
that fans out individual archive POSTs for every archivable run in the
column via Promise.allSettled, then revalidates the board. A toast
summarizes full success, partial failure, or total failure. The menu
is hidden when a column has no archivable runs.
Tracked for a future single-POST API in #226.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
macOS recursive fs.watch fires multiple events per logical save and emits
spurious "bubble" events for sibling directories. With the previous slow
~10s tailwind step those re-fires were absorbed between rebuilds; with
~60ms rebuilds the watcher entered a continuous-rebuild loop instead.
- Coalesce events with a 75ms debounce window so one save fires one
rebuild even when the editor produces several FS events.
- Filter to source-relevant extensions (ts/tsx/css/html/images/fonts);
ignore .DS_Store, .tsbuildinfo, swap files, and the extensionless
bubble events (e.g. "rename images") that were the dominant source
of the loop.
- Optional FABRO_BUILD_DEBUG=1 logs which path queued or skipped each
rebuild for future diagnosis.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
In --watch-web dev mode the server silently fell back to the embedded SPA
snapshot whenever the disk dist/ was missing or partial, so edits to the
web app appeared not to take effect with no error anywhere. This change
makes the dev loop visible and quick:
- static_files plumbs a dev_disk_only flag from RouterOptions.watch_web
into the fallback handler. When set, embedded fallback is skipped and
a miss returns 503 with a "build in progress" auto-refresh page.
- The web build script writes each rebuild into apps/fabro-web/.dist-builds/<id>/
and atomically replaces the dist symlink via rename(2), so requests
never observe a partially-populated dist tree.
- Tailwind is invoked through node_modules/.bin/tailwindcss directly
instead of bunx, removing a per-rebuild bun add @latest --force round-
trip and dropping rebuild time from ~10s to ~250ms.
- load_asset no longer falls through to the workspace dist/ when an
explicit asset_root is provided, restoring test isolation when a
real dev build is sitting next door.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Always route non-empty Files Changed views through Pierre's Virtualizer and worker pool, with full-height layout propagation and stable per-file cache keys. Copy Pierre worker assets during the web build so the static worker URL resolves in production.
## Summary
This makes the advertised mid-run steering path real: users can send
append or interrupt steering messages through the API, CLI, and web UI,
and the worker delivers them to live API-mode agent sessions or buffers
them for the next session. The change adds the control protocol, session
interrupt machinery, workflow hub, server route/OpenAPI/client updates,
and UI feedback needed for the whole path.
### Plan Summary
- Add `SteerKind`/`run.steer` wire protocol and `POST /runs/{id}/steer`
- Deliver steers through subprocess JSONL or the in-process
`SteeringHub`
- Support append and interrupt behavior in agent sessions, with bounded
buffering and events
- Expose steering in the CLI/web UI and surface SSE toasts
## Flow
```mermaid
flowchart TB
UI["CLI / Web UI"] --> API["POST /runs/{id}/steer"]
API -->|"subprocess transport"| Control["Worker control JSONL"]
API -->|"in-process transport"| Hub["SteeringHub"]
Control --> Hub
Hub -->|"active API sessions"| Session["SessionControlHandle"]
Hub -->|"no active session"| Pending["Pending buffer"]
Pending -->|"first future API session"| Session
Session --> Agent["Session round loop"]
Agent --> Events["RunEvent stream"]
Events --> UI
```
## What changed and why
- Agent sessions now expose a lightweight `SessionControlHandle`, drain
steering at the top of each round, and use a replaceable round
cancellation token for interrupts. LLM waits are cancelled promptly,
while tool execution observes cancellation cooperatively so every
committed `tool_use` still gets a matching `tool_result`.
- `SteeringHub` owns active API session registration, broadcast
delivery, pending buffering, FIFO queue caps, and steering
lifecycle/drop events. A completion coordinator closes the
final-response race without introducing a workflow dependency into the
agent crate.
- The server route replaces the 501 stub, validates run state and
best-effort CLI-only steerability, and forwards through either
subprocess control JSONL or the in-process hub. OpenAPI and generated
clients now include the request type.
- The CLI and web UI can send append or interrupt steers. Run detail and
board views open the new composer, and shared SSE subscriptions now
support per-subscriber event callbacks so invalidation and steering
toasts can coexist on one EventSource.
## Review notes
- Steering actors stay on top-level `RunEvent.actor`; event props only
carry steering kind/drop metadata.
- Buffered steers replay as append messages to the first API session
that registers after an empty-active period. Per-stage targeting remains
out of scope.
- CLI-mode agent stages are still not steerable; the server returns a
best-effort 409 when all active agent stages are CLI-mode, while the
worker hub remains the authoritative safety net.
- No persistence or schema migration is required; active and pending
steering state is in memory.
- New tests focus on protocol round-trips, hub buffering/bounds, session
steering-loop behavior, SSE fanout, and basic server rejection paths.
⚒️ Generated with [Fabro](https://fabro.sh)
---------
Co-authored-by: Fabro <noreply@fabro.sh>
Co-authored-by: Bryan Helmkamp <bryan@brynary.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
## Summary
Token scopes describe what *a run* is authorized to do, not server
identity. Today they live under
`[server.integrations.github.permissions]`, which can't be overridden by
`workflow.toml` / `project.toml` (server keys are stripped from
per-workflow layers) — so projects and workflows can't tighten or relax
permissions despite the docs already advertising a per-run config. This
PR moves them under `[run.integrations.github.permissions]`, where the
standard layer-merge (workflow > project > user > defaults) Just Works.
Greenfield, no migration shim.
## What changed
- **New layer/resolved types** in `fabro-config` and `fabro-types`:
`RunIntegrationsLayer`, `RunIntegrationsGithubLayer`, and resolved
counterparts. `permissions` becomes a flat `HashMap<String,
InterpString>` post-resolve; empty = no token requested.
- **Server schema**: `permissions` removed from `GithubIntegrationLayer`
/ `GithubIntegrationSettings`. `deny_unknown_fields` rejects the stale
path.
- **Bundled `workflow.toml` parsing** (`run_manifest.rs`): now goes
through `SettingsLayer` via the new `parse_run_layer_from_settings_toml`
helper, so stale `[server.integrations.github.permissions]` errors
instead of being silently dropped by the old `toml::Table` lift-out.
- **Consumers updated**: server preflight, run launch path, and the CLI
worker (`runner.rs`) all read run-level permissions. CLI worker
previously hardcoded `HashMap::new()` — runs launched via the local CLI
path were getting no `GITHUB_TOKEN` regardless of TOML.
- **Shared helpers** on `RunIntegrationsGithubSettings`:
`is_token_requested()` and `resolve_permissions(lookup)` so server and
CLI don't drift.
- **OpenAPI + TS client** regenerated; new `RunIntegrationsSettings` /
`RunIntegrationsGithubSettings` schemas added, `permissions` removed
from `GithubIntegrationSettings`.
- **Repo workflows + docs** rewritten to the new path. Docs gain a
security-model note (boundary = installation grants; no Fabro-side cap).
## Key design decision: hand-rolled `Combine` for
`RunIntegrationsGithubLayer`
`ReplaceMap`'s "empty inherits from below" semantics (`maps.rs:76-80`)
are wrong here — we want `permissions = {}` in a higher layer to act as
an explicit clear. So the layer field is `Option<HashMap<...>>` with
hand-rolled `Combine`:
| Higher layer | Lower layer | Result |
|---|---|---|
| `None` | anything | lower (inherit) |
| `Some(map)` | anything | `Some(map)` (full replace, including
`Some({})` = clear) |
Not derived: the blanket `Option<T: Combine>` impl would recurse into
the inner `HashMap` and reintroduce empty-fallback. Documented inline in
`layers/run.rs`.
`InterpString` is preserved through resolve and only flattened to
`String` at the start-services boundary, matching the existing pattern.
### Plan Summary
- New `[run.integrations.github.permissions]` layer + resolved types;
remove from server side.
- Hand-rolled `Combine` so empty-wins-as-clear; no change to
`ReplaceMap` semantics for other consumers.
- Strict `SettingsLayer` parse for bundled `workflow.toml` so stale
schema errors loudly.
- Both server and CLI worker paths read run-level permissions via shared
helpers.
- OpenAPI + TS client regenerated; parity test added.
- Repo workflow TOMLs and `integrations/github.mdx` rewritten.
### Fabro Details
<details>
<summary>Ran 0 stages in 61m 23s for $53.41</summary>
| Stage | Duration | Cost | Retries |
|---|---|---|---|
| **Total** | **61m 23s** | **$53.41** | **0** |
</details>
<details>
<summary>Ran <code>ImplementPlan.fabro</code> (12 nodes and 15
edges)</summary>
```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."]
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="cargo +nightly-2026-04-14 clippy -q --workspace --all-targets -- -D warnings 2>&1 && cargo nextest run --cargo-quiet --workspace --status-level fail 2>&1 && cargo dev docs refresh 2>&1 && cargo dev docs check 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 clippy lint warnings, test failures, and generated docs errors.", max_visits=3]
fmt [label="Format", shape=parallelogram, script="cargo +nightly-2026-04-14 fmt --all 2>&1", max_retries=0]
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 -> fmt [condition="outcome=succeeded"]
verify -> fixup
fixup -> verify
fmt -> exit
}
```
</details>
⚒️ Generated with [Fabro](https://fabro.sh)
---------
Co-authored-by: Fabro <noreply@fabro.sh>
Co-authored-by: Bryan Helmkamp <bryan@brynary.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>