# Golden-set eval for cheap-model routing (#1354) > **Status: v1.** The *mechanism* (`eval-golden.mjs`) is design-invariant and runs > today. Reference labels are now frozen (10 synthetic cases — see **Labeling > methodology** below); the gate threshold and per-model cost remain tunable > constants, and wiring into CI is still deferred (see **Open design questions**). ## What this is A small labeled golden-set plus a harness that measures how well a *candidate* cheap model agrees with reference labels, so "is model X good enough to route to?" becomes a number instead of a hunch. It reuses the `---SCORE_SUMMARY---` contract that every `*-eval.mjs` already emits (`SCORE` + `ARCHETYPE`), so there is no new scoring surface. ## Labeling methodology (v1) The metric is **agreement-with-reference, not absolute correctness.** For #1354's purpose — *which cheap model can hold which task* — distance-to-reference is exactly the right measure: if a candidate model reproduces the reference verdict, it is safe to route that task to it. **v1 labels are the frozen verdict of a reference (Claude-tier / premium) evaluation** applied to each synthetic JD under the repo's own rubric (`modes/_shared.md` § Scoring System + § Archetype Detection): - **`archetype`** is the gate. It is a property of the JD alone (the 6-archetype keyword taxonomy in `_shared.md`), so it is profile-independent and reproducible. - **`score`** (1–5) is the reference model's fit verdict. It is profile-relative by construction, but because both reference and candidate are scored under the *same* conditions, distance-to-reference stays meaningful regardless of whose profile drives it. It is a secondary, tolerance-banded signal — it does **not** gate. The 10 cases cover all 6 archetypes and deliberately favor **edge archetypes over easy wins**: four are hybrids/ambiguous (`platform-agentic-hybrid`, `pm-architect-ambiguous`, `forward-deployed-vs-architect`, `transformation-vs-pm`) where a cheaper model is most likely to diverge, and scores span 3.2–4.3 so the tolerance band has real range to catch drift on red-flag postings. **Future upgrade path:** hand-curated labels. Freezing the reference verdict is the cheap, reproducible v1; a later pass can replace or reconcile individual labels with human-graded ground truth (flip `provenance` to `hand-curated`) without changing the harness. ## Layout ``` evals/ golden/ labeled cases — one JSON per case (synthetic JDs, no user data) fixtures/ recorded candidate outputs for $0 deterministic replay in CI README.md this file eval-golden.mjs the harness (root level, sibling to openai-eval.mjs) ``` ### Golden case format (`evals/golden/*.json`) ```json { "id": "ai-platform-llmops", "synthetic": true, "jd": "", "label": { "archetype": "AI Platform / LLMOps", "score": 4.2, "provenance": "reference-frozen-v1" } } ``` `provenance` records how the label was set (`reference-frozen-v1` today; future hand-curated labels flip it). Edge cases may also carry an `edge_note` explaining why the reference resolved an ambiguous JD the way it did. Both are advisory — the harness only requires `archetype` (string) and `score` (number). All JDs are **synthetic** so the set stays clear of the `no-user-data` guard. ### Fixture format (`evals/fixtures/__.txt`) A recorded candidate-model output containing a `---SCORE_SUMMARY---` block. Only that block is parsed; surrounding prose is illustrative and trimmed. Slash-form provider ids (`deepseek/deepseek-chat`) are flattened to a path-safe token for the filename (`__deepseek-deepseek-chat.txt`), so a fixture never lands in a phantom subdirectory. ## Running ```bash npm run eval:golden -- --replay --model cheap-stub # offline, deterministic, $0 npm run eval:golden -- --live --model gpt-4o-mini # real call via openai-eval.mjs (needs key + cv.md) ``` Replay is the CI-friendly path: no API keys, no `cv.md`, fully deterministic. The harness reports per-case archetype/score agreement, mean |Δscore|, median latency (live only), and a placeholder $/run, then exits `0/1` on the archetype agreement gate. ## Open design questions (TODO #1354) Resolved in v1: - **Reference labels** — frozen reference (Claude-tier) verdict; see **Labeling methodology** above. Hand-curation is the documented future upgrade path. - **Set size / spread** — grown from 2 to 10 cases across all 6 archetypes, favoring edge/hybrid archetypes, scores spanning 3.2–4.3. Still tunable (named constants, safe defaults today): | Question | Where it lives | v1 default | |----------|----------------|------------| | `SCORE` agreement: tolerance band width | `SCORE_TOLERANCE` in `eval-golden.mjs` | ±0.5 (band, per distance-to-reference) | | CI gate threshold for archetype agreement | `MIN_ARCHETYPE_AGREEMENT` in `eval-golden.mjs` | 0.8 | | Per-model $/run rates | `COST_PER_RUN_USD` in `eval-golden.mjs` | empty — needs real provider rates | Wiring this into the required CI job (`.github/workflows/test.yml`) is intentionally deferred until the gate threshold is confirmed, so a default value can't make `main` go red. The replay path is deterministic and $0, so it is ready to wire whenever the threshold is signed off.