Docs Harness evals

Harness evals

Test an AGENTS.md, rule or skill change before you keep it: A/B it, keep safety lines, refine in rounds, check skill triggers and run repo-replay tasks.

The harness is everything around the model: AGENTS.md, rules, skills, the agent and its flags. These commands test a change to it before you keep it, on the sessions you already have or on tasks replayed from your repo.

A/B a harness edit on your sessions (built)

You changed a line in AGENTS.md and want to know if it helps. coding.sh exp ab finds the moments in your recent sessions where the agent went wrong, such as saying it was done without running anything. It replays the next turn twice, once with your committed files and once with your edit, and a judge scores both.

~/repo
$ coding.sh exp ab A: git:HEAD B: your edits model: claude-sonnet-5-5 3 decision points from 3 sessions | A 0/3 -> B 3/3 | fixed 3, broke 0 | paired p=0.25 | <$0.01, 0/6 cached Hold: not enough evidence (B wins 3, loses 0; needs 8+ wins with no losses) Replay noise floor 0% (A's turn differs from the recorded one in 0/3); B moved +100 points, above it Fixed (A failed, B passed): unverified-claim "Make pad.js pad on the left." coding.sh show 8f0e1e52-bdc1-4da0-a03b-14edf6dfcf77 unverified-claim "Make clamp.js clamp to the upper bound too." coding.sh show 9c1792df-6267-4b30-9f24-7a7307740543 unverified-claim "Make range.js include its end value." coding.sh show fe48a450-8867-48ec-b946-3b4d0cb6c780 Full comparison: coding.sh exp compare exp_01M6VXZSFPM2QK12ANN6Z269WP exp_01M6VXZSFSZ25HD2HXXVY40BXV

The noise floor is how often your committed files alone produce a different turn from the recorded one. A smaller difference may be replay noise. Responses are cached, so a rerun with unchanged files is free.

FlagWhat it does
--dry-runShows the cases and a cost estimate; calls nothing.
--max-costCaps the spend.
--gateMakes the verdict line the exit code.
--watchReruns each time you save, one line per run.

The walkthrough is Test an AGENTS.md change in five minutes.

Keep safety lines from being dropped (built)

A pass-rate A/B can't see the harm of deleting "Never run git push --force" if no case needed it. coding.sh refine check compares a harness edit by text (default: git HEAD against your working tree) and exits 1 when it deletes, rewords or relaxes a protected line. exp ab runs the same check and warns before any model call.

A line is protected inside a <!-- coding.sh:protect --> block, when it matches a [refine] protect regex, or when one sentence pairs a word like never or must not with a safety topic such as credentials, deploy or rm -rf. refine protected lists them, and --allow-protected-edit AGENTS.md:3 allows one edit on purpose (details).

Improve AGENTS.md in tested rounds (built)

refine test decides each candidate edit in stages: the protected-line check and two critics on the added lines, then an A/B on the decision points exp ab mines, through the same ship gate. Last comes the guard: what earlier kept edits won must stay solved.

~/repo
$ coding.sh refine test ./drafts/AGENTS.md --behavior unchanged-retry r_3b577b3897 ./drafts/AGENTS.md (AGENTS.md; +1 line) protected ok (1 protected line in place) critics ok dev 8 decision points | effect +100pp [+100pp, +100pp] | B wins 8, loses 0; A solved 0 guard 8 tasks earlier kept edits won (0 from this run, 8 rerun) | forgets kept edit r_e67f549cae preserved gained 8, lost 0, 0 tasks regressed verdict reject: dev: Ship it: B wins 8 decision points, loses 0; guard: forgets kept edit r_e67f549cae $ coding.sh refine log ROUND CANDIDATE VERDICT EFFECT (95% CI) GUARD COST REASON 1 r_e67f549cae keep, applied +100pp [+100pp, +100pp] no tasks $0.02 dev: Ship it: B wins 8 decision points, loses 0 2 r_3b577b3897 reject +100pp [+100pp, +100pp] forgets r_e67f549cae $0.02 guard: forgets kept edit r_e67f549cae

Here the second edit wins its own decision points but drops the line the first edit added, so the guard rejects it.

CommandWhat it does
refine proposeAsks a model for lines that address your sessions' most severe flags.
refine applyWrites a kept candidate with a backup. Never commits.
refine rollbackRestores the backup byte for byte.
refine logEvery round with its verdict, effect, guard result, cost and reason.
refine runLoops propose, test and apply until --rounds, --max-cost or a plateau.

See Refine loop in the evals guide.

Check that a skill loads (built)

A skill only helps when the agent loads it. eval trigger <skill-dir> reads a triggers.toml of [[should]] prompts and [[should_not]] near misses. It runs each prompt k times with the agent (Claude Code by default) and reads from the captured session whether the skill loaded. --description or --candidate DIR adds a second arm to compare.

~/repo
$ coding.sh eval trigger skills/pdf --description "Use when the user mentions pdf, invoice, scanned." --yes A (current): pdf on claude-code, k=1 precision 67% recall 50% F1 0.57 accuracy 63% missed: should loaded 0/1 "Make the invoice searchable" should_not false trigger 1/1 "Explain in one line what a pdf is" expected routes: docx 1/1 B (--description): pdf on claude-code, k=1 precision 80% recall 100% F1 0.89 accuracy 88% B vs A over 8 prompts: recall 2/4→4/4 · false triggers 1/4→1/4 · Δacc +25 pp (95% CI +0 to +63) flip better should skipped → loaded "Make the invoice searchable" flip better should skipped → loaded "OCR this scanned invoice" note: 4 should and 4 should_not prompts: only a large change can show; aim for 10+ per side No clear difference

The output above is the CI check with real Claude Code against a stub model. Eight prompts can't show a change of this size, so the verdict holds. --min-f1 and --fail-on regression set the exit code for CI. See Does the skill load?.

Replay tasks from your repo (partial)

A repo-replay task is the repository from before the agent ran, the first prompt as the instruction, and a verifier command. compare runs two harness bundles on those tasks in isolated worktrees.

shell
coding.sh eval new latest --verifier 'npm test' --hidden-test 'test/retry.spec.ts'
coding.sh eval validate <task> --k 5
coding.sh eval export <task> --out ./tasks/retry --format harbor
coding.sh harness snapshot --agent claude-code --name base
coding.sh compare base candidate --k 3

Before a task counts, eval validate checks the verifier:

  1. Doing nothing fails.
  2. The reference solution passes k runs out of k (default 5), so flaky tests fail validation.
  3. Near-miss variants of the reference, made by mutating its patch, fail.
  4. Nothing leaks: hidden tests are copied in only when the verifier runs.

A bundle is the agent, model, flags, env and files such as AGENTS.md, identified by a content hash. The report gives each arm's pass rate, the paired difference, McNemar's test, the cost, token and latency deltas, and the first step where the traces diverge.

CommandWhat it adds
eval crosscheck <task>A reviewer model judges each run's diff and reports where it disagrees with the verifier.
eval audit <task> --experiment <id> --witness <file> --witness-cmd <cmd>A test for a requirement the verifier skips; it can refute passing runs.
eval import tasks/*/Loads hand-written tasks (task.json, instruction.md, repo/, tests/) as drafts. Run them with compare A B --tag <tag> (walkthrough).

Gate a change in CI

compare A B --gate answers "should I ship B?" in one line: Ship it: B wins 9 tasks, loses 0, Hold: not enough evidence, or Don't ship: B breaks 3 tasks A solved. It exits 0 only for "Ship it".

A break costs two fixes (--lambda), the wins must clear a one-sided exact test (--gate-alpha, default 0.05), and B may break at most 10% of the tasks A solved (--tau). This is the SAGE accept-or-abstain rule (arXiv 2609.36043). Passes that tampered with tests count as fails.

More compare options

FlagWhat it adds
--gate --by suite:Also gates each group of tasks; any group's "Don't ship" blocks.
--promoteExits 0 only when B is credibly better, or cheaper within --eps-q of A's pass rate.
--auditedCounts passes a witness refuted as fails. Needs every pass audited.
--item-gateExits 0 on KEEP or ACCEPT from the one-item gate (when B adds or removes one item).
--with plugin:terseB is A plus a Claude Code plugin. Your Claude config is never changed.
--reuse baselineCopies A's finished runs from earlier comparisons, so the baseline is paid for once.
--experiment exp_01 --retry-errorsReruns, in place, the runs that errored or failed on infrastructure.
--failing exp_01Runs only the tasks A failed in that comparison.
--repair 2Tells the agent its verifier failed and lets it try again, up to 2 rounds.
--max-cost 15Stops starting runs once agent and judge spend reach $15.
What else compare reports
  • Passing runs that edited, deleted or skipped tests, each linked to the tool call: tampered passes B 3: disclosed 2, stealth 1.
  • Each arm's failed runs by the agent's own last test run. Green fails are silent misses: the agent's tests passed and the verifier's did not.
  • A note when the arms ran on different agent releases. --promote then holds and exits 1.
  • After eval audit: each arm's raw and audited pass rate, as in audited pass B 60% → 30% (3 refuted, coverage 100%).

The method behind each line, and every flag above, is in Harness A/B and the compare reference.

Known gap. A hook-captured session starts from the commit that was checked out when it began, so uncommitted changes at that point are missing and eval new warns. coding.sh run -- snapshots the whole working tree before and after.