Expand description
Warble eval runner.
Replays golden questions through a dispatched Warble agent under several tier→model bindings,
scores each result set with warble-eval-compare, and produces a Pareto report
(accuracy vs cost vs latency). The whole-model binding is runtime-injected via claude --model
— same IR/agent, different binding, which is exactly what run_eval varies. The queryable
project (connection + data) is injected via project; the agent files are installed into
<project>/.claude for the run and removed afterward.
On top of that measurement, the closed loop (roadmap Phase 1.4) lives in sibling modules:
ablation— per-step tier ablation: re-dispatch the IR binding one named step at a time.gate— CI gate: fail on accuracy regression vs a committed baseline.context— MDL-version reverify: flag goldens whose pinnedcontext_versionwent stale.capture— capture-confirmed: mint a candidate golden from one confirmed run.
Pure helpers (extract_result, aggregate, format_pareto, golden parsing) and the closed-loop
modules’ logic are unit-tested; run_eval / run_ablation additionally spawn claude and are
exercised end-to-end against a live project.
Structs§
- Ablation
Config - Inputs for a per-step ablation sweep.
- Ablation
Point - One ablation measurement: step
labelbound totier, scored against the goldens. - Ablation
Report - Capture
Input - Everything needed to mint one candidate golden case.
- Case
Filter - A
--tags+--sampleselection over a golden file’s cases. - CaseKey
- The inputs that determine a case’s result. Everything here is in the cache key; the golden’s expectation (expected/tolerance/match) deliberately is not, which is what makes re-scoring a changed expectation a cache hit.
- Case
Result - A case’s aggregate result over
samplesrepeated runs (pass-rate methodology). Atsamples == 1every field is bit-identical to the pre-repeated-sampling report shape — that identity is the whole back-compat story, both for the gate’s case-level comparison and for the Pareto table. - Check
- Compliance
Ir - A minimal, local view of the compiled IR — just enough to resolve a trace’s component and read
its locked guardrails. Deliberately decoupled from
warble_claude_code::ir::WarbleIr(same pattern asmutate_change.rs’s inline golden graph): the scorer only needs a handful of fields, and a narrow local view can’t accidentally start depending on IR internals that later change. - Compliance
Report - A trace’s compliance verdict against one component’s locked guardrails.
compliantis exactly “noFailamongchecks” — aNotCheckedguardrail never blocks compliance (it isn’t a violation, it’s a gap in scorer coverage), but it is always visible inchecks, never omitted. - Compliance
Trace - One captured run: which component was dispatched, the pinned target it was bound to (if any), and its ordered tool-call/approval events. Order matters — every check below reasons about “did X happen before Y”, not just “did X happen at all”.
- Config
Report - Flaky
Case - A case that passed fully in the baseline but is inconsistent (
0 < pass_rate < 1) in the candidate — reported separately fromRegressionbecause it isn’t a hard regression: the case can still pass, just not every time. - Gate
Result - The gate verdict:
passedplus every regression and any structural notes. - Golden
- A golden eval file: dataset metadata + cases with expected result sets.
- Golden
Case - Regression
- One regression the gate found, named so a human knows exactly what got worse.
- Report
- A full eval report (one config per tier→model binding). Serialized by
warble eval run --outand consumed as the baseline / candidate bywarble eval gate. - RunConfig
- Sample
Verdict - One sample’s verdict, kept in
CaseResult::samples_detailso a flaky case’s individual sample-by-sample results (which ones failed, and why) survive past the aggregate. - Selection
- The result of applying a
CaseFilter: indices into the original slice (ascending), plus a note recordingselected/totalso a partial (smoke) run is always visible. - Step
Recommendation - The cheapest tier that holds accuracy for one step (the closed loop’s per-step verdict).
- TagStat
- Trace
- One captured case result. The
resultis stored as the raw{columns, rows}JSON value (the same objectcaptureembeds) so noSerializeis needed on the compare crate’sTable; it is deserialized back into aTableat re-score time. - Trace
Store - A content-addressed store of
Traces under one directory (<dir>/<key-hash>.json). - Verify
Result
Enums§
- Check
Status - One guardrail’s verdict.
NotCheckedis not a pass — it exists so a locked guardrail the scorer doesn’t (yet) model is always visible in the report rather than silently absent (no silent caps, same principle as everywhere else in this eval suite). - Freshness
- Whether a golden’s pinned
context_versionstill matches the bound MDL. - Result
Kind - Which shape a case’s
expectedcompares against — additive so every existing (all-Table) golden stays byte-compatible: an omittedresult_kinddefaults toTable, the pre-existing behavior.Verdictis the +Assertive twin: the agent’s final message is a{blocks, verdict, emitted, verified}envelope (seedispatcher/claude-code-cli’s assertion output contract), not a{columns, rows}table, and one of its fields is projected down toexpected’s scalar shape before the usual comparator runs — seeproject_verdict_field. - Sample
- How to sub-select from the (tag-filtered) golden cases. Parsed from
--sample. - Trace
Event - One event in a trace, tagged by
t— the same shape a live captured stream would emit.
Functions§
- aggregate
- Aggregate per-case results for one model binding into a config report.
- build_
candidate_ yaml - Render one confirmed run as a golden-shaped case block (a single
- …list item), ready to append under a candidates file’scases:. Fails if the result carries no{columns, rows}or the match mode is unrecognized — a candidate that wouldn’t parse as a golden is not worth writing. - candidates_
header - The candidates staging file header — a golden-shaped document so accepted candidates can be moved straight into a golden set. Written once when the file is first created.
- classify
- Classify a pin against a freshly computed MDL SHA.
- compute_
mdl_ sha - Compute a single stable SHA over all MDL files: hash each file’s content (git blob SHA), build a
sorted
<relpath> <sha>manifest, and hash the manifest. Any content or path change moves it. - extract_
result - Extract the
{columns, rows}object from an agent’s free-form final text: prefer a fenced code block, else the whole text; then take the first{… last}and parse it as a table. - extract_
result_ json - Like
extract_resultbut returns the raw{columns, rows}JSON object (kept serializable sowarble eval capturecan embed it verbatim as a golden’sexpected). - extract_
verdict_ json - Extract a
{blocks, verdict, emitted, verified}verdict envelope from an agent’s free-form final text — the +Assertive twin ofextract_result_json. Same fence-stripping and outermost-brace slicing, but the acceptance guard checks forverdictorblocksinstead ofrows, since a verdict envelope carries neither of the table’s fields. Rejects anything else (including a plain{columns,rows}table) so a verdict case never accidentally scores a table-shaped answer. - format_
ablation - Render the per-step ablation as a table: baseline first, then each (step, tier) with Δacc / Δcost, followed by the per-step recommendation.
- format_
compliance - Render a
ComplianceReportin the same reporting voice asformat_gate— a short banner, a line per check, and a final compliant/non-compliant verdict naming exactly what failed. - format_
gate - Render the gate verdict for humans / CI logs.
- format_
pareto - Render the Pareto table (accuracy vs cost vs latency, with per-tag accuracy).
- parse_
context_ version - Split a
context_version(<dataset>@<pin>) into its dataset prefix and pin. With no@, the whole value is the pin and there is no dataset. - project_
verdict_ field - Project one field of a verdict envelope down to the 1x1 scalar
Tablea case’sexpectedcompares against."fresh"readsverdict.fresh;"severity"reads the breach severity off the envelope’sstatusblock (falling back toverdict.severity, in case a future envelope carries it there instead). Fails closed: an absent, wrong-typed, or unrecognized field returnsNone— the case then fails outright rather than silently comparing something else. - rescore
- Re-score a cached
traceagainst a golden case’s current expectation — the 0-LLM path. Dispatches on the case’scrate::ResultKindviascore_valuejust like a fresh run does, so a Table case’s cached{columns,rows}and a Verdict case’s cached envelope both re-project the same way. ReturnsNoneif the cached result doesn’t have the shape the case’s kind expects (a corrupt entry, or a case whoseresult_kindchanged since the trace was captured; the caller then re-runs the case). This is what makes “only the golden’s expected changed” cost zero LLM calls: the result is reused, only the comparison is redone. - run_
ablation - Run the per-step ablation: baseline (all steps at
base_tier) plus, for each named step, one re-dispatch per swept tier. Streams progress to stderr and returns the aggregated report. - run_
eval - Load the golden file, install the agent, run every case under every model binding, and return
the aggregated report. Progress is streamed to stderr. The agent install is cleaned up on return
(including on error) via the
InstalledAgentsdrop guard. - run_
gate - Gate a candidate report against a baseline. A metric regresses when
current < baseline - tolerance. Configs are matched by theirmodellabel; a case regresses when it passed in the baseline but fails or is absent in the candidate. - score_
compliance - Score
traceagainstir’s declared guardrails for the trace’s component. Pure: no file I/O, no process spawn, no model call — the CLI (run_eval_compliance) does the reading, this function only reasons over already-parsed data, which is what makes it trivially unit-testable. - score_
value - Score a raw result value against a case’s expectation, dispatching on
ResultKindso the fresh-run path (run_case) and the cache re-score path (cache::rescore) project the same way from the same stored shape: aTablevalue forResultKind::Table, a verdict envelope forResultKind::Verdict. ReturnsNonewhenresult_valuedoesn’t have the shape the case’s kind expects — the caller treats that as a hard fail, never a false pass. - select_
cases - Apply
filtertocases: tag-filter first (union over--tags), then--sample. Deterministic and order-preserving so re-runs are comparable. - stamp_
context_ version - Rewrite a golden YAML’s
context_version:line to<dataset>@<sha>, preserving the dataset prefix when the golden already had one (else falling back tofallback_dataset). Line-based so comments and formatting survive. Returns the new document text. - verify_
context - Verify a golden’s
context_versionagainst the bound project’s current MDL SHA.