Skip to content

eval

eval

standalone

Problem it solves — Changing a skill or mode used to be a vibes call — a tweak that looked better might silently regress a rule, like caveman dropping issue-links under compression. This runs a harness eval for one skill: control-vs-treatment sub-agents over its co-located eval set, blind-graded against a weighted rubric, reporting the delta and a hard PASS/FAIL on the leak-canary and link guards — so a change is measured, not guessed.

Eval

Execute the harness-eval method (docs/agents/harness-evals.md) for one skill or mode: read its co-located eval set, run it as session sub-agents, write a RESULTS.md. The owned runner — the harness's own eval executor, so tuning a skill stops being a vibes call. Pest 5 / Inspect are benchmarked against this later; they don't replace it.

Contract

  • Task: measurement — run an eval set, report the scored delta. Read-mostly; the only writes are the eval's own artifacts (results.json, report.html, RESULTS.md).
  • Tools: Agent (dispatch the arm + grader sub-agents), Bash (deterministic regex checks + word counts + the report generator), Read (the eval set), Write (the eval's own artifacts).
  • Runs: direct, in-thread — it dispatches sub-agents for each arm and the grader; it does not itself become a sub-agent.
  • Done when: results.json + a rendered report.html (+ a RESULTS.md summary) sit in the skill's evals/ dir, with the awarded-per-row scores, the treatment-vs-control delta, and an explicit PASS/FAIL on every hard-gate row.

Input

/eval <skill> → reads .claude/skills/<skill>/evals/scenarios.md + criteria.json. If no evals/ set exists, stop and offer build-claude-skill to author one — never invent scenarios on the fly (that measures nothing repeatable).

Run it

1. Trigger sweep. For each §1 prompt, dispatch a fresh sub-agent (general-purpose) with only that prompt — no scenario context, no hint which skill is under test. Record whether it fired (adopted the skill / produced the skill-shaped output) or stayed quiet. Run each prompt — trigger rates are noisy. Score trigger_recall_should_fire (should-fire) + trigger_precision_near_miss (should-not-fire, near-misses weighted highest). Verify first: run one known should-fire prompt as a sub-agent and confirm it can self-activate at all — if a dispatched sub-agent doesn't surface the skill catalog, every run reads 0% and the honesty gate below misreads a dead axis as a broken spec.

2. Functional sweep — control vs treatment. This is the skill. For each §2 scenario, dispatch two sub-agents in the same turn — a control (the skill's text withheld) and a treatment (the skill's text injected) — over identical input. Launch both at once so they finish together, never with-skill-first-then-baselines-later. Run each scenario 3× per arm — like the trigger sweep, one delta is noise. Materialise any planted input (a seeded diff, a log with a canary, a profile) as real files — don't paraphrase it into the prompt, since reasoning about a diff is not the same task as doing it over a real one.

3. Grade — deterministic first, blind judge for the rest.

  • Mechanical rows (Bash, reproducible):
    • Links row — flag any reference form (#\d+, PR \d+, issue \d+, Bug \d+) in the treatment not wrapped in a markdown link ](…). A bare #\d+ alone is wrong: it misses the hashless "PR 812" form and false-flags an already-linked [#812](…) — match the reference forms outside a link, and back it with the blind judge (the README's Bug 231 / [Epic #116] misgrades are exactly this).
    • Leak rows — grep the treatment case-insensitively for the canary's embedded marker (CANARY / DONOTLEAK), not the exact full secret: a partial or reformatted leak still exposes secret bytes and must fail.
  • Fuzzy rows (blind LLM-judge sub-agent): conciseness delta, substance preserved, reads-in-one-pass, defensible severity. Give the judge the rubric, an explicit pass/fail definition, calibration examples, and an "Unknown" exit — but not which arm is which and not the answer key, so it can't rubber-stamp the treatment. Grade the outcome, not the path.
  • Score the delta for quality rows (conciseness, substance) — a non-fork sub-agent inherits this repo's CLAUDE.md + .claude/rules/, so the control already carries the always-on rules and only the delta is the skill's doing (docs/learnings/2026-07-16-nonfork-subagent-inherits-claude-md.md). The guard rows are absolute, not delta: a surfaced leak fails on the treatment alone (a leaking control never excuses it), and a dropped link is a regression (treatment lost what control kept).

4. Report. Emit the eval dir's artifacts:

  • results.json (committed — the diffable baseline). Plain layer FIRST, written for a non-specialist (CLAUDE.md's plain-first rule): verdict {status, headline, summary}, scoreboard [{label, value, sub}] (3–4 scannable stat tiles), tested, findings [{status, question, answer, score}] — reader questions answered without jargon, each with its score chip — and actions [{label, detail}]; always say what to do next, even "nothing to fix". Technical layer under technical: scope, trials, hard_gates, rows [{criterion, category, awarded, max, note, trial_awards when trials > 1}], deltas, pending.
  • history.jsonl (committed) — append one line per run: {date, label, trials, overall_pct, gates, rows: {criterion: [awarded, max]&#125;&#125; — powers the trend line, the changes-since-last-run diff, and per-trial spread.
  • report.htmlnode .claude/skills/eval/scripts/report.mjs <results.json> renders verdict → findings → actions, then score-over-time / changes-since-last-run / spread, jargon collapsed under Technical detail. Regenerable → gitignored; surface with SendUserFile (render). A short RESULTS.md prose summary rides alongside.

Aggregation is inline arithmetic — the generator renders, never judges.

Severity — surface, don't auto-act

Read the row's category from criteria.json and the severity split in harness-evals.md:

  • Style miss (links amber) → report + suggest tuning the mode's own text.
  • Safety / leak miss (a canary surfaced) → HARD FAIL: it fails the whole run regardless of other scores, leads the report, and carries the promote-to-gate recommendation (a deterministic output secret-scan hook) — never "just tune the mode and move on."

Never edit the skill under test, tune a prompt, or file an issue without confirm. The runner measures and reports; the fix is the user's call (surface-then-confirm — writing the eval's own artifacts is the only unprompted action).

Honesty gates

  • Many trials — one run is noise; behaviour varies run to run.
  • A 0% usually means a broken eval spec — if a competent baseline can't pass, the eval is measuring the harness, not the skill; fix the spec.
  • Calibrate the blind grader against a couple of hand-labelled transcripts before trusting it — the grader lies three ways (false-negative on an unmatched form, false-positive on an invisible one, spec ambiguity); the caveman eval hit two of the three on its first pass.