fabro/lib/crates
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
..
fabro-agent fix(openai): surface responses stream terminal errors 2026-05-04 11:12:40 -04:00
fabro-api fix(billing): render non-LLM run stages 2026-05-03 22:49:33 -04:00
fabro-auth refactor(api): unify secret metadata types 2026-04-29 20:26:13 -04:00
fabro-checkpoint Make git metadata sandbox-native 2026-04-27 21:43:15 -07:00
fabro-cli Model Test Bounded Concurrency Implementation Plan (#204) 2026-05-04 13:46:44 -04:00
fabro-client fix(cli): bound server readiness probes 2026-05-02 20:13:18 -04:00
fabro-config fix(error): preserve source chains across workspace 2026-05-01 17:30:20 -04:00
fabro-core refactor: simplify auth and actor handling 2026-05-02 11:44:17 -04:00
fabro-dev fix(docs): use MDX comment syntax for generated fences 2026-04-29 01:27:43 -04:00
fabro-devcontainer refactor(static): centralize env var names 2026-04-24 12:29:51 -04:00
fabro-dump Merge origin/main 2026-05-01 22:05:41 -04:00
fabro-github refactor(error): drop String error shims and DisplayContains test traits 2026-05-01 19:35:22 -04:00
fabro-graphviz refactor: simplify Rust review cleanup 2026-05-02 15:41:17 -04:00
fabro-hooks feat(command): distinguish cancelled commands from timeouts 2026-04-30 22:45:37 -04:00
fabro-http refactor(static): centralize env var names 2026-04-24 12:29:51 -04:00
fabro-install feat(install): add sandbox provider step to web install wizard 2026-04-27 14:59:17 -07:00
fabro-interview refactor: simplify auth and actor handling 2026-05-02 11:44:17 -04:00
fabro-llm refactor(server): simplify model availability probes 2026-05-04 11:47:37 -04:00
fabro-macros refactor(dev): simplify generated docs tooling 2026-04-24 18:41:00 -04:00
fabro-mcp fix(error): preserve source chains across workspace 2026-05-01 17:30:20 -04:00
fabro-model refactor(server): simplify model availability probes 2026-05-04 11:47:37 -04:00
fabro-oauth fix(error): preserve remaining error context 2026-05-02 10:51:15 -04:00
fabro-options-metadata refactor(dev): simplify generated docs tooling 2026-04-24 18:41:00 -04:00
fabro-proc refactor(static): centralize env var names 2026-04-24 12:29:51 -04:00
fabro-redact fix(redact): skip name fields to preserve sandbox identifiers 2026-05-03 19:57:55 -04:00
fabro-retro fix(error): preserve remaining error context 2026-05-02 10:51:15 -04:00
fabro-sandbox perf(sandbox): skip throwaway reqwest::Client in Daytona probe 2026-05-04 12:48:44 -04:00
fabro-server perf(sandbox): inject http client into Daytona credential probe 2026-05-04 12:39:46 -04:00
fabro-slack refactor: simplify auth and actor handling 2026-05-02 11:44:17 -04:00
fabro-spa feat(dev): gitignore embedded spa assets 2026-04-26 21:31:11 -04:00
fabro-static feat(server): validate Daytona API key scopes 2026-05-03 17:09:01 -04:00
fabro-store Merge remote-tracking branch 'origin/main' 2026-05-02 10:06:25 -04:00
fabro-telemetry refactor(static): centralize env var names 2026-04-24 12:29:51 -04:00
fabro-template refactor(async): lint std::process::Command across all targets 2026-04-12 13:35:57 -04:00
fabro-test fix(cli): bound server readiness probes 2026-05-02 20:13:18 -04:00
fabro-tracker fix(error): preserve remaining error context 2026-05-02 10:51:15 -04:00
fabro-types fix(workflow): retain exec output tails on failures 2026-05-03 21:30:34 -04:00
fabro-util refactor(error): drop String error shims and DisplayContains test traits 2026-05-01 19:35:22 -04:00
fabro-validate refactor: simplify Rust review cleanup 2026-05-02 15:41:17 -04:00
fabro-vault refactor(api): unify secret metadata types 2026-04-29 20:26:13 -04:00
fabro-workflow chore: fix flaky test 2026-05-04 11:12:40 -04:00