You revised your SKILL.md, ran it once, and it felt better. What is that feeling worth? Zero — unless you have a baseline, machine-checkable assertions, and a run count greater than one.
Anthropic puts "Create evaluations BEFORE writing extensive documentation" in bold at the top of the evaluation section of Skill authoring best practices. Five steps follow, plus a JSON structure — and then a Note: there is not currently a built-in way to run these evaluations; users can create their own evaluation system. That is why the entire section gets skipped.
The Note has fallen behind the tooling. Claude Code's claude plugin eval is now a complete runner with a baseline arm, several grader types, and an HTML report (in early access as of this writing). This issue names all three viable paths, gives the commands and the file layout I actually scaffolded, and then answers the real question: how do you know a change is a genuine improvement rather than the model having a good day?
# Path A: the built-in runner. Scaffold a case skeleton from a plugin/skills root
claude plugin eval init --bare my-first-case
# -> writes evals/my-first-case/prompt.md and evals/my-first-case/graders/criteria.md
# Fill them in, then run the current directory three times with a self-contained report
claude plugin eval . --runs 3 --report ./eval-report.html
# Path B: install the official skill-creator and let it drive the whole loop
/plugin marketplace add anthropics/claude-plugins-official
/plugin install skill-creator@claude-plugins-official
/reload-plugins
# then just say: evaluate my summarize-changes skill with skill-creator
# Path C: the fallback. Turn the skill off in .claude/settings.local.json, rerun in a fresh session
{ "skillOverrides": { "summarize-changes": "off" } }
# Five-second checkup before any of this: did the frontmatter parse at all?
claude plugin validate .claude/skills
What init --bare emits is smaller than you would guess: prompt.md carries only max_turns and allowed_tools in its frontmatter, graders/criteria.md only type and weight, with a one-line TODO under each. A case is a directory, not a config file: one folder per case, holding a prompt and a graders/ directory.
plugin eval is still in early access. On an account without it, the command parses your flags and then exits, printing a single line — "plugin eval is currently in early access". It runs nothing, and it fails quietly enough that you may not notice. Do not depend on it silently in CI; confirm by hand that it really runs first.--ablation defaults to with-without: whenever a plugin resolves, the runner adds an arm with no plugin at all and reports the score delta. Why must this be the default? Because the model is already strong. Your skill says "write commit messages in conventional-commits form" — the model may well do that with no skill loaded. Without the second arm you would book that point to yourself. skill-creator states the same rule even more bluntly: the with-skill and baseline runs must be spawned "in the same turn". No running the with-skill batch today and backfilling baselines next week.
This is the sharpest piece of the design. Under with-without, graders marked with-only — including tool_used: Skill — count as an indicator that the plugin fired, not as part of the score. The reasoning is plain: the arm without the skill can never fire the skill, so folding that into the delta is a free point you award yourself. Trigger rate and output quality must be measured separately, which is exactly why skill-creator has a distinct description-tuning step: it generates should-trigger and should-not-trigger prompts, measures the hit rate, and proposes description edits.
Put an invalid type in a grader and the CLI lists every legal value: regex | tool_order | tool_used | file_exists | llm | baseline. The first four are pure program checks — free, and they add no variance. The last two are model calls (--judge-model defaults to haiku). That line decides how you design cases: if success can be stated as "produced output.txt", "read before it edited", or "the output contains feat(", do not restate it as a vague "the response should be professional and clear" for an LLM to interpret. It is also precisely the official evaluation principle — more questions with slightly weaker automated grading beats fewer questions graded by hand.
--runs defaults to case.runs ?? 3. A single winning run tells you almost nothing in agent evaluation, because the same prompt can produce wholly different trajectories twice in a row. skill-creator carries this into the report: python -m scripts.aggregate_benchmark produces benchmark.json and benchmark.md giving pass_rate, time and tokens per configuration as mean +/- stddev, plus the delta. Read the stddev before the delta — a difference drowned in the standard deviation is not an improvement.
agents/analyzer.md exists to force an analyst pass over exactly these two diseases: non-discriminating assertions (pass with or without the skill) and high-variance evals (three runs of one configuration scatter wildly, usually a flaky case rather than a flaky skill).The official method is two instances: Claude A helps you write and revise the skill, Claude B has it loaded and does real work, and you carry B's failures back to A. The docs give a concrete case — "I asked B for a regional sales report; it wrote the query but forgot to filter out test accounts, even though the skill mentions that rule" — and A might respond by changing "always" to "MUST", or by reordering sections.
"Fresh session" is a hard requirement, not a flourish: the docs say plainly that context left over from authoring the skill will mask gaps in the written instructions. You think it understood the file; it actually remembers what you said out loud. Both claude plugin eval and skill-creator run cases in subagents for this reason — every case gets a clean context.
claude plugin validate (v2.1.233+) — checks only that the frontmatter YAML parses. It catches a genuinely sneaky failure: the YAML breaks, the skill body still loads, but the metadata is empty, so /skill-name still works by hand while automatic triggering never happens again. Run it before any eval. Five seconds./skill-doctor (v2.1.252+) — measures usage and context cost, not quality. It lists what each skill costs in context and how often it was invoked, and flags the ones never invoked at all. In an interactive session the report opens in the /plugin manager's Stats tab; in non-interactive mode with -p it prints as text. Where it will not run: over a Remote Control connection it answers "Skill usage reports are not available on this connection." Run it in the terminal on the machine hosting the session.skillOverrides — four visibility states (on / name-only / user-invocable-only / off) in .claude/settings.local.json; the /skills menu writes it for you (Space cycles, Esc saves). Use it as a poor man's ablation switch. Boundary: it has no effect on plugin skills — those go through /plugin./skill-doctor as a quality evaluation. A skill that was "never invoked" is usually not useless — its description never survived selection. That is a triggering problem: fix the description and run the should-trigger set, do not rewrite the body. Conversely, a skill that fires every time and makes things worse every time looks perfectly healthy in /skill-doctor.llm and baseline graders each add a model call on top. claude plugin eval offers --max-cost-usd as a hard ceiling; on a breach it aborts and reports partial results (exit 2), with the overrun bounded to a single agent run — and for that run the paid graders are skipped while the free ones still score it. Use it on your first run.--threshold (default 1.0, exit 1 below it) and --json are obviously built for pipelines — but the command exits immediately on an account without access. Confirm it runs under the CI identity before you wire a gate to it.npm install -g promptfoo, start with promptfoo init --example getting-started; it supports LLM-as-judge assertions and is built around CI integration. When not to use it: it knows nothing about Claude Code's skill triggering. It can score "is this output good", never "should this skill have fired".pip install inspect-ai; it ships model-graded scorers and over 200 prebuilt evaluations. When not to use it: it is research-grade infrastructure for comparing model capability. Pointing it at one SKILL.md is artillery against a mosquito.skillOverrides, compared by eye. When that is enough: the skill is yours alone, the change was pure wording, or you only want to know whether it fires.Do these three first: (1) claude plugin validate .claude/skills — five seconds to confirm the frontmatter did not silently break; (2) claude plugin eval init --bare <name> for the skeleton, push as much of your success criteria as possible into the free graders regex / file_exists / tool_used, then claude plugin eval . --runs 3 --max-cost-usd <your ceiling>; (3) two weeks later run /skill-doctor and treat the never-invoked ones as description problems.
Pick a path by situation: want a report a human can read, with mean +/- stddev → install skill-creator@claude-plugins-official; want CI and an exit code → claude plugin eval --threshold (confirm early access first); evaluating raw prompt quality rather than skill triggering → promptfoo; just want to know whether it got better → skillOverrides: off and two fresh sessions by hand.
Numbers to keep: --runs defaults to 3 · --ablation defaults to with-without · --threshold defaults to 1.0 · --judge-model defaults to haiku · free graders are regex / tool_order / tool_used / file_exists, paid ones are llm / baseline · cases live in evals/ by default · results land in evals/results/<timestamp>/.
How to read the report: stddev before delta; a delta drowned in the stddev is not an improvement. Any case where both arms score full marks should be deleted and rewritten — it discriminates nothing.
tool_used: Skill). It indicates firing and is excluded from the score delta.skillOverrides states, /skill-doctor (v2.1.252+) and its Remote Control error, claude plugin validate (v2.1.233+)python -m scripts.aggregate_benchmark, grading.json requiring the exact fields text/passed/evidence, and eval-viewer/generate_review.py; the repo shows roughly 175.1k stars / 20.7k forks per its GitHub page, 2026-09agents/grader.md (scoring assertions), agents/comparator.md (blind A/B), agents/analyzer.md (finding non-discriminating and high-variance cases), references/schemas.md (the exact evals.json / grading.json structures), and eval-viewer//plugin install skill-creator@claude-plugins-official fetchesnpm install -g promptfoopip install inspect-ai