How we know, phase by phase, whether CodeRadar is getting better or drifting wrong.
- Eval-first. The eval harness (Phase 0.2) is built before the features it measures. Every phase ends with an eval gate; a phase is not done until its gate passes.
- Fixtures encode failure modes. Every failure mode in failure-modes.md that a phase claims to address gets a fixture — a minimal React app fragment reproducing that pattern, plus a golden expected output. The scorecard is therefore a direct readout of "which failure modes do we survive."
- Thresholds ratchet, never loosen. CI stores the current thresholds in
eval/thresholds.json. A PR may raise a threshold; lowering one requires a written justification in the PR body. - Wrong-and-confident is the cardinal sin. Metrics penalize a wrong top-1 more than
an honest
ambiguous. An answer with high confidence that is wrong counts double against accuracy (tracked as the poison rate).
| Layer | Location | Runs | Purpose |
|---|---|---|---|
| Unit tests | packages/*/src/**/*.test.ts (vitest) |
every PR | Parser/query correctness in isolation |
| Fixture evals | eval/fixtures/ + eval/run.ts |
every PR | Failure-mode scorecard against golden outputs |
| End-to-end ticket evals | eval/tickets/ |
every PR from Phase 5 | Real-shaped tickets → context bundle correctness |
| Perf benchmarks | eval/bench/ |
nightly / on-demand | Scan time + memory budget on a large fixture repo |
| Determinism tests | part of eval runner | every PR from Phase 6 | Same input twice → byte-identical output |
eval/
├── fixtures/
│ ├── c1-shared-datatable/ # one fixture per failure-mode ID
│ │ ├── app/ # minimal React source reproducing the pattern
│ │ └── golden.json # expected nodes/edges/attributions
│ ├── a2-i18n-keys/
│ ├── b1-prop-drilled-handler/
│ └── ...
├── tickets/ # Phase 5+: full ticket → bundle cases
│ ├── 001-invoice-total-wrong/
│ │ ├── ticket.json # text, screenshot terms (pre-OCR'd), links
│ │ └── golden.bundle.json # expected components, sources, blast radius
│ └── ...
├── bench/ # generated large repo for perf runs
├── thresholds.json # current CI gate values
└── run.ts # runner: scan fixtures, diff vs golden, emit scorecard
golden.json format (per fixture; full contract in eval/src/golden.ts):
{
"failureMode": "C1",
"app": "./app",
"expect": {
"components": [{ "name": "DataTable", "instances": 2 }],
"attributions": [
{ "component": "DataTable", "instanceAt": "pages/UsersPage.tsx",
"endpoints": ["/api/users"],
"expectedFail": "phase-2: per-instance attribution requires prop-flow (step 2.2)" }
],
"forbidden": [
{ "component": "DataTable", "instanceAt": "pages/UsersPage.tsx",
"endpoint": "/api/invoices",
"note": "poison: invoices API attributed to the users-page table" }
],
"queries": [
{ "terms": ["Save"], "status": "ambiguous" },
{ "terms": ["All Users"], "status": "ok", "top": "UsersPage" }
]
}
}forbiddenentries catch the specific wrong answer each failure mode produces — passing isn't just finding the right edges, it's not emitting the poisonous ones. Forbidden checks never carryexpectedFail: poison gates in every phase.expectedFail(per check) gives xfail semantics: a failing check reportsxfailand doesn't gate; a passing check still carrying the marker reportsunexpected-passand does gate — stale markers are removed the moment the capability lands, so capability arrival is always an explicit, reviewed event.
| Metric | Definition | Used from |
|---|---|---|
| Lineage precision | attributed data sources that are correct / all attributed | Phase 1 |
| Lineage recall | golden data sources found / all golden | Phase 1 |
| Instance attribution accuracy | fixtures where per-instance API attribution is exactly right | Phase 2 |
| Journey edge recall | golden journey edges recovered / all golden | Phase 3 |
| Match accuracy@1 / @3 | correct component is top-1 / in top-3 | Phase 4 |
| Poison rate | wrong answers delivered with confidence ≥ high / all answers (lower is better) | Phase 4 |
| Ambiguity honesty | genuinely ambiguous fixtures answered ambiguous (not a wrong top-1) |
Phase 4 |
| OOD rejection | out-of-domain tickets correctly declined | Phase 5 |
| Bundle correctness | ticket evals where the bundle contains all golden sections and respects the token budget | Phase 5 |
| Scan perf | full-scan wall time + peak RSS on the bench repo | Phase 6 |
| Determinism | byte-identical scorecard across two runs | Phase 6 |
A phase is complete when pnpm eval passes these thresholds (initial values —
ratchet upward as reality informs them):
| Gate | Requirement |
|---|---|
| Gate 0 | Eval harness runs in CI; scorecard.json emitted; demo-app fixture green; schema round-trips (scan → JSON → load → identical) |
| Gate 1 | Lineage precision ≥ 0.90, recall ≥ 0.80 across fixtures C2, C3, C5, A2, A7, A8, D4; zero forbidden hits |
| Gate 2 | Instance attribution accuracy = 1.0 on C1 fixtures (the headline case); B1 handler resolution ≥ 0.85 on prop-drilling fixtures ≤ 4 levels; C6 store-writer linking green |
| Gate 3 | Journey edge recall ≥ 0.80 on B3, B4, B8 fixtures; lazy path expansion returns correct 3-level golden paths with cycles present (B6) and terminates < 1 s |
| Gate 4 | Match accuracy@1 ≥ 0.75, @3 ≥ 0.90 on the matching eval set (incl. noisy-OCR-term fixtures A10); poison rate ≤ 0.05; ambiguity honesty ≥ 0.90 |
| Gate 5 | Bundle correctness ≥ 0.80 on ticket evals; OOD rejection ≥ 0.95; every bundle ≤ token budget |
| Gate 6 | Bench repo (≥ 2,000 files): full scan < 5 min, peak RSS < 4 GB; incremental re-scan of 10 changed files < 15 s; determinism green |
pnpm eval -- --recordappends the run's summary toeval/history.jsonl(recorded deliberately — e.g. at each step completion — and committed). A shrinking metric between phases is a regression investigation, not noise.- From Phase 4, the corrections store (G4) is periodically folded into
eval/tickets/— real mismatches from the pipeline become permanent eval cases. This is the flywheel: production errors can only happen once. - The scorecard is grouped by failure-mode category (A–G), so "are we going the right way" is answered per requirement: matching (A), journeys (B), attribution (C), and pipeline behavior (D–G) each have their own trend line.