Commit graph

3199 commits

Author SHA1 Message Date
Fabro
39c172ae28 fabro(01KQW3293BWKEXCA7F3YVFPAH5): list_prs (failed)
Fabro-Run: 01KQW3293BWKEXCA7F3YVFPAH5
Fabro-Completed: 2
Fabro-Checkpoint: 3ec495bdb1

⚒️ Generated with [Fabro](https://fabro.sh)
2026-05-05 12:51:23 +00:00
Bryan Helmkamp
b6ea81b678
Merge remote-tracking branch 'origin/main' 2026-05-05 08:34:37 -04:00
fabro-sh-0530[bot]
333b603f5b
Encode stage visits in run stage URLs (#206)
### Summary
Stages that re-enter the same workflow node now get distinct
`node@visit` identities end to end, so looped stages like `verify@1` and
`verify@2` no longer collapse to the same sidebar link, event stream,
graph selection, or turns view.

### What changed
- `RunStage.id` now uses the full `StageId` string (`node_id@visit`),
with required `node_id` and `visit` fields in the OpenAPI schema and
generated clients. This intentionally replaces the old `dot_id` field.
- The server builds `/runs/{id}/stages` from
`RunProjection::iter_stages()` instead of checkpoint `completed_nodes`,
preserving visit information and including in-flight stages from
projection data.
- Stage status is derived from the latest lifecycle event for each exact
`stage_id`, so retrying stages do not appear failed while a retry is
underway.
- The frontend maps and displays visits with `(N)` suffixes, filters
fallback turns by `stage_id`, invalidates suffixed stage-turn query keys
from SSE, and aggregates graph nodes by `node_id` with latest-visit
click targets.

### Plan Summary
- Preserve per-visit stage identity across API, server projection,
generated clients, and UI routing.
- Keep graph nodes keyed by workflow node while routing clicks to the
latest visit.
- Add coverage for multi-visit stages, retrying status derivation,
suffixed SSE invalidation, sidebar labels, and stage event filtering.

### Reviewer notes
This is a breaking API shape change for `RunStage`: consumers should use
`node_id` for graph/node identity and `id` for per-visit stage identity.
The old `dot_id` field is removed rather than kept as a compatibility
alias.

### Fabro Details

<details>
<summary>Ran 9 stages in 54m 55s for $41.40</summary>

| Stage | Duration | Cost | Retries |
|---|---|---|---|
| start | 0s | – | 0 |
| toolchain | 1s | – | 0 |
| preflight_compile | 2m 8s | – | 0 |
| preflight_lint | 2m 14s | – | 0 |
| implement | 31m 38s | $17.65 | 0 |
| simplify_opus | 10m 2s | $2.40 | 0 |
| simplify_gpt | 6m 9s | $21.35 | 0 |
| verify | 2m 3s | – | 0 |
| fmt | 2s | – | 0 |
| **Total** | **54m 55s** | **$41.40** | **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>
2026-05-05 08:27:11 -04:00
Bryan Helmkamp
d9c8030e74
refactor(workflow): harden PR content generation
Return named PR content from the builder and keep title/body fallback logic inside the builder.

Move the PR body prompt to markdown and scale prompt truncation from model context windows.

Keep PR creation resilient when generated bodies are empty by emitting a reviewer-visible skeleton body.
2026-05-05 08:25:17 -04:00
fabro-releases[bot]
a2fbac1d60 Bump version to 0.224.0-nightly.0 2026-05-05 09:52:01 +00:00
fabro-sh-0530[bot]
7769c5cec1
Generate Fabro PR titles and bodies with structured output (#208)
Some checks are pending
TypeScript / Typecheck (push) Waiting to run
TypeScript / Test (push) Waiting to run
TypeScript / Build (push) Waiting to run
Rust / Format (push) Waiting to run
Rust / Clippy (push) Waiting to run
Rust / Generated Docs (push) Waiting to run
Rust / Test (Linux) (push) Waiting to run
Rust / Test (macOS) (push) Waiting to run
### Summary
Fabro now asks the LLM for a structured PR title and reviewer-sized body
instead of deriving every title from the workflow goal. This ports the
compound-engineering PR-writing recipe into the existing pull request
pipeline while preserving Fabro's programmatically appended trailing
sections.

### What changed
- Replaced plain-text PR body generation with `generate_object` and a
strict `{ title, body }` schema.
- Added the sizing matrix, writing principles, visual-aid guidance, and
duplicate-section guardrails to the PR prompt.
- Added model-aware goal/plan/diff truncation caps, with unknown or
smaller-context models using the conservative tier.
- Kept goal-derived titles as a narrow fallback only when the LLM
returns a usable body with an empty title.
- Enforced a 72-character title cap across both LLM-generated and
fallback titles.
- Updated workflow, server, and integration tests for structured
responses, fallback behavior, title truncation, and blank-body failures.

### Plan Summary
- Move PR content generation to structured output.
- Keep existing body assembly and appended sections intact.
- Add coverage for title fallback and validation edge cases.

### Fabro Details

<details>
<summary>Ran 9 stages in 42m 43s for $44.59</summary>

| Stage | Duration | Cost | Retries |
|---|---|---|---|
| start | 0s | – | 0 |
| toolchain | 1s | – | 0 |
| preflight_compile | 2m 3s | – | 0 |
| preflight_lint | 2m 13s | – | 0 |
| implement | 15m 8s | $6.37 | 0 |
| simplify_opus | 11m 16s | $3.53 | 0 |
| simplify_gpt | 9m 5s | $34.69 | 0 |
| verify | 2m 17s | – | 0 |
| fmt | 2s | – | 0 |
| **Total** | **42m 43s** | **$44.59** | **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>
2026-05-04 22:00:15 -04:00
Bryan Helmkamp
4b100d350d
chore: add plan 2026-05-04 16:09:12 -04:00
Bryan Helmkamp
b5b08e78d3
refactor(api): reuse board column contract across clients
Make BoardColumnDefinition.id reference the existing BoardColumn schema and carry that typed contract through generated TypeScript, server responses, demo data, and the runs board UI.
2026-05-04 15:52:24 -04:00
Bryan Helmkamp
63940fdddc
fix(web): recover cross-tab SSE coordination after fallback
Reset coordinator state when the last subscriber leaves, clear pending debounce timers on close, and keep coordinated EventSource construction owned by the coordinator while fallback subscriptions keep their local factories.
2026-05-04 15:52:18 -04:00
Bryan Helmkamp
e4e51511e0
refactor(web): simplify cross-tab SSE message parsing and helpers
Use unknown.ts helpers in parseMessage, factor out parseLeaderPair/Triple
and per-variant parsers to remove repeated typeof guards. Extract
leaderIsFresh() for the staleness check used in three places, and make
RecentEventCache amortized O(1) by walking expired entries from the
oldest instead of scanning the whole map per event. Drop the
closeOnTerminal parameter in run-events; the fallback path computes
close at its single call site.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 15:38:19 -04:00
Bryan Helmkamp
6529845554
fix(web): clean up cross-tab SSE lifecycle
Prune stale election candidates as generations advance, reset coordination availability on explicit close, and keep fallback subscribers tracked so coordinator shutdown can clean them up consistently.
2026-05-04 15:29:42 -04:00
Bryan Helmkamp
38726666af
fix(web): harden cross-tab SSE fallback
Stop coordinated election and leadership work when BroadcastChannel posting fails, so tabs degrade cleanly to per-subscriber fallback without stale resync or heartbeat side effects. Expand election coverage for the edge cases called out in the coordination plan.
2026-05-04 15:24:25 -04:00
Bryan Helmkamp
ade721ae65
feat(web): coordinate SSE subscriptions across tabs
Elect a single browser tab to own the global attach stream and broadcast run events to sibling tabs. Keep the existing per-tab EventSource path as the fallback when cross-tab coordination is unavailable.
2026-05-04 14:54:39 -04:00
Bryan Helmkamp
f39e512990
feat(web): split Queued column out of Initializing on the run board
Submitted and Queued lifecycle statuses now live in a dedicated Queued
column rendered to the left of Initializing; Starting stays in
Initializing. The column is omitted from the board when it has no items
so day-to-day boards stay compact.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 14:13:01 -04:00
Bryan Helmkamp
8064aa269e
fix(web): hide runs landing zero-state until data resolves
Render kanban column shells while board/auth/system queries load, so the
"Your runs will appear here" panel no longer flashes before runs arrive.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 13:46:58 -04:00
Bryan Helmkamp
3eef9ee928
Model Test Bounded Concurrency Implementation Plan (#204)
This change makes bulk `fabro model test` run configured model checks
concurrently instead of serially. A new `--jobs/-j` flag (defaulting to
4, minimum 1) controls the concurrency bound; the single-model path
(`--model <MODEL>`) is unaffected. Under the hood, the serial `for` loop
over configured models is replaced with a
`futures::stream::buffer_unordered(jobs)` pipeline that clones the
shared-state `Client` per request. Completed results carry their
original list index and are sorted before rendering, so final stdout
table rows and JSON output remain in listing order regardless of which
requests finish first.

Three new integration tests verify the concurrency behavior using an
inline Axum harness with a `ConcurrencyGate` barrier. The gate holds all
in-flight requests until the expected number arrive simultaneously, then
releases them, letting tests assert `max_in_flight` exactly rather than
relying on timing. The ordering test goes further by assigning
reverse-listing response delays so the last-listed model always finishes
first; if the index sort were dropped, the JSON result order would
invert and the assertion would fail. A 15-second gate timeout ensures a
regression to serial execution surfaces as a clear `max_in_flight == 1`
failure rather than a hung test.

Existing behavior is fully preserved: unconfigured models are still
skipped without a POST, a configured model returning `skip` after
listing is still a failure, `--deep` uses the same `--jobs` value, and
`--jobs 1` reproduces the previous serial behavior for users hitting
provider rate limits.

### Fabro Details

<details>
<summary>Ran 9 stages in 30m 52s for $19.61</summary>

| Stage | Duration | Cost | Retries |
|---|---|---|---|
| start | 0s | – | 0 |
| toolchain | 1s | – | 0 |
| preflight_compile | 2m 8s | – | 0 |
| preflight_lint | 2m 21s | – | 0 |
| implement | 8m 56s | $3.87 | 0 |
| simplify_opus | 7m 55s | $1.43 | 0 |
| simplify_gpt | 6m 58s | $14.32 | 0 |
| verify | 1m 49s | – | 0 |
| fmt | 2s | – | 0 |
| **Total** | **30m 52s** | **$19.61** | **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>
2026-05-04 13:46:44 -04:00
Bryan Helmkamp
92cfcbde71
chore: update plan 2026-05-04 12:55:19 -04:00
Bryan Helmkamp
88216313bf
perf(sandbox): skip throwaway reqwest::Client in Daytona probe
Two follow-ups that were still costing ~1s per credential probe:

- Bumped the daytona-sdk-rust pin to fa4870f, which deletes a dead
  underscore-prefixed _http_client field on Client. The field was
  unused but new_with_config built a fresh reqwest::Client for it on
  every call, paying the macOS proxy-discovery tax even with our
  injection seam in place.

- build_api_keys_configuration was using Configuration::new() and then
  overwriting cfg.client with our injected client. The Default impl
  generated by openapi-generator builds a reqwest::Client::new() for
  the client field eagerly, which we then threw away — another
  ~470ms hit per probe. Construct the Configuration as a struct
  literal so the injected client is the only one we ever build.

Drops the three credential-probe tests from ~700ms to ~10ms.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 12:48:44 -04:00
Bryan Helmkamp
17f1d1dfeb
perf(sandbox): inject http client into Daytona credential probe
Routes the two reqwest clients in the Daytona credential probe through
fabro_http (system-proxy) in production and fabro_test::test_http_client
(no_proxy) in tests, by threading an http_client parameter through
check_daytona_api_key_with and build_api_keys_configuration. Bumps the
daytona-sdk-rust pin to 314ffd9, which exposes DaytonaConfig::http_client
and ships on reqwest 0.13.

Drops the three credential-probe unit tests from >1s SLOW to ~0.5s by
skipping macOS proxy discovery on the localhost httpmock requests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 12:39:46 -04:00
Bryan Helmkamp
f1742d1ab2
test(cli): align attach JSON snapshot with new OpenAI default
The default OpenAI model moved from gpt-5.5 back to gpt-5.4 in 38b51c4c2,
but this attach test snapshot still asserted gpt-5.5.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 11:54:45 -04:00
Bryan Helmkamp
38b51c4c29
refactor(server): simplify model availability probes
Use a lightweight basic probe target for preflight instead of fabricating catalog models, run configured model probes with bounded concurrency, and keep expensive model choices opt-in for defaults and live tests.
2026-05-04 11:47:37 -04:00
Bryan Helmkamp
2ef34a228e
docs: sync public docs to recent runtime changes 2026-05-04 11:43:03 -04:00
Bryan Helmkamp
31cbdb5c31
docs(changelog): refresh recent product changes 2026-05-04 11:38:55 -04:00
Bryan Helmkamp
061ccc673b
refactor(server): probe LLM providers concurrently in doctor
Use join_all to fan out provider probes instead of awaiting them
sequentially, and reuse fabro_util::error::collect_chain for the chain
rendering. Carry Provider through ProviderFailure instead of stringifying
it at construction.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 11:32:31 -04:00
Bryan Helmkamp
f4f5416db8
docs: clarify chain-rendering boundary in error strategy
`thiserror`-derived `Display` does not walk `#[source]`, so `format!("{err}")`
and `format!("{err:#}")` on a typed error silently produce only the
top-level message — the same format string changes meaning when migrating
from `anyhow::Result` to a typed `Result`. Point at
`fabro_util::error::collect_chain` as the canonical helper and broaden
the test guidance to cover typed errors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 11:28:28 -04:00
Bryan Helmkamp
8dcb8ebe7e
fix(server): preserve LLM error source chain in doctor output
`fabro_llm::Error`'s Display only renders the top-level message field for
`Network`/`Stream`/`Configuration`/`RequestTimeout` variants — the
`#[source]` chain is dropped. Walk the chain at the rendering boundary
so connectivity failures (DNS, connection refused, TLS) surface their
underlying cause in `fabro doctor` output.

Per docs/internal/error-handling-strategy.md, CLI surfaces should render
the full cause chain. Adds a regression test that walks `err.source()`
on a typed Network error with an inner io::Error.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 11:23:58 -04:00
Bryan Helmkamp
558e585985
feat(server): treat LLM provider probe failures as errors
`fabro doctor` now classifies LLM provider connectivity and auth probe
failures as `CheckStatus::Error` (so the command exits non-zero) and
surfaces the actual probe error text — truncated to one short line per
provider — instead of the generic "Connectivity issues with: <provider>".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 11:20:51 -04:00
Bryan Helmkamp
36c686b828
test(server): avoid float equality in usage scenario
Use a tolerance-based runtime assertion so workspace clippy can run with float_cmp denied.
2026-05-04 11:12:40 -04:00
Bryan Helmkamp
f3f0fa0e80
fix(openai): surface responses stream terminal errors
Propagate OpenAI Responses SSE error and response.failed events as structured provider errors, and treat response.incomplete as a normal length finish with partial output preserved.

Also preserve those stream errors through Codex-mode complete_via_stream and add agent coverage proving quota failures do not replay the turn.
2026-05-04 11:12:40 -04:00
Bryan Helmkamp
493ab7f442
chore(catalog): set Opus 4.7 as the Anthropic default
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 11:12:40 -04:00
Bryan Helmkamp
7be557312e
chore(workflows): bump implement-plan simplify stage to gpt-5.5
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 11:12:40 -04:00
Bryan Helmkamp
e0594a86bd
chore: add plan 2026-05-04 11:12:40 -04:00
Bryan Helmkamp
a614183378
chore(catalog): add GPT-5.5 and GPT-5.5 Pro, set 5.5 as OpenAI default
GPT-5.5 (released 2026-04-23) replaces 5.4 as the default OpenAI model.
Live integration tests confirm both new IDs respond on the OpenAI API;
they require default temperature like other reasoning models.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 11:12:40 -04:00
Bryan Helmkamp
b5bde88614
fix(preflight): finish LLM probe regression 2026-05-04 11:12:40 -04:00
Bryan Helmkamp
0fca8a3625
fix(workflows): refresh and check generated docs in verify gate
PR #202 shipped a new CLI subcommand without regenerating
docs/public/reference/cli.mdx, so the Generated Docs CI job failed on
push. The implement-plan workflow's verify gate had no equivalent of
`cargo dev docs check`.

Append `cargo dev docs refresh && cargo dev docs check` to verify so
the gate auto-fixes drift and surfaces real authoring errors (missing
help text, removed generated-region fences) through the fixup loop.
Also broaden the fixup prompt to cover docs errors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 11:12:40 -04:00
Bryan Helmkamp
33180703dd
chore(workflows): bump default model to claude-opus-4-7
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 11:12:40 -04:00
Bryan Helmkamp
be0b5829b9
chore: add plan 2026-05-04 11:12:40 -04:00
Bryan Helmkamp
1994f938d7
chore: fix flaky test 2026-05-04 11:12:40 -04:00
Bryan Helmkamp
ea3b7437e2
fix(workflows): add --all-targets to clippy in fabro workflows
The implement-plan and smoke workflows ran clippy without --all-targets,
so test, example, and bench targets were skipped. CI runs clippy with
--all-targets, so lint errors in test code passed the workflow's verify
gate but failed CI on push. Aligns the workflow lint commands with CI
and CLAUDE.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 11:12:40 -04:00
fabro-releases[bot]
9b0e700a8d Bump version to 0.223.0-nightly.0
Some checks are pending
Rust / Format (push) Waiting to run
Rust / Clippy (push) Waiting to run
Rust / Generated Docs (push) Waiting to run
Rust / Test (Linux) (push) Waiting to run
Rust / Test (macOS) (push) Waiting to run
TypeScript / Typecheck (push) Waiting to run
TypeScript / Test (push) Waiting to run
TypeScript / Build (push) Waiting to run
2026-05-04 03:13:54 +00:00
Bryan Helmkamp
253af11508
refactor(billing): simplify run-billing post-review cleanups
Use BilledTokenCounts::default() for the non-LLM branch, hoist the
by-model stage count and hasLlmStages predicate out of JSX, and drop
the in-test for-loop in favor of iterator-based assertions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 23:01:04 -04:00
Bryan Helmkamp
95eb13750a
fix(billing): render non-LLM run stages
Include completed stages without LLM usage in run billing responses so command-only runs still show runtime rows. Keep token and model aggregates scoped to billed LLM usage, and render placeholder values in the web billing table.
2026-05-03 22:49:33 -04:00
Bryan Helmkamp
6780dff63f
fix(workflow): retain exec output tails on failures
Keep sandbox exec failures structured until event emission so git push, checkpoint, notice, and retro failures can expose redacted output tails without expanding their terse error strings.

Also add log rendering that appends sanitized tail content for exec-backed errors while preserving the existing safe Display behavior.
2026-05-03 21:30:34 -04:00
Bryan Helmkamp
446c7cc065
fix(sandbox): surface snapshot lifecycle progress
Emit snapshot slow-path events only when Docker or Daytona actually performs image or snapshot work, replace retired completion markers with snapshot.ready, and render the lifecycle in attach/log output.
2026-05-03 20:44:16 -04:00
Bryan Helmkamp
fe342a4bd7
fix(redact): skip name fields to preserve sandbox identifiers
ULID-derived sandbox names like fabro-01KQR3V9D4VPFFWMNTVH09J48G tripped
the entropy detector and rendered as REDACTED in CLI run output.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 19:57:55 -04:00
Bryan Helmkamp
93f255c6c4
fix(web): preserve run-overview exit-node color after archive
Use the archived status's prior terminal kind so the Exit node keeps
its succeeded/failed fill instead of falling back to the default
transparent server fill.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 19:42:07 -04:00
Bryan Helmkamp
57dad3a11f
chore(daytona): preinstall pinned nightly toolchain in snapshot
The smoke workflow's Lint Rust stage runs `cargo +nightly-2026-04-14
fmt`/`clippy`. The previous fabro-v7 snapshot only had stable, so rustup
silently synced the nightly channel on every run. Bump to fabro-v8 and
add `rustup toolchain install nightly-2026-04-14` with clippy+rustfmt so
lint starts immediately.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 19:33:28 -04:00
Bryan Helmkamp
a425724719
refactor(server): reuse canonical_origin in run_web_url
Delegate to the existing AppState::canonical_origin helper instead of
re-resolving server.web.url and re-checking emptiness inline. The helper
already validates the URL via validate_public_url, so a misconfigured
non-http(s) origin no longer leaks through into run_web_url's output.

Also pass web_url into create_run_input directly rather than constructing
with None and immediately patching the field at the call site.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 18:27:34 -04:00
Bryan Helmkamp
649b57c185
feat(api): expose web_url on run responses and run.created event
So that CLI and other API consumers can surface a clickable link to the
run's web UI page instead of guessing route shapes or probing settings.
The server populates `web_url` from `server.web.enabled` and
`server.web.url`, returns it on `RunStatusResponse` (create plus all
lifecycle transitions), and persists it on the `run.created` event so
attach replays the same link without re-deriving it.

CLI: prints `Web UI: <url>` as a run-header info line, driven off the
replayed event so fresh runs and `attach` share one code path. Absent
when the UI is disabled.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 18:18:19 -04:00
Bryan Helmkamp
e5c5feaa8d
fix(workflows): pin clippy and fmt to nightly-2026-04-14
The smoke and implement-plan workflows ran cargo clippy without a
toolchain prefix, so on the Daytona snapshot they fell through to the
baked-in stable toolchain. clippy.toml now uses allow-unwrap-types
(added in clippy 1.95), which the stable in fabro-v7 doesn't recognize.
Pin every fmt and clippy invocation to nightly-2026-04-14 so they match
.github/workflows/rust.yml. Also update the public repl-handoff example
to keep the documented template consistent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 17:32:46 -04:00