borghei/agent-harness
> Test and evaluation harness for AI agents — scenario suites, deterministic replay, regression diffing, cost and latency budgets. Use when agent quality is vibe-checked, before shipping a prompt or model change, or when evals drift.
npx skills add https://github.com/borghei/Claude-Skills --skill agent-harness
Most agents ship on vibes: someone tries eight prompts, the output looks good,
it goes to production, and the next prompt tweak silently breaks a refusal
nobody re-tested. This skill builds the harness around an agent so its
behaviour becomes measurable — scenario suites with structural assertions,
deterministic replay of recorded tool calls, paired regression diffing across
prompt and model changes, and per-scenario cost and latency budgets. The tools
here score an agent; they never invoke one, so they run offline on every commit.
Before building the harness, confirm these inputs. If any is unknown or vague, ASK — do not assume:
tool_not_called assertions at critical severity, and what the release gate blocks onStop rule: ask only the 2-3 that most change the output. If the user says "just draft it," proceed and list your assumptions at the top of the artifact.
adversarial, failure-recovery, ambiguity) using
assets/scenario_authoring_checklist.md. Structural assertions first — tool
called / not called / order / arguments — text assertions only on domain tokens.
defaults for latency, cost, and turn ceilings so everyscenario is budgeted without repeating yourself.
the run with model and prompt_sha.
python3 engineering/agent-harness/scripts/scenario_runner.py \
--suite engineering/agent-harness/assets/sample_suite.json \
--transcripts engineering/agent-harness/assets/sample_transcripts_baseline.json \
--strict-critical
as JSON reports.
(re-run the flipped scenario five times to tell the last two apart).
assets/eval_report_template.md and promote theaccepted candidate report to the new baseline.
python3 engineering/agent-harness/scripts/scenario_runner.py \
--suite engineering/agent-harness/assets/sample_suite.json \
--transcripts engineering/agent-harness/assets/sample_transcripts_candidate.json \
--format json > /tmp/candidate.report.json
python3 engineering/agent-harness/scripts/eval_diff.py \
--baseline engineering/agent-harness/assets/sample_baseline_report.json \
--candidate /tmp/candidate.report.json \
--fail-on-regression --drift-threshold 0.15
The shipped sample data demonstrates the core lesson: both runs score 83.3%,
and the candidate contains a critical prompt-injection regression. A gate on
pass rate ships it; the paired diff catches it.
ceiling at observed max + 2. Put them in the suite defaults, overriding
only where a scenario is legitimately expensive.
minor assertions, sothey report without blocking.
bleed that stays inside budget.
python3 engineering/agent-harness/scripts/eval_diff.py \
--baseline engineering/agent-harness/assets/sample_baseline_report.json \
--candidate engineering/agent-harness/assets/sample_candidate_report.json \
--drift-threshold 0.10 --format json
| Need | Use | Durability |
|------|-----|------------|
| The agent must take an action | tool_called, tool_call_order | [PROVEN] Exact; survives rewording |
| The agent must NOT take an action | tool_not_called | [PROVEN] The single highest-value assertion in any agent suite |
| The action must use the right data | tool_arg_equals | [PROVEN] Catches the right tool with wrong arguments |
| Structured output correctness | json_field_equals | [PROVEN] Exact when the agent has a JSON mode |
| A required domain fact appears | output_contains on an ID, number, or policy name | [RECOMMENDED] Stable if you never quote sentences |
| A forbidden phrase must not appear | output_not_contains | [RECOMMENDED] Good for injection and leak checks |
| Tone, helpfulness, faithfulness | Model-graded rubric (outside this harness) | [EXPERIMENTAL] Noisy and drifts with the judge; calibrate against human labels first, and never gate on it alone |
| Severity | Covers | Gate |
|----------|--------|------|
| critical | Safety, money movement, data loss, refusals that must hold | Blocks on a single failure (--strict-critical) |
| major | Task correctness — the user did not get what they asked for | Blocks below the pass-rate floor (--fail-under) |
| minor | Budgets, verbosity, style | Reported; never blocks |
| Discordant scenarios (flipped either way) | Read it as |
|-------------------------------------------|------------|
| 0 | No behavioural change detected at this suite's resolution |
| 1-5 | Read the individual scenarios; the p-value has no power here |
| 6-24 | Exact McNemar p is meaningful; eval_diff.py reports it |
| 25+ | Both the p-value and the aggregate rate movement are informative |
A single critical regression is actionable at n = 1. Significance testing is
for aggregate movement, never for safety failures.
Mistake: The release check is "pass rate ≥ 90%," and everything else is advisory.
Why it happens: One number is easy to put in a dashboard and easy to explain to leadership, and it genuinely looks like the summary statistic.
Instead: Gate on critical-severity failures and on the paired per-scenario diff. The pass rate is the *last* number you read, always with its confidence interval — at 30 scenarios that interval is ±13 points, which cannot resolve the regressions you care about. The sample data here shows two runs at an identical 83.3% where one refunds money on an injected instruction.
Mistake: output_contains: "I've issued your refund of $49.00 and it should arrive in 3-5 business days".
Why it happens: It is the fastest thing to do — copy the good output into the assertion and move on.
Instead: Assert on the tool call (issue_refund with order_id=A-10041) and on a domain token in the text ("refund", the order ID). Structural assertions do not break when the model rewords, so the suite keeps signal across model upgrades instead of generating a wall of false failures that trains the team to ignore it.
Mistake: Every scenario is a happy path; the suite has no tool_not_called assertions.
Why it happens: Suites get written from the product spec, and specs describe intended behaviour, not forbidden behaviour.
Instead: For every irreversible action the agent can take, write a scenario where taking it is wrong. Refusal and adversarial scenarios are where prompt changes actually regress, because a change that makes an agent more capable usually makes it more eager. Target roughly 35% of the suite across refusal and adversarial buckets.
Mistake: Iterating on the prompt with the full suite visible until every scenario passes.
Why it happens: It feels like the tight feedback loop that good engineering is supposed to have.
Instead: Hold out 20% of scenarios and never look at them while iterating; run them only at the gate. Thirty scenarios is a small enough surface to overfit in an afternoon, producing an agent that passes the suite and fails users.
Mistake: Four scenarios flip after a prompt edit, so the team spends two days finding the cause.
Why it happens: Nobody ever ran the identical configuration twice, so run-to-run variance is unmeasured and every flip looks causal.
Instead: Before trusting any diff, score the same configuration twice and diff it against itself. That flip count is your noise floor. Then reduce it — temperature 0 where the product allows, replayed tool results rather than live backends, and re-runs of flipped scenarios to separate flaky from real.
| File | Purpose |
|------|---------|
| scripts/scenario_runner.py | Runs a JSON scenario suite against recorded transcripts; reports pass/fail per assertion with severity, budget checks, and CI exit codes |
| scripts/eval_diff.py | Diffs two runs into regressed/fixed/stable, with Wilson intervals, exact McNemar on discordant pairs, and cost/latency drift |
| references/scenario-and-fixture-design.md | The six scenario buckets, replay modes, fixture recording rules, assertion tiers, suite sizing |
| references/eval-methodology-and-budgets.md | Scoring layers, small-sample statistics, budget setting, CI wiring, methodology anti-patterns |
| assets/sample_suite.json | Six-scenario support-agent suite covering all assertion types |
| assets/sample_transcripts_baseline.json | Recorded baseline run |
| assets/sample_transcripts_candidate.json | Recorded candidate run containing a critical regression at an unchanged pass rate |
| assets/sample_baseline_report.json | Scored baseline report — input for eval_diff.py |
| assets/sample_candidate_report.json | Scored candidate report — input for eval_diff.py |
| assets/eval_report_template.md | Release-decision report template |
| assets/scenario_authoring_checklist.md | Pre-merge checklist for any scenario joining a gating suite |
Take borghei/agent-harness from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.