Scores committed agent-run fixtures against the golden corpus at (file, rule_key) granularity, building on the deterministic coordinator contract (4a). Offline: committed reviewer payloads, no live agent spawn, no LLM, no network (the LLM-in-the-loop grading is the separate 4c tier). - lib/review/gold-scorer.mjs: scoreFindings (precision/recall/f1 at (file,rule_key) granularity, line+severity ignored) + scoreVerdict; pure, with documented vacuous-set conventions. - tests/fixtures/bakeoff-rich/runs/run-perfect.json: committed run that reproduces all 5 seeded gold findings through runContract. - tests/lib/gold-eval.test.mjs: the scoring RUN (precision/recall/f1 = 1.0, verdict == expected_verdict BLOCK, nothing suppressed/skipped). - lib/util/test-census.mjs: third census category (goldEval) — a scoring run is neither behavior coverage nor a doc-pin; honest-count invariant now 3-way. - docs/eval-corpus/README.md: 4b moved from Future hardening to implemented. Suite 809 -> 822 (820/0/2). gold-scorer covers TP+FP+FN+degenerate paths. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BJQYC5vpkJWxndS55vQQZ6
5.2 KiB
Eval corpus — frozen failures for Voyage's own agents
This directory is the home for the golden / frozen-failure corpus that grounds Voyage's self-evaluation (SKAL-1·4a, the eval-foundation tier). It follows the Anthropic "collect 20–50 real cases" practice: every time a Voyage review/coordinator agent misfires on a real task, the case is distilled into a machine-readable record and added here, so the failure can never silently regress.
This corpus is the gold that the offline gold-scored output eval (SKAL-1·4b)
scores against — that eval is now implemented and wired into node --test
(see §Gold-scored output eval below). The
corpus records here are committed fixtures + a schema; the scoring run is the
separate piece that consumes them.
Seed example
The first corpus entry is
tests/fixtures/bakeoff-rich/gold.json
— the 5 brief-traceable seeded findings of the bakeoff-rich JWT-auth fixture.
gold.json is the canonical machine-readable form; the prose table in
tests/fixtures/bakeoff-rich/README.md is illustrative. When they disagree,
gold.json wins.
Record schema (voyage-eval-gold/1)
A corpus file is one JSON object:
{
"schema": "voyage-eval-gold/1",
"source": "<path to the prose/fixture this was distilled from>",
"description": "<one line>",
"expected_verdict": "BLOCK | WARN | ALLOW", // review-coordinator Pass-4 outcome
"findings": [
{
"file": "<repo-relative path in the reviewed diff>",
"line": 0, // integer >= 0; 0 = file-scoped
"rule_key": "<member of lib/review/rule-catalogue.mjs RULE_CATALOGUE>",
"severity": "BLOCKER | MAJOR | MINOR | SUGGESTION",
"owner_reviewer": "conformance | correctness"
// optional eval-extension fields are permitted, e.g.:
// "dual_flaggable": "<a second rule_key the same issue could carry>"
}
]
}
Hard constraints
rule_keymust be a member ofRULE_CATALOGUE(lib/review/rule-catalogue.mjs). The catalogue is the contract; an invented rule_key is a corpus bug.severitymust be one ofSEVERITY_VALUES(BLOCKER,MAJOR,MINOR,SUGGESTION).expected_verdictis the deterministic Pass-4 outcome oflib/review/coordinator-contract.mjs::computeVerdict:BLOCKER ≥ 1 → BLOCK, elseMAJOR ≥ 1 → WARN, elseALLOW.- Finding-level fields (
file,line,rule_key,severity) mirrorFINDING_REQUIRED_FIELDS(lib/review/findings-schema.mjs);owner_reviewerand any extension fields are eval-specific additions (superset).
Adding a new frozen failure
- Distil the misfire into a
voyage-eval-gold/1record (one.jsonfile here, or a new entry in an existing corpus file). - Confirm every
rule_keyis in the catalogue andexpected_verdictmatches the Pass-4 computation. - Add (or extend) a loader test in the
tests/lib/gold-corpus.test.mjsshape so the record's shape + catalogue membership are pinned undernode --test.
Gold-scored output eval (SKAL-1·4b)
The offline scoring run that grades a recorded agent run against this corpus. Offline = committed reviewer payloads, no live agent spawn, no LLM, no network (the LLM-in-the-loop grading is the separate 4c tier).
- Committed runs live under
tests/fixtures/bakeoff-rich/runs/. Each file is the JSON reviewer payloads (one object per reviewer,{ reviewer, findings }) that a recorded run produced.run-perfect.jsonis the regression guard: fed through the coordinator contract it must reproduce every seeded gold finding. - The contract
lib/review/coordinator-contract.mjs::runContract(payloads)turns those payloads into the deterministic coordinator output (4a). - The scorer
lib/review/gold-scorer.mjs:scoreFindings(runFindings, goldFindings)matches at(file, rule_key)granularity (line + severity ignored) →{ tp, fp, fn, precision, recall, f1, matched, missed, spurious }. Vacuous-set conventions (empty run → recall 0; empty gold → precision 0; f1 collapses to 0) are documented in the module header.scoreVerdict(runVerdict, goldVerdict)→ exact verdict match.
- The scoring run
tests/lib/gold-eval.test.mjsassertsrun-perfectreproduces gold at precision/recall/f1 = 1.0 andverdict === expected_verdict. - Third test-census category.
lib/util/test-census.mjsnow reports agoldEvalbucket (matched byGOLD_EVAL_FILE_RE) separately frombehavioranddocPins— a scoring run is neither behavior coverage nor a prose pin, so the honest-count invariant is now a 3-way sum.
Future hardening (not in this tier)
- A prose↔JSON cross-assertion (parse the fixture README table, diff against
gold.json) to mechanically bound the two-source-of-truth drift. - Degraded-run fixtures (a run that misses or invents findings) to exercise the
scorer's discriminating path end-to-end; today that path is covered by
tests/lib/gold-scorer.test.mjswith inline synthetic findings. - SKAL-1·4c: the LLM-in-the-loop eval that grades live agent runs (needs a filesystem + model judgement, deliberately excluded from this deterministic tier).