Testing & Evaluating Skills

This content was generated with the assistance of AI and should be verified against the official documentation of each tool and framework before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

This page is written against the Claude Code CLI 2.1.283, VS Code 1.139 with the GitHub Copilot Chat extension, and the GitHub Copilot CLI 1.0.88, all re-verified as current as of 2026-09-28. Every ai-catalog example is excerpted from the repository at the pinned commit 58fb709 (main). A SKILL.md is executed literally by an agent, not read with judgment the way a person reads a runbook — Writing Effective Skills covers how to phrase it well, and this page covers how to find out whether it actually works: does it fire on the requests it should and stay silent on the ones it shouldn’t, does it produce the right result when it does fire, and does an edit break something that used to pass.

Trigger-accuracy tests

A skill’s description decides whether Claude reaches for it at all, before a single line of its body is read — so the first thing worth testing is not the skill’s instructions but its triggering, independently of whether the instructions themselves are correct. Anthropic’s own skill-authoring guidance calls this out as the starting point rather than an afterthought: "Create evaluations BEFORE writing extensive documentation. This ensures your Skill solves real problems rather than documenting imagined ones," and prescribes an evaluation-driven loop — identify gaps by running Claude on representative tasks without the skill, build a handful of scenarios that test those gaps, establish a baseline, write the minimal instructions needed to pass, then iterate — see Anthropic — Skill authoring best practices, "Evaluation and iteration". Its documented evaluation record is a small, data-driven JSON object naming the skill under test, the query, any input files, and the behaviour expected in response:

An evaluation record, per Anthropic’s documented shape
{
  "skills": ["iru-check-license"],
  "query": "Make sure every source file in this repo has the right license header",
  "expected_behavior": [
    "Finds the repository's LICENSE file before checking anything",
    "Reads the header format from a file that already has one, rather than assuming a fixed template",
    "Reports which files were missing a header and which were already compliant"
  ]
}

That page, written for Agent Skills on the Claude API, notes that it has no built-in runner for these records and that users can build their own. Claude Code now ships two runners of its own, covered under Trigger accuracy in Claude Code below. The best-practices checklist for a shippable skill closes with a testing section worth pairing with the record above: "At least three evaluations created," "Tested with Haiku, Sonnet, and Opus," and "Tested with real usage scenarios."

For trigger accuracy specifically, an evaluation record only tells you whether the skill produced the right result once invoked; it says nothing about whether the request should have invoked this skill, a different one, or none at all. Build that out as a small should-fire / shouldn’t-fire table alongside the description before trusting it against neighbouring skills:

Prompt Should invoke iru-check-license? Why

"Make sure every file has the right license header"

Yes

Matches the description’s stated scope and invocation form directly.

"/iru-check-license src/main/java"

Yes

Explicit user invocation, scoped to a path — the explicit-invocation-with-an-argument ($ARGUMENTS) case.

"Add an MIT license to this repository"

No

Adding a brand-new LICENSE file is out of the description’s stated scope; a request phrased this way should not silently fall through to a header-checking skill.

"Review this pull request for licensing compliance issues"

No (or ambiguous — worth a real run)

Close enough in wording to be worth an explicit test, not an assumption either way.

To run the table by hand, type each prompt in a fresh session, so leftover context from writing the skill doesn’t mask a weak description, and check which skill, if any, loaded. Both tools also let you force the skill by name, which separates "the instructions work" from "the description triggers":

In Claude Code
# Force the skill directly, bypassing description matching entirely
/iru-check-license src/main/java

# Test model-invoked triggering with a fresh prompt in a new session
Make sure every file has the right license header
In GitHub Copilot (VS Code)
# Skills appear in the / menu by default (user-invocable: true)
/iru-check-license src/main/java

# Test model-invoked triggering with a fresh prompt in a new chat
Make sure every file has the right license header

Trigger accuracy in Claude Code

Claude Code documents two tools that automate the should-fire / shouldn’t-fire comparison, each running a prompt in a fresh session with and without the skill:

  • The skill-creator plugin, for iterating on one skill inside a conversation. Its description-tuning mode generates should-trigger and should-not-trigger prompts, measures the hit rate, and proposes description edits. It stores its cases in evals/evals.json inside the skill directory.

  • claude plugin eval, for a skill packaged in a plugin (see Plugins & Marketplaces). Each case is a prompt.md plus graders. A tool_used grader on the Skill tool passes only when the named skill actually fired.

The two formats aren’t interchangeable. The claude plugin eval files below belong to the illustrative ai-catalog-gates plugin built on Plugins & Marketplaces, under its default evals/ directory.

In Claude Code — the skill-creator plugin
/plugin install skill-creator@claude-plugins-official

# then, in the same session
evaluate my iru-check-license skill with skill-creator
ai-catalog-gates/evals/license-headers/prompt.md
---
max_turns: 10
tags: [license]
allowed_tools: [Read, Glob, Grep, Skill, Edit, Write]
---

Make sure every source file in this repo has the right license header.

Each run starts in an empty working directory with only the read-only tools, so on its own this case has no LICENSE to find, no source file to fix and no tool to fix it with. A case.yaml beside prompt.md seeds the workspace from a script in the case directory:

ai-catalog-gates/evals/license-headers/case.yaml
schema_version: "1.1"
name: license-headers
context:
  scaffold_script: fixture.sh
ai-catalog-gates/evals/license-headers/fixture.sh
#!/usr/bin/env bash
set -euo pipefail
git init -q
curl -fsSL https://www.apache.org/licenses/LICENSE-2.0.txt -o LICENSE
mkdir -p src/main/java/com/example/docsassistant
cat > src/main/java/com/example/docsassistant/DocsController.java <<'JAVA'
package com.example.docsassistant;

public class DocsController {
}
JAVA

The scaffold script runs as you, outside the run’s sandbox, and only when you pass --scaffold, so pass that flag only for suites you or your organization wrote. Edit and Write are outside the read-only set, so listing them in allowed_tools is not enough: grant them with --allow-tools as well. See Claude Code — Seed the workspace or conversation and Claude Code — Grant tools.

ai-catalog-gates/evals/license-headers/graders/skill-fired.md
---
type: tool_used
tool: Skill
input_match: '"skill"\s*:\s*"(?:[\w-]+:)?iru-check-license"'
---

claude plugin eval . --scaffold --allow-tools Edit Write from the plugin root runs each case three times with the plugin and three times without it, and reports the score difference. A tool_used grader on Skill can never pass without the plugin, so in this two-arm run it is reported with scored: false: a pass/fail indicator in the with-arm that does not count toward either score. A Δ near zero with this indicator failing means Claude isn’t choosing the skill on natural phrasing. For a shouldn’t-fire case, set min: 0, max: 0 and arm: both on the same grader, so it is scored in both arms (as of 2026-09-28). Every run is a real model call billed to your plan or API account. See Claude Code — Evaluate and iterate on a skill and Claude Code — Test plugins with evals.

Dry runs in a temp checkout

A skill that scaffolds files, edits a manifest, or runs real tooling (npm install, a test runner, a linter) cannot be trusted from reading its SKILL.md alone — the only way to know its embedded commands and templates actually work against real tooling is to run them. Doing that against the repository the skill lives in risks leaving behind generated files, partial edits, or state a later git status has to untangle by hand. The pinned ai-catalog repository’s own convention is to scaffold and run everything in a throwaway directory outside the repository, keyed off $TMPDIR, so a verification pass can never touch the skill’s own source tree:

From Creating and updating skills and agents, "The fastest reliable way to draft one"
... **Iterate by running it**, not just by reading it. Invoke the draft skill/agent against a real, low-stakes
   case and fix whatever it gets wrong or asks about that it shouldn't have to — a skill's instructions are
   only as good as what actually happens when an agent follows them literally. For a skill that embeds file
   templates (a scaffold, a CI workflow, a `.gitignore`), "running it" means exercising those templates
   against the real tooling in a throwaway directory *outside* the repository
   (`$TMPDIR/iru-verify/<stack>/<skill>/`), folding whatever quirks surface back into the `SKILL.md`, never
   deleting anything inside the repository to do it, and reporting whatever couldn't be verified locally
   rather than assuming it works ...

Extending the skills and agents catalog restates the same rule for the catalog’s own setup and CI-workflow skills as one of its recurring conventions — "Throwaway-directory verification — exercise every scaffold/workflows/gate template against the real tooling in a directory outside this repository ($TMPDIR/iru-verify/<stack>/<skill>/), fold whatever quirks surface back into the SKILL.md, never delete anything inside this repository to do it, and report whatever couldn’t be verified locally instead of assuming it works." Generalised, the pattern has four parts regardless of which tool runs the skill: a scratch directory under the OS temp path ($TMPDIR, or %TEMP% on Windows) that lives entirely outside the repository under test; running the skill’s actual embedded commands and templates there, not a paraphrase of them; folding back into the skill whatever quirk the real tooling surfaces that the instructions did not anticipate (a flag that moved, an output path that differs from what was assumed); and reporting — in the skill’s own description, a verification note, or a PR — exactly what could and could not be exercised locally, instead of asserting untested behaviour works.

Golden-output checks

Trigger accuracy answers "did the right skill run"; a golden-output check answers "did it produce the right result" — comparing the actual output of a real run against a known-good reference instead of eyeballing it. Anthropic’s documented evaluation record already carries the raw material for this: its expected_behavior array is a checklist to verify against the transcript of an actual run, not just a description of intent. Where a skill’s output is itself a file rather than free text — a generated CHANGELOG.md entry, a scaffolded build.yml, a rewritten license header — the more exacting version of the same check is a literal golden file: a known-correct example of that output, checked into the skill’s own test fixtures or a throwaway verification directory, that a real run’s output is diffed against. A mismatch either means the skill regressed or the golden file itself needs updating because the correct output genuinely changed — and a reviewer should be able to tell which from the diff.

A golden-file check from a throwaway checkout
$ cd "$TMPDIR/iru-verify/java/iru-check-license"
$ # ... run /iru-check-license here, then:
$ diff -u "$OLDPWD/fixtures/iru-check-license/Foo.java.golden" src/main/java/Foo.java

For a skill packaged in a plugin, claude plugin eval graders cover the same ground without a hand-written script: regex checks the reply or a file for a pattern (or, with match: not_contains, its absence), file_exists checks that the run created a file matching a glob, and baseline has a judge compare the run against a reference transcript:

ai-catalog-gates/evals/license-headers/graders/header-present.md
---
type: regex
pattern: 'Licensed under the Apache License, Version 2\.0'
target: { source: file, path: src/main/java/com/example/docsassistant/DocsController.java }
---

A regex grader reads Claude’s final reply unless target says otherwise, so this one points at the contents of the scaffolded file. See Claude Code — Test plugins with evals ("Grader types" and "What a grader can look at").

Regression suites when editing skills

A skill in active use accumulates a body of prompts that once triggered it correctly and outputs that were once right; editing its description or its steps risks breaking either without anyone noticing until it happens in production. Anthropic’s documented iteration workflow makes this explicit by splitting the two Claude instances that catch this kind of drift: "Claude A" drafts and refines the skill, while a fresh "Claude B" — with the skill freshly loaded, no memory of the design discussion — runs it against real tasks and reveals what changed. The prescribed loop after any edit is: "Use the Skill in real workflows …​ Observe Claude B’s behavior …​ Return to Claude A for improvements …​ Apply and test changes …​ Repeat based on usage." In practice this means re-running the should-fire / shouldn’t-fire table above and any golden-output checks after every edit to a skill’s description or steps, not only when the skill is first written — a description edited to fix one weak trigger can just as easily narrow or widen what else it now matches, and a step reworded for clarity can silently change what the skill actually does. Keep a small, named set of representative prompts/fixtures for a skill precisely so re-running them after an edit is mechanical rather than something a reviewer has to reconstruct from memory each time.

Re-running the suite after editing a skill
# a plugin-packaged skill: rerun every case, or only the ones tagged for this skill
claude plugin eval . --scaffold --allow-tools Edit Write
claude plugin eval . --tag license --scaffold --allow-tools Edit Write

# a standalone skill: ask skill-creator to compare the edited version with the previous one
compare the new version of my iru-check-license skill with the previous one using skill-creator

claude plugin eval exits non-zero below a score threshold, so the same command can gate a CI job; skill-creator’s version comparison runs a blind A/B between two versions of a skill. See Claude Code — Test plugins with evals and Claude Code — Run evals with skill-creator.

Versioning and a changelog for skills

Neither Claude Code’s skills reference nor the Agent Skills open standard defines a dedicated version key for a SKILL.md the way a package manifest does — the frontmatter reference on Skills covers metadata (a free-form map Claude Code stores but does not act on) and license. The Agent Skills specification’s own metadata example records a version there, as data for your own tooling:

SKILL.md frontmatter, per the Agent Skills specification’s metadata example
---
name: iru-check-license
description: Check for a license file in the repository root and verify every source file carries a matching header.
metadata:
  version: "1.0"
---

See Agent Skills — Specification ("`metadata` field"). A skill shipped in a plugin does get an enforced version: the plugin’s plugin.json version, which pins installed users to that version until it changes (see Plugins & Marketplaces). The Claude API’s own Skills endpoint does version Anthropic-managed skills explicitly ("skill_id": "pptx", "version": "latest", per Anthropic’s Agent Skills quickstart) — but that mechanism belongs to skills uploaded and served through the Skills API, not to a project’s own .claude/skills/ folder read straight off disk. For a project skill, the pinned ai-catalog repository does not version individual skills at all; it versions the repository the skills live in, the same way it versions any other source file:

  • A skill’s edit history is exactly its SKILL.md’s `git log, no different from any other tracked file — git log --follow — .claude/skills/<name>/SKILL.md shows every change, by whom, and why (from the commit message).

  • Every ai-catalog GitHub link on this sub-section’s pages pins a specific commit SHA rather than main for exactly this reason: a skill’s behaviour at the moment an excerpt was written is only guaranteed by the commit it was read at, not by the skill’s name.

  • The repository-level CHANGELOG.md — bootstrapped by the Setup Changelog skill and kept current by iru-release on each tagged release — records what changed release over release across the whole catalog, skills included, in Keep a Changelog format; it is not a per-skill changelog, but a skill-affecting change still lands in it as part of that release’s entry.

Generalised, a project that wants finer-grained tracking than "read the git log" can adopt either or both of two conventions neither tool enforces: a ## Changelog heading inside the SKILL.md body itself (or a sibling CHANGELOG.md in the skill’s own folder) noting what changed and why on each edit, and a repository-wide convention — pinning example links and cross-references at a commit SHA the way this sub-section does — so that documentation referencing a skill’s exact behaviour does not silently drift out of sync with a later edit.

Using the skill-creator workflow

Anthropic’s skill-authoring best-practices guide documents an iterative, two-instance workflow for developing and refining a skill, summarised above under regression suites: one instance ("Claude A") helps design and refine a skill’s instructions, while a second, freshly-loaded instance ("Claude B") tests it against real tasks, with observations fed back to the first. The same page notes that Claude understands the Skill format natively, so asking it to create a skill produces a properly structured SKILL.md without a special system prompt.

Claude Code’s skill-creator plugin automates the testing half of that loop. Install it from the official marketplace, then ask for an evaluation of an existing skill:

In Claude Code
/plugin install skill-creator@claude-plugins-official

# then, in the same session
evaluate my iru-check-license skill with skill-creator

The plugin walks you through writing test cases and then:

  • stores prompts, input files and expected behavior in evals/evals.json inside the skill directory

  • runs each case in its own subagent, so every run starts with a clean context, and records tokens and duration

  • grades each assertion and writes pass or fail with evidence to grading.json

  • aggregates with-skill versus without-skill pass rate, time and tokens into benchmark.json

  • runs a blind A/B between two versions of the skill

  • generates should-trigger and should-not-trigger prompts and proposes description edits

  • opens an HTML review viewer for qualitative feedback that the next iteration reads

See Claude Code — Run evals with skill-creator. A practical order: draft with one instance (grounded in an existing example skill, per Creating and updating skills and agents's "ground it in an existing example" step), write the evaluation cases before extensive instructions, let skill-creator run them in fresh contexts, and iterate.

The LLMOps & Evaluation sub-section under Guides & References / AI covers evaluation at the level of a whole AI product or pipeline — offline eval suites, LLM-as-judge scoring, production monitoring and drift detection — of which the skill-level trigger-accuracy and golden-output checks on this page are one narrow, project-local instance.