agent-regression-evals

$npx mdskill add alirezarezvani/claude-skills/agent-regression-evals

> **Portability:** 3 stdlib scripts, no APIs, no LLM calls. You run the agent; these grade, count and gate.

SKILL.md

.github/skills/agent-regression-evalsView on GitHub ↗
---
name: agent-regression-evals
description: "Use before shipping any agent, prompt, model or tool change, or when a pass-rate drop needs a verdict. Runs repeated trials, gates candidate vs baseline with paired statistics in regression_gate.py, checks tool-call trajectories and finds flaky cases."
argument-hint: "[baseline.jsonl candidate.jsonl | results.jsonl | a question about agent evals]"
license: MIT
metadata:
  version: 1.0.0
  build_pattern: "Statistics-first eval gate: 3 deterministic stdlib scripts (trajectory grading, flake detection, paired regression gate) with blocking exit codes"
  distinct_from: "prompt-governance (versions and registers prompts; this gates a change with statistics); self-eval (grades one session's work); agenthub eval (ranks parallel agents in one session)"
---

# Agent Regression Evals — prove the change did not make it worse

> **Portability:** 3 stdlib scripts, no APIs, no LLM calls. You run the agent; these grade, count and gate.

"Pass rate went from 82% to 79%" is not a finding until you know whether three
points is outside the noise. Agents are non-deterministic, so one trial per
case cannot tell a regression from a coin flip, and a final answer can be right
while the path to it called a tool it never should have.

## Workflow

```bash
# 0 - Run every case k times (k >= 3) on baseline and candidate, from a clean
#     environment per trial. Write one JSONL row per trial:
#     {"case_id": "refund-lookup", "trial": 1, "passed": true, "critical": true,
#      "cost_usd": 0.012, "trajectory": [{"tool": "get_order", "args": {...}}]}
# 1 - Grade the path where the path matters (safety, required lookups).
python scripts/trajectory_checker.py --spec cases.json --results candidate.jsonl
# 2 - Separate flaky cases from stable ones before trusting any comparison.
python scripts/flake_detector.py --results candidate.jsonl --k 3 --quarantine-out q.json
# 3 - Gate: paired per-case comparison against baseline. Exit 4 blocks the merge.
python scripts/regression_gate.py --baseline baseline.jsonl --candidate candidate.jsonl --quarantine q.json
```

Step 1 uses match modes `strict | unordered | subset | superset`. Default to
`superset` (required steps present, harmless exploration allowed); grade the
outcome, not the path, unless order is the requirement. `forbidden` tools block
in every mode.

Step 2 reports **pass@k** (succeeds at least once in k) and **pass^k** (succeeds
all k times). A user meets every trial, so pass^k is the reliability number.
Cases marked critical are never quarantine candidates.

Step 3 averages trials within each case first (trials of one case are
correlated), then tests paired per-case differences with a t-based 95% CI and a
one-sided exact McNemar test. When the drop is real but the sample is too small
to show it, the gate says how many cases it would take.

## The gate blocks on

1. A statistically significant drop (CI upper bound below zero, or McNemar p < 0.05).
2. A critical case that passed every baseline trial and failed any candidate trial.
3. A baseline case missing from the candidate run. Dropping a case is the cheapest fake fix.
4. A quarantine entry with no owner or expiry, or covering a critical case.
5. A forbidden tool call (trajectory checker, exit 4).

It warns, and a human decides, on an underpowered drop above `--tolerance` or a
cost rise above `--max-cost-increase`.

## Hard rules

- **Never compare single trials.** Under 3 trials per case the flake detector refuses (exit 4).
- **Never pool trials as independent samples.** Average per case, then compare cases.
- **Never quarantine without owner and expiry.** An expired entry re-enters the gate automatically.
- **Never delete a failing case to go green.** The gate counts what is missing.
- **Never call "no significant drop" a pass on 8 cases.** Report the power note with the verdict.
- **Start the suite from real failures** (20-50 cases), balanced between "should do" and "should not do".

## Scripts

| Script | Role | Exit codes |
|---|---|---|
| `scripts/trajectory_checker.py` | Grades tool-call paths: match modes, args modes, forbidden tools, call ceilings | 0 · 2 deviations · 3 bad input · 4 forbidden tool |
| `scripts/flake_detector.py` | STABLE_PASS / STABLE_FAIL / FLAKY per case, Wilson CI, pass@k, pass^k, quarantine stubs | 0 · 2 flaky · 3 · 4 too few trials |
| `scripts/regression_gate.py` | Paired CI, exact McNemar, critical cases, dropped cases, quarantine validity, cost | 0 PASS · 2 WARN · 3 · 4 BLOCK |

All support `--sample` and `--output json`.

## References and assets

- [`references/eval_statistics.md`](references/eval_statistics.md) — pass@k vs pass^k, per-case averaging, paired CI, McNemar, power (7 sources)
- [`references/trajectory_grading.md`](references/trajectory_grading.md) — outcome vs path, match modes, grader types (6 sources)
- [`references/flaky_cases_and_quarantine.md`](references/flaky_cases_and_quarantine.md) — flake causes, quarantine with expiry, clean trials (6 sources)
- [`assets/eval_case_spec.example.json`](assets/eval_case_spec.example.json) — trajectory spec for step 1
- [`assets/sample_baseline.jsonl`](assets/sample_baseline.jsonl), [`assets/sample_candidate.jsonl`](assets/sample_candidate.jsonl) — results format
- [`assets/ci_eval_gate.example.yml`](assets/ci_eval_gate.example.yml) — CI job that runs the gate and fails on exit 4

More from alirezarezvani/claude-skills

SkillDescription
a11y-auditUse for any accessibility audit or a11y fix (WCAG 2.2 A/AA, color contrast, compliance report, CI check) in React, Next.js, Vue, Angular, Svelte or HTML. Scans with a11y_scanner.py and checks contrast with contrast_checker.py, then fixes and re-verifies.
ab-test-setupUse before planning or launching any A/B, split or multivariate test, or when asked if a result is significant. Sizes the test with sample_size_calculator.py and writes the hypothesis, metric and stop rule first, so it is not called early on noise.
access-lifecycleUse when preparing or running a user access review, checking that leavers lost their access, finding orphaned or ownerless service accounts, answering an auditor's access-control request for SOC 2, ISO 27001, PCI DSS, HIPAA or NIST 800-53, or checking whether a finished review campaign will hold up. Diffs the HR roster against account exports, tests the campaign for coverage, self-review, unremoved revokes and rubber-stamping, and maps every exception to the controls it breaks.
ad-creativeUse for any ad copy request (RSA headlines, Meta, LinkedIn or TikTok ads, bulk variations), before writing. Checks every line against platform character limits and policy with ad_copy_validator.py. Campaign strategy and budgets: paid-ads.
adversarial-reviewerTough, critical review of a diff or PR before merge, through hostile reviewer personas that catch shared blind spots. Use whenever the user wants a change torn apart, a harsh second opinion, or a self-review felt too agreeable.
aeoUse when content should be cited by ChatGPT, Perplexity, Claude or Gemini (answer engine optimization), before rewriting it. Scores E-E-A-T and structure with aeo_audit.py, rewrites with aeo_optimizer.py and logs citations with citation_tracker.py.
agent-decision-receiptsUse when an autonomous agent takes a side-effecting action (deploy, delete, pay, grant access) that may need proving later, or for EU AI Act Art. 12 record-keeping. Builds a tamper-evident signed receipt with build_action_manifest.py and verifies it.
agent-designerDesign multi-agent systems (supervisor, swarm, pipeline) and tool schemas; find bottlenecks in run logs. Use whenever agents split work or a run is slow or costly. Not for Claude Code workflow files (workflow-builder).
agent-harnessUse when an agent should drive a goal in one of this repo's domains to a verified close, before starting the work. Compiles the goal into a task plan with goal_compiler.py and runs it through loop_controller.py, which verifies each task, caps retries and escalates.
agent-launcher-orchestratorUse when someone wants to build, launch, grade or schedule a Claude Managed Agent in their own Anthropic account ('build me an agent', 'run this nightly'). Reads the session goal, routes with goal_router.py and compiles the loop with loop_compiler.py.