Files
santifer--career-ops/evals
wehub-resource-sync d083df1fdb
CodeQL Analysis / Analyze (javascript-typescript) (push) Failing after 2s
Web CI / web typecheck + build (push) Failing after 1s
Release Please / release-please (push) Failing after 1s
CodeQL Analysis / Analyze (go) (push) Failing after 16s
chore: import upstream snapshot with attribution
2026-07-13 12:02:43 +08:00
..

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 (15) 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.24.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)

{
  "id": "ai-platform-llmops",
  "synthetic": true,
  "jd": "<full synthetic job description text>",
  "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/<case-id>__<model>.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 (<case>__deepseek-deepseek-chat.txt), so a fixture never lands in a phantom subdirectory.

Running

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.24.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.