Regression testing
Skill Victoriakaey/build-reliable-agents/skills/regression-testing
15 engineering skills for building reliable LLM agents in Claude Code. Extracted from production failure modes — not theoretical best practices.
npx -y skills add Victoriakaey/build-reliable-agents --skill regression-testingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Use when comparing two versions of an LLM system, debugging non-deterministic behavior, or establishing a baseline before making changes. Covers controlled comparison protocol, determinism verification, and bisect protocol for finding which change caused a regression.
SKILL.md
6.1 KB, as published. Nobody here has run it
Regression Testing for LLM Systems
The Core Problem
LLM systems have two sources of variance that look identical from the outside:
- Code/prompt changes — something you did caused the behavior to change
- LLM non-determinism — same code, same prompt, different output due to sampling variance
You cannot distinguish between these without a controlled protocol. Every "fix" that isn't measured is a guess.
Step 0: Establish Determinism First
Before running any comparison, verify your system is as deterministic as possible.
Check these in order:
- Temperature = 0 — is it set in your LLM config?
- Seed parameter — is a fixed seed set? (OpenAI:
seed=42, best-effort but significantly reduces variance) - No shared mutable state — verify no module-level mutation, no shared checkpointer across runs
- No parallel runs — sequential only; parallel runs share API quota and cause cascading rate limit errors
Verify determinism before proceeding: Run the same batch file twice with identical parameters. If outputs differ significantly, you have a determinism problem that must be fixed before any comparison is meaningful.
# Run 1
python terminal_app.py --batch-file test-inputs/core.txt \
--logs-dir logs/determinism-check/run1 --max-critic-rounds 3
# Run 2 (immediately after)
python terminal_app.py --batch-file test-inputs/core.txt \
--logs-dir logs/determinism-check/run2 --max-critic-rounds 3
# Compare outputs manually
If runs differ: do not proceed. Fix determinism first (seed, temperature, state isolation).
Step 1: Define Your Test Suite
A test suite must have:
- Fixed cases — same queries every time, in the same order
- Known expected outputs — what "correct" looks like for each case
- Difficulty labels — easy cases (should always pass) and hard cases (aspirational)
- Minimum size — at least 5-7 cases covering different query types
Easy cases are your regression canaries. If an easy case fails after a change, something is wrong regardless of whether the hard cases improved.
Step 2: Establish a Baseline
Before making any change, run the full test suite and record:
baseline_commit: <hash>
date: <ISO date>
results: [case1: sufficient, case2: sufficient, case3: no_evidence, ...]
pass_rate: N/M
notes: <any known instability>
This is your ground truth. Every subsequent run is compared against this.
Step 3: Controlled Comparison Protocol
When comparing two versions (A vs B), these MUST be identical across both runs:
| Parameter | Must Match |
|---|---|
| Batch file | Same file, same cases, same order |
--max-critic-rounds | Identical — different values = different experiment |
--sleep-seconds | Identical |
--retry-cooldown-seconds | Identical |
| Model | Identical |
| Temperature | Identical |
| Seed | Identical |
| Time of day | Best effort — avoid running during peak API load |
If any parameter differs, the comparison is not valid for causal claims.
Document every run with a _metadata.txt:
commit: <hash> <message>
command: <full command>
batch_file: <path>
max_critic_rounds: <N>
sleep_seconds: <N>
model: <model>
temperature: <N>
seed: <N>
started_at: <ISO timestamp>
results: <per-case outcomes>
pass_rate: N/M
Step 4: Interpreting Results
A change is an improvement if:
- Pass rate increased AND
- No previously-passing easy case regressed
A change is neutral if:
- Pass rate unchanged within ±1 case (could be noise)
A change is a regression if:
- Any previously-stable easy case now fails
- Pass rate dropped
A result is inconclusive if:
- Only 1-2 runs available (need ≥3 for statistical signal)
- Parameters differed between runs
- Determinism not verified
Step 5: Bisect Protocol (for regressions)
When a regression appears and you don't know which change caused it:
- Identify the last known-good commit (last run where all easy cases passed)
- Identify the first known-bad commit (first run where regression appeared)
- List all commits between them that touched LLM behavior (prompts, node logic, routing)
- Binary search: checkout midpoint commit, run stability batch, check result
- Repeat until you find the first bad commit
Key insight: Not all commits affect LLM behavior. Commits that only change display logic, docs, or non-LLM utilities can be skipped in the bisect.
Document the bisect:
last_good: <commit> — <description> — <pass_rate>
first_bad: <commit> — <description> — <pass_rate>
bisect_result: <commit> — <what it changed> — confirmed by <N> runs
Step 6: Statistical Thresholds
For production systems, define minimum acceptable pass rates per case difficulty:
| Difficulty | Minimum pass rate across N runs |
|---|---|
| Easy | ≥ 4/5 runs |
| Medium | ≥ 3/5 runs |
| Hard | ≥ 2/5 runs (aspirational) |
A single run proving nothing is the most common mistake. Budget for multiple runs before claiming improvement.
Cost estimation before running:
N cases × M LLM calls per case × P runs × cost_per_call = total cost
Calculate this before starting any validation cycle. If cost is prohibitive, reduce P (runs) or N (cases) but document the tradeoff.
Known Failure Modes
| Failure | Symptom | Fix |
|---|---|---|
Different max-critic-rounds between runs | Hard case passes with 3 rounds, fails with 1 round — looks like regression | Always match this parameter |
| Entity-specific prompt examples | Parser produces same phrasing as example every run, breaks specific test cases | Remove entity-specific examples from prompts |
| Parallel validation runs | Cascading 429 errors, partial runs, misleading results | Run sequentially only |
| Comparing across API version updates | Model behavior changes independent of your code | Pin model version, note date of any OpenAI updates |