Appearance
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 renderedreport.html(+ aRESULTS.mdsummary) sit in the skill'sevals/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 3× — 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.
- Links row — flag any reference form (
- 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 — andactions [{label, detail}]; always say what to do next, even "nothing to fix". Technical layer undertechnical: 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]}}— powers the trend line, the changes-since-last-run diff, and per-trial spread.report.html—node .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 withSendUserFile(render). A shortRESULTS.mdprose 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.