Runtime Skill Evals With Pi: Measuring What Agent Skills Actually Change

Serban Mihai / 10 July 2026
~4 min read
Skills are context files that change how coding agents behave. But do they actually help? We built a runtime eval harness to find out — and the answer is more interesting than "yes" or "no."
The repo is at github.com/mihaiserban/skills — 29 agent skills for engineering workflows, design translation, git operations, and research. Each skill is a SKILL.md file with frontmatter and instructions that an agent loads on-demand.
The Problem
Static review tells you a skill is well-written. It doesn't tell you whether the skill changes outcomes.
A skill that says "always reproduce the bug before fixing it" is good advice. But if the model already does that by default, the skill adds tokens without changing behavior. You need a runtime test: run the same task with and without the skill, grade the outputs, and measure the delta.
This is what Anthropic's skill-creator does — spawn subagents with and without the skill, compare results. We wanted the same thing, but using the pi coding agent as our harness.
The Harness
The eval pipeline has three stages, all scriptable and CI-friendly:
eval-runner.py → runs each eval with and without the skill (parallel)
eval-grade.py → grades outputs against assertions (batch LLM)
eval-aggregate.py → produces benchmark.json + HTML reviewThe runner uses pi with full skill isolation:
# Without skill: zero skills loaded, no contamination
pi --no-skills --no-extensions -e ~/.pi/agent/extensions/gateway \
--no-context-files --no-session \
--model gateway/planner -p "eval prompt"
# With skill: only the tested skill, nothing else
pi --no-skills --no-extensions -e ~/.pi/agent/extensions/gateway \
--no-context-files --no-session \
--skill /path/to/SKILL.md \
--model gateway/planner -p "eval prompt"The --no-skills flag prevents all skill discovery. Global skill directories (~/.agents/skills/, ~/.pi/agent/skills/) are physically moved during eval runs to guarantee the baseline can't cheat. The --skill <path> flag loads only the tested skill — additive even with --no-skills.
For skills with dependencies (like the design orchestrator that routes to picker → apply → audit), the evals.json declares skill_deps and the runner passes multiple --skill flags.
The Evals
25 skills, 2 evals each, 100 total runs across 8 parallel workers. Each eval has 3-7 assertions that check specific, verifiable outcomes:
{
"skill_name": "kill-dead-code",
"evals": [
{
"id": 1,
"name": "remove-unused-function",
"prompt": "Clean up this module. I think some functions are never called...",
"assertions": [
{"id": "identifies-dead", "text": "Identifies all four dead functions", "type": "quality"},
{"id": "keeps-live", "text": "Keeps the two used exports", "type": "quality"},
{"id": "warns-exports", "text": "Warns unused exports might be public API", "type": "behavior"}
]
}
]
}Grading uses a batch LLM approach: all assertions for one eval variant go to a single gateway/coder call. The grader receives the model output in XML tags (to avoid code-fence collision bugs) and returns numbered PASS/FAIL verdicts.
The Numbers
100 runs, 97 completed, 3 timed out (all with_skill — complex code generation). Here's the full benchmark:
| Skill | With Skill | Without Skill | Delta |
|---|---|---|---|
| governance-fanout | 89% | 11% | +78% |
| show-first | 85% | 15% | +69% |
| design-md-style-audit | 67% | 0% | +67% |
| pr-from-diff | 90% | 40% | +50% |
| design (orchestrator) | 89% | 44% | +44% |
| blog-post | 100% | 60% | +40% |
| context-budget | 88% | 50% | +38% |
| systematic-debugging | 83% | 50% | +33% |
| revert-surgical | 100% | 78% | +22% |
| changelog-from-diff | 100% | 80% | +20% |
| input-validation | 100% | 80% | +20% |
| design-md-style-apply | 83% | 67% | +17% |
| design-taste-distiller | 50% | 33% | +17% |
| research | 58% | 42% | +17% |
| kill-dead-code | 71% | 57% | +14% |
| decision-record | 100% | 89% | +11% |
| adversarial-verify | 100% | 90% | +10% |
| sql-review | 70% | 60% | +10% |
| secret-scan | 36% | 27% | +9% |
| clean-commits | 100% | 91% | +9% |
| design-md-style-picker | 100% | 100% | 0% |
| bisect-regression | 100% | 100% | 0% |
| contract-test | 90% | 90% | 0% |
| rebase-safely | 80% | 80% | 0% |
| domain-modeling | 11% | 78% | -67% |
20 of 25 skills show positive delta. 4 show no measurable difference. 1 shows a negative delta.
What the Deltas Tell You
Strong positive (+40% to +78%) — these skills teach structured workflows the model can't improvise. governance-fanout (+78%) teaches a plan → delegate → synthesize pattern. show-first (+69%) enforces wireframe-before-code. design-md-style-audit (+67%) provides an audit rubric the model doesn't invent on its own.
Moderate positive (+9% to +20%) — the skill adds a behavior the model sometimes does but not reliably. revert-surgical ensures git bisect reset is mentioned. changelog-from-diff enforces a specific output format.
Zero delta — the model already handles the task well, or the assertions don't capture what the skill changes. contract-test scores 90%/90% — the model writes good contract tests by default. rebase-safely scores 80%/80% — git rebase is well-known territory.
Negative delta (-67%) — domain-modeling makes the model more cautious: it refuses to act without full domain context, which is the skill's intent but doesn't score well on synthetic prompts that provide the context upfront. The skill is correct; the eval is wrong.
Running the Benchmark
# Full pipeline: run → grade → aggregate, all skills, 8 parallel workers
bash scripts/start-evals.sh --all --parallel 8
# Or step by step
python3 scripts/eval-runner.py --all --parallel 8
python3 scripts/eval-grade.py --all --parallel 8 --model gateway/coder
python3 scripts/eval-aggregate.py --all --output htmlEach skill gets eval-results/iteration-1/ with benchmark.json, benchmark.md, and a review.html showing per-eval outputs and assertion pass/fail.
The runner handles skill dependencies — skills that reference other skills (like the design orchestrator routing to picker → apply → audit) declare skill_deps in their evals.json, and the runner loads them via multiple --skill flags.
Bottom line
Agent skills are software. They need tests. A 100-run benchmark takes ~15 minutes with 8 parallel workers and tells you exactly which skills earn their context budget and which are dead weight.
The full skill pack — 29 skills, eval harness, and all evals — is at github.com/mihaiserban/skills. Clone it, run bash scripts/start-evals.sh --all, and see what your skills actually do.