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 - Context
Injection Report - Strict, non-sensitive projection of a dispatched agent’s context identity.
- 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 - Monitor
Metric - Monitor
Observation - Monitor
Report - 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§
- Backend
- Which back-end/runtime a case was (or should be) run against. Value domain matches the
dispatcher/<name>directory names exactly, soclap’s own unknown-value error already lists every back-end this crate knows the name of — but knowing the name is not the same as having an adapter for it; seeresolve_adapter(crate-private) below. - 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_
monitor_ report - 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 - 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_
monitor_ pair - Score the one currently-live freshness scenario. Other injection kinds deliberately remain a
follow-up: only
stopped_updateshas deterministic freshness labels in the manifest. - 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.