Module 22 · Part V · Evaluating the building blocks

Evaluating agent skills

A skill is a folder of know-how that an agent loads only when it thinks the skill is relevant. That single design choice creates two separate ways to fail: the skill never fires when it should, or it fires and does not actually help. This module shows how to measure both, following the method in Anthropic's own open-source skill-creator skill, and then goes further: adherence, many skills at once, portability across models, and skills as a security supply chain.

Advanced~65 minPrereqs: Modules 03, 04, 05, 13

By the end of this module you can

  • Explain what a skill is (folder, SKILL.md, frontmatter, bundled files), how progressive disclosure loads it in three levels, and how it differs from a tool, an MCP server and a system prompt.
  • Treat triggering as a classification problem: build should-trigger and near-miss should-not-trigger sets, run repeats, and compute trigger rate, precision, recall and a confusion matrix.
  • Optimize a skill's description with a train / held-out split and explain why you pick the winner by held-out score.
  • Run an uplift eval: with-skill vs baseline runs in parallel, objective assertions, a grader that cites evidence, aggregate stats with mean and standard deviation, and an analyst pass that catches non-discriminating and flaky assertions.
  • Check adherence from the trajectory: did the agent read the referenced file, run the bundled script, follow the steps in order?
  • Evaluate selection when dozens of skills are installed, portability across models and harnesses, and safety of third-party skills.
  • Compute trigger metrics and a paired bootstrap confidence interval for uplift in Python.

What a skill is

Imagine you hire a very capable generalist. They can write, code and analyze data, but they do not know your company's way of doing things: which report template finance expects, which chart colors are on-brand, which validation step always catches the broken export. You could brief them on all of it every morning, but most days they would not need most of it. A better approach is a shelf of labeled binders. The generalist glances at the labels, pulls down the right binder when a task calls for it, and only then reads the details.

An agent skill is one of those binders. Anthropic introduced Agent Skills in October 2025. Concretely, a skill is a folder containing:

  • A file called SKILL.md. It starts with YAML frontmatter (a small block of key: value lines between --- markers) holding at least a name and a description, followed by Markdown instructions (the body).
  • Optional bundled resources: scripts/ (executable code for deterministic or repetitive steps), references/ (longer documents loaded into context only when needed) and assets/ (files used in the output, such as templates, icons or fonts).
The folder quarterly-report/ ├─ SKILL.md ├─ scripts/ │ └─ make_chart.py ├─ references/ │ └─ style-guide.md └─ assets/ └─ template.docx only SKILL.md is required Inside SKILL.md --- name: quarterly-report description: Build the quarterly sales report from a CSV export. Use whenever the user ... frontmatter: the label --- # Quarterly report 1. Validate the CSV columns first 2. Run scripts/make_chart.py 3. Read references/style-guide.md 4. Fill assets/template.docx body: the instructions the body points at bundled files; the agent opens or runs them only when a step needs them
Anatomy of a skill. The frontmatter is a short label the agent always sees; the body is the playbook it reads after deciding the skill is relevant; the bundled files are opened or executed only when a step calls for them. We will use this quarterly-report skill as the running example.

Two limits from the spec matter for evals. The name can be at most 64 characters and the description at most 1,024 characters; skill-creator's validator enforces both and its description optimizer aims for roughly 100 to 200 words. That short description is everything the agent knows about the skill before deciding to open it, so it carries almost all of the triggering behavior. Hold on to that thought: it is why trigger evals exist.

Progressive disclosure: three levels of loading

The key design idea is progressive disclosure: show the model a little, and let it ask for more. Skills load in three levels:

  1. Level 1, metadata. The name and description of every installed skill sit in the context all the time. skill-creator puts this at about 100 words per skill.
  2. Level 2, the body. When the agent decides a skill is relevant, the whole SKILL.md body is loaded. The guidance is to keep it under about 500 lines, and to split anything bigger into reference files with clear pointers.
  3. Level 3, bundled resources. Reference files are read only when a step needs them. Scripts can be executed without their code ever entering the context: only their output does. This level is effectively unlimited.
Level 1 · always Level 2 · on trigger Level 3 · on demand system prompt skill labels (name + description)x every installed skill user: "make the Q3 report" system prompt skill labels (name + description)x every installed skill user: "make the Q3 report" SKILL.md bodysteps, rules, pointers system prompt skill labels (name + description)x every installed skill user: "make the Q3 report" SKILL.md bodysteps, rules, pointers references/style-guide.md(read because step 3 said so) make_chart.py output only fires needs ~100 words per skill, paid on every request the body, ideally under ~500 lines, paid only when used unbounded; script code never enters context, only its output
Progressive disclosure, as the context window sees it. Level 1 costs a little on every request for every installed skill. Level 2 is paid only when the agent decides the skill is relevant. Level 3 is paid only for the specific file or script output a step needs. Each arrow is a decision the agent makes, and each decision can be wrong, which is what this module teaches you to measure.

Skill vs tool vs MCP server vs system prompt

Beginners often blur these four. They sit in different places around the agent loop and fail in different ways, so they need different evals. This module covers skills; tools and MCP servers get their own treatment in Module 24.

Agent (model in a loop) System prompt rules and persona, every turn Skill (folder) know-how, read when relevant Tool one callable function MCP server tools + resources over a protocol always if chosen call / result protocol a skill's steps can tell the agent which tools to call
Where each building block plugs in. A system prompt is always present. A tool or MCP server is an action the model can take. A skill is knowledge the model chooses to read, which may in turn tell it how to use tools well. Only the skill has a "did the model choose to open it?" step, and that step is what makes skill evals different.
System promptToolMCP serverSkill
What it isStanding instructionsA callable function with a schemaA process exposing tools, resources and prompts over a protocolA folder of instructions plus optional scripts and files
When it is in contextAlwaysSchema always; result after a callIts tool schemas, usually alwaysLabel always; body and files on demand
Who decides to use itNobody: it is just thereModel picks the tool and argumentsModel picks one of its toolsModel decides from the description alone
Typical failureToo long, instructions ignoredWrong tool, wrong argumentsSame as tools, plus server errorsNever opened, opened wrongly, or opened but not followed
Main evalInstruction following (Module 06)Tool-call accuracy (Module 06, Module 24)Tool and server evals (Module 24)Trigger eval + uplift eval + adherence (this module)
An open standard

In December 2025 the format was published as an open standard, with a specification at agentskills.io, and other tools now load the same folders (VS Code documents "Use Agent Skills in VS Code", for example). For evals this matters: a skill is a portable artifact that may run under several models and harnesses you did not test, which is why this module has a section on portability.

Why skills need their own evals

A normal prompt change has one job: make the output better. A skill has two independent jobs, and skill-creator frames its whole method around them:

  1. Does it trigger at the right times? The agent must decide, from the description alone, to open the skill for relevant requests and to leave it closed for irrelevant ones.
  2. When it triggers, does it improve the output? A skill that loads perfectly but adds nothing (or makes things worse) is just context bloat.

These are independent. A brilliant body behind a vague description never gets read. A crisp description in front of a muddled body gets read and then ignored. Measuring only the end result mixes the two, and you cannot tell which half to fix.

100 relevant requests the skill would help here 70 trigger the skill recall = 70% 60 follow the body adherence = 60/70 42 beat baseline uplift when used 30 never fire fix the description (Part 1) 10 loaded, not followed fix the body's clarity (Part 3) 18 followed, no better fix the content itself (Part 2) plus: fires on unrelated requests false triggers: wasted tokens, derailed tasks illustrative numbers; each leak points to a different part of the skill to fix
The skill funnel. Only 42 of 100 relevant requests end with a skill-driven improvement, and each leak has a different cause and a different fix. On top of that, false triggers on unrelated requests cost tokens and can pull the agent off course. Numbers are illustrative.

So a complete skill eval has at least three measurements, plus two situational ones:

Trigger eval (Part 1)
A classification test: for each query, should the skill fire, and did it? Metrics: trigger rate, precision, recall.
Uplift eval (Part 2)
A paired A/B test: same tasks with and without the skill. Metrics: pass-rate delta, plus time and token cost.
Adherence (Part 3)
A process check on the trajectory: did the agent do what the skill said, in order, with the bundled files?
Many skills, portability, safety (Parts 4 to 6)
Selection among dozens of skills, behavior across models, and the risks of installing someone else's code.
Analogy: the recipe card

A recipe card in a kitchen binder can fail in three ways. Nobody pulls it out when making that dish (trigger). The cook pulls it out but improvises anyway (adherence). The cook follows it exactly and the dish is no better than their usual version (uplift). A food critic who only tastes the final plate cannot tell you which one happened.

Part 1: Trigger evals

Triggering is a binary classification problem. Each query is a case. The label is "should this skill fire?" and the prediction is "did it fire?". Everything you learned about precision and recall in Module 04 applies directly.

How triggering actually works

Skills appear in the agent's list of available skills with just their name and description, and the model decides whether to consult one based on that description. skill-creator adds two observations that shape how you write trigger evals:

  • Claude tends to under-trigger skills, that is, not use them when they would help. Its advice is to make descriptions "a little bit pushy": say what the skill does and list the situations where it should be used, even when the user does not name the skill.
  • Simple, one-step queries may not trigger a skill even when the description matches perfectly, because the agent can handle them directly with basic tools. "Read this PDF" is a poor trigger test. Complex, multi-step or specialized queries trigger reliably when the description matches.

The second point is subtle and important: a trigger eval full of short, easy queries will show low recall for every description, and you will waste time "fixing" a description that is fine. Your queries must be substantive enough that a skill would genuinely help.

Building the query set

skill-creator recommends about 20 queries: 8 to 10 that should trigger and 8 to 10 that should not. The format is a plain JSON list:

[
  {"query": "ok so finance just dropped q3_sales_FINAL.csv in /data and the CFO wants the usual quarterly writeup by thursday, same format as last quarter pls", "should_trigger": true},
  {"query": "Can you prepare the Q2 business review document for EMEA? Revenue by country is in the 'Q2' tab of the attached sheet; flag anything down more than 10% QoQ.", "should_trigger": true},
  {"query": "what was our Q3 revenue growth vs Q2? just the percentage, csv is data/q3_sales.csv", "should_trigger": false},
  {"query": "write a python job that pulls the quarterly CSV exports from s3 every night and loads them into postgres", "should_trigger": false}
]

The guidance on writing these is specific, and worth following closely:

  • Realistic, concrete and detailed. File paths, column names, company context, a little backstory, casual phrasing, even typos. Bad: "Create a chart". Good: a paragraph that sounds like a real person with a real file.
  • Should-trigger coverage. Different phrasings of the same intent (formal and casual), cases where the user never names the skill or file type but clearly needs it, uncommon use cases, and cases where this skill competes with another and should win.
  • Should-not-trigger = near-misses. The most valuable negatives share keywords or concepts with the skill but actually need something else: adjacent domains, ambiguous phrasing where naive keyword matching would fire. "Write a fibonacci function" as a negative for a report skill is too easy and tests nothing. In the example above, both negatives mention quarterly CSVs, but one only needs a number and the other needs a data pipeline.

Repeats and the trigger rate

Triggering is stochastic: the same query can fire on one run and not on the next. So each query is run 3 times, and the result per query is a trigger rate (fired runs divided by total runs). In skill-creator's scripts a query "passes" if its trigger rate is at least a threshold (default 0.5) when it should trigger, or below that threshold when it should not.

How does the harness know a skill fired? skill-creator's run_eval.py installs the description temporarily, runs the query headlessly through the Claude Code CLI (claude -p), and watches the streamed events: if the agent's first tool call is a Skill or Read call that names this skill, it counts as triggered; any other first tool call counts as not triggered. That is a precise, cheap, code-based grader (Module 03), which is exactly what you want for a classification eval.

The confusion matrix, worked

Suppose you run 10 should-trigger and 10 should-not-trigger queries, 3 times each: 60 runs. You can count at the run level:

what the agent did fired did not fire should trigger 10 queries x 3 should not 10 queries x 3 22 true positives 8 misses (under-trigger) 4 false triggers 26 true negatives 60 runs total (illustrative) precision = 22 / (22 + 4) = 0.85 when it fires, is that right? recall = 22 / (22 + 8) = 0.73 when it should fire, does it? F1 = 0.79 false-trigger rate = 4 / 30 = 0.13 how often near-misses grab it
A trigger confusion matrix at the run level. Recall is the under-triggering problem; precision and the false-trigger rate are the over-triggering problem. With a pushy description you usually trade some precision for recall, so you must watch both. Numbers are illustrative.

At the query level, with the 0.5 threshold, the same data might look like this: of the 10 positives, 8 fired in at least 2 of 3 runs (pass) and 2 fired in 1 or 0 runs (fail); of the 10 negatives, 9 fired in at most 1 run (pass) and 1 fired in 2 runs (fail). Score: 17 of 20 queries pass. The two views answer different questions. Run-level rates tell you how often a real user would see the behavior; query-level pass counts tell you which phrasings are broken, which is what you need to fix the description.

Expert nuance: a partial trigger is its own signal

A query that fires 1 run in 3 is not "a miss". It is sitting on the model's decision boundary, and a tiny change in wording, model version or the other installed skills will flip it. Report the trigger-rate distribution (how many queries at 0/3, 1/3, 2/3, 3/3), not just the pass count. Many queries at 1/3 or 2/3 means your description is ambiguous, even if the pass count looks fine.

What a good description looks like

Because the description is the only thing the agent sees before deciding, description quality is trigger quality. Before running any optimizer, check a description against the rules that skill-creator and its scripts encode:

PropertyWhy it matters for triggeringWeakStronger
Says what and whenAll "when to use" information belongs in the description, not the body, because the body is not visible until after the decision"Quarterly report helper.""Build the quarterly sales report from a CSV export. Use whenever the user mentions QBRs, quarterly writeups or finance's quarterly CSV..."
A little pushyCounters the tendency to under-triggerLists only the exact skill nameAdds "even if they don't explicitly ask for a report"
ShortHard limit of 1,024 characters (longer is truncated); the optimizer aims for about 100 to 200 words; every word is paid on every requestA 400-word essayTwo or three dense sentences
Distinct from neighborsOverlapping descriptions collide (Part 4)"Helps with reports and slides"Names what it does not cover, or the artifact it produces

A description review like this is cheap and catches the obvious problems, but it is a hypothesis, not a measurement. Only the trigger eval tells you whether the description actually works with a given model.

Try it: move the description's trigger threshold and watch precision and recall trade off.

Optimizing the description without fooling yourself

Once you can score a description, the obvious next step is to improve it automatically: ask a model to rewrite the description based on which queries failed, re-score, repeat. skill-creator has a script for exactly this (run_loop.py), and its design contains a classic machine-learning lesson.

If you tune the description against the same 20 queries you score it on, you will overfit: the description slowly turns into a list of those exact queries' keywords, scores 20 of 20, and then fails on the next real user. So the loop:

  1. Splits the set into 60% train and 40% held-out test, stratified so both halves contain should-trigger and should-not-trigger queries (with 10 and 10, that is 6 + 6 train and 4 + 4 test).
  2. Evaluates the current description on both halves, running each query 3 times.
  3. Asks Claude to propose an improved description based on the train failures only.
  4. Repeats for up to 5 iterations.
  5. Returns the best_description, selected by test score, not train score.

It also runs the eval with the same model that powers the user's session, so the triggering test matches what the user will actually experience.

1.0 0.75 0.5 0 1 2 3 4 5 description iteration queries passed pick this one train (12 queries) held-out test (8) Train keeps rising: the description is memorizing the 12 training queries. Test peaks, then falls. Select by test score, so iteration 2 wins, even though 3 to 5 look perfect.
Overfitting a description. The train score reaches 12 of 12 while the held-out score drops back after iteration 2. Picking by train score would ship the most overfitted description. Illustrative data, not a real run.
Eight test queries is still a small test

With 8 held-out queries, one query flipping moves the score by 12.5 points, and the queries themselves are stochastic. Treat the held-out score as a guard against gross overfitting, not a precise measurement. If triggering really matters (a skill used by thousands of people), grow the query set, keep a second untouched set for a final check, and add real queries from production logs where the skill should have fired but did not.

Part 2: Uplift evals

Now the second question: when the skill is used, is the output better? "Better" must mean better than something. The heart of an uplift eval is a paired comparison: the same task run with the skill and against a baseline.

Choosing the baseline

  • New skill: the baseline is no skill at all. Same prompt, same model, same tools, no skill path. This answers "is the skill worth having?".
  • Improving an existing skill: the baseline is the old version. Snapshot the skill folder before editing and point the baseline run at the snapshot. This answers "is the new version better than the old?".

skill-creator insists on one operational detail: spawn the with-skill and baseline runs in the same turn, at the same time, rather than running all with-skill runs first and baselines later. Running them together means both arms see the same model endpoint, load and conditions, and it avoids the temptation to stop once the with-skill results look good. It is the agent-eval version of running control and treatment together in an experiment.

The test cases: evals.json

Test prompts live in evals/evals.json inside the skill folder. Start with 2 or 3 realistic prompts, and add verifiable statements (the expectations, also called assertions) while the first runs are in progress:

{
  "skill_name": "quarterly-report",
  "evals": [
    {
      "id": 1,
      "prompt": "finance dropped the Q3 export in data/q3_sales.csv, can you turn it into our usual quarterly report? regions are col B, revenue col E",
      "expected_output": "A .docx built from the company template with a revenue-by-region chart and a short executive summary",
      "files": ["evals/files/q3_sales.csv"],
      "expectations": [
        "The report is built from assets/template.docx (same headers and footer)",
        "Revenue-by-region totals in the chart match the CSV to the dollar",
        "The executive summary names the highest and lowest region by revenue",
        "The chart was produced by scripts/make_chart.py"
      ]
    }
  ]
}

What makes a good assertion, according to skill-creator:

  • Objectively verifiable. Someone else (or a script) could check it and agree. "Totals match the CSV to the dollar" is checkable; "the report is professional" is not.
  • Descriptively named. Someone glancing at the results should understand what each one checks without opening anything.
  • Not forced onto subjective qualities. Writing style or design quality go to human review instead. Forcing a fuzzy judgment into a pass/fail assertion gives you a noisy number you will over-trust.
  • Scripted when possible. For assertions a program can check (file exists, totals match, template headers present), write a script instead of eyeballing: faster, more reliable, reusable across iterations. This is the code-grader-first rule of Module 03.
A second practical recipe

Philipp Schmid's Practical Guide to Evaluating and Testing Agent Skills (2026) reaches a very similar recipe from outside Anthropic. His own summary: define success criteria covering outcome, style and efficiency, then write around 10 to 12 prompts with deterministic checks. The three criteria line up with this module: outcome is the uplift eval, style is where human review or a calibrated judge comes in, and efficiency is the time and token columns. His AI Engineer talk makes the point in its title: "Don't Ship Skills Without Evals".

The pipeline

evals.json prompts + files with_skill run outputs/ + timing.json (tokens, duration) baseline run no skill, or old snapshot same prompt, same time grader each assertion: text, passed, evidence aggregate pass rate, time, tokens: mean ± sd + delta grading analyst patterns the averages hide human viewer: outputs tab + benchmark tab free-text feedback per run, saved to feedback.json
The uplift pipeline in skill-creator. Paired runs start together, a grader checks every assertion against the transcript and output files and cites evidence, an aggregation script summarizes each configuration, an analyst pass looks for patterns the averages hide, and a human reviews real outputs, not just numbers.

The grader: evidence, not vibes

skill-creator's grader (agents/grader.md) is an LLM-as-a-judge (Module 13) with an unusually strict brief. It reads the full transcript and examines the output files itself (it is told not to trust what the transcript says was produced), then marks each assertion pass or fail. A few of its rules are worth memorizing because they apply to any grader:

  • Pass requires genuine substance. "A file exists with the right name but wrong or empty content" is a fail. So is meeting an assertion "by coincidence rather than by actually doing the work".
  • The burden of proof is on the assertion. When uncertain, fail.
  • No partial credit. Each assertion is pass or fail.
  • Extract and verify claims beyond the assertions: factual ("the form has 12 fields"), process ("used pypdf") and quality claims, flagging any that cannot be verified.
  • Critique the evals themselves. "A passing grade on a weak assertion is worse than useless: it creates false confidence." If an assertion would also pass for a clearly wrong output, or an important outcome has no assertion, the grader says so.

The result is a grading.json per run. Its expectations array must use the field names text, passed and evidence, because the viewer depends on them:

{
  "expectations": [
    {
      "text": "Revenue-by-region totals in the chart match the CSV to the dollar",
      "passed": true,
      "evidence": "Ran check_totals.py: all 6 regions match (APAC 1,204,310 = CSV sum)"
    },
    {
      "text": "The executive summary names the highest and lowest region by revenue",
      "passed": false,
      "evidence": "Summary names APAC as highest but calls LATAM lowest; CSV shows MEA is lowest"
    }
  ],
  "summary": {"passed": 3, "failed": 1, "total": 4, "pass_rate": 0.75},
  "claims": [
    {"claim": "All six regions are included", "type": "factual", "verified": true,
     "evidence": "Six rows in the chart data table"}
  ],
  "eval_feedback": {
    "suggestions": [
      {"assertion": "The report is built from assets/template.docx (same headers and footer)",
       "reason": "Passes in baseline runs too when the model copies a generic header; check the footer text exactly"}
    ],
    "overall": "Totals are well checked; the template assertion is weak."
  }
}

Aggregate, then look past the aggregate

An aggregation script (aggregate_benchmark.py) turns all the grading files into a benchmark.json with, for each configuration, the mean, standard deviation, min and max of pass rate, time and tokens, plus the delta between configurations. Time and tokens come from a timing.json saved for each run as soon as it finishes (the notification that carries total_tokens and duration_ms is the only chance to capture them).

Then comes a step many teams skip: the analyst pass (agents/analyzer.md, "Analyzing Benchmark Results"). Its job is to surface patterns that aggregate numbers hide, not to suggest fixes. For each assertion across all runs, it asks:

  • Does it always pass in both configurations? Then it does not differentiate skill value (a non-discriminating assertion).
  • Does it always fail in both? It may be broken, or beyond the model's ability.
  • Does it always pass with the skill and fail without? That is where the skill clearly adds value.
  • Does it always fail with the skill but pass without? The skill may be hurting.
  • Is it highly variable? Possibly a flaky assertion or non-deterministic behavior.

It also compares time, tokens and tool calls across configurations (does the skill make runs much slower or more expensive?) and looks for outlier runs that skew averages.

Worked example: the quarterly-report skill

Four test prompts, 3 runs each per configuration (a new skill, so the baseline is no skill). Here is what the benchmark summary and a per-assertion breakdown might look like. These numbers are illustrative, chosen to show the patterns the analyst pass is meant to catch.

ConfigurationPass rate (mean ± sd)Time, sTokens
with_skill0.86 ± 0.1052 ± 126,100 ± 900
without_skill0.44 ± 0.1734 ± 83,500 ± 600
delta+0.42+18+2,600
Assertion (12 runs per arm)With skillWithoutAnalyst reading
Output is a .docx file12/1212/12Non-discriminating: drop it or strengthen it
Built from the company template12/120/12Clear skill value (the baseline cannot know the template)
Chart totals match the CSV11/127/12Real uplift from the bundled script
Summary names top and bottom region10/128/12Small difference; not significant at this n
Chart made by make_chart.py11/120/12Process check: measures adherence, not uplift

The analyst notes might read:

[
  "Assertion 'Output is a .docx file' passes 100% in both configurations - may not differentiate skill value",
  "Eval 'emea-multi-currency' shows high variance with skill (1.00, 0.40, 1.00) - run 2 converted EUR twice; may be flaky",
  "Without-skill runs never use the company template (0/12)",
  "Skill adds 18s and ~2,600 tokens per run on average but improves pass rate by 0.42"
]

Three expert lessons hide in this small example:

  1. Some assertions inflate uplift by construction. "Chart made by make_chart.py" and "built from the company template" can never pass without the skill, because the baseline has no access to those files. They are legitimate checks, but they measure "did the agent use the skill?" (adherence), not "is the result better?". Report them separately, or the headline delta will look bigger than the real quality gain. The honest uplift number comes from outcome assertions both arms could pass (totals correct, summary correct).
  2. Cost is part of the result. A skill that adds 50% tokens and 18 seconds for a +0.42 pass-rate gain is a great trade. The same cost for a +0.03 gain probably is not. Always read the three columns together.
  3. A 4-prompt suite is a smoke test. skill-creator itself says to expand the test set and try again at larger scale once the skill looks good. To claim uplift with confidence you need enough prompts for a confidence interval, which is the Python section below.

The human review loop and iterations

Numbers alone do not tell you why an output is worse. skill-creator generates a viewer with two tabs: Outputs (one test case at a time: prompt, output files rendered inline, previous iteration's output, formal grades, and a feedback box) and Benchmark (the stats). The reviewer's comments are saved to feedback.json; empty feedback means "looked fine". It even says to generate the viewer before the agent evaluates the outputs itself, so a human sees real outputs as early as possible.

Draft / edit skill run with_skill+ baseline together grade + aggregate analyst pass human review(feedback.json) improve: generalize,keep lean, explain why iteration N stop when feedback is empty, the user is happy, or progress stalls quarterly-report-workspace/ ├─ iteration-1/ │ ├─ template-q3/ │ │ ├─ with_skill/outputs/ │ │ ├─ without_skill/outputs/ │ │ └─ eval_metadata.json │ ├─ emea-multi-currency/ │ ├─ benchmark.json │ └─ feedback.json ├─ iteration-2/ (reruns all) └─ skill-snapshot/ each run dir also holds grading.json and timing.json
The iteration loop and its workspace. Every iteration reruns all test cases, including the baseline, into a new directory, and the viewer shows the previous iteration's output and feedback side by side. Eval directories get descriptive names (template-q3), not eval-0, so results read clearly.

The advice on how to improve is as much eval wisdom as skill-writing wisdom:

  • Generalize from the feedback. You are iterating on a handful of examples, but the skill will be used on huge numbers of different prompts. Fiddly, overfitted fixes that only rescue these test cases are useless. This is the uplift-eval twin of the train/test lesson in Part 1.
  • Read transcripts, not just outputs. If the skill makes the agent waste time on unproductive steps, remove the part of the skill causing it and see what happens.
  • Look for repeated work. If every test run independently wrote a similar helper script, bundle that script in scripts/ once.
  • Explain the why. ALWAYS and NEVER in capitals are a yellow flag; explaining the reasoning usually works better with capable models.
Deep dive: blind comparison between two skill versions

For a more rigorous "is version 2 really better?", skill-creator offers an optional blind comparison. A comparator agent (agents/comparator.md) receives two outputs labeled A and B without knowing which skill produced which, builds a task-specific rubric with a content dimension (correctness, completeness, accuracy) and a structure dimension (organization, formatting, usability), scores each 1 to 5, and picks a winner, using assertion pass rates only as secondary evidence. Ties should be rare. Then an analyzer (agents/analyzer.md) "unblinds" the result: it reads both skills and both transcripts, scores instruction-following 1 to 10, and writes prioritized improvement suggestions in categories such as instructions, tools, examples and error handling.

This is pairwise LLM judging (Module 13), so the usual cautions apply: swap A and B positions across runs to cancel position bias, and validate the comparator against a few human judgments before trusting it on close calls.

Part 3: Adherence and process

An uplift number tells you the outcome improved. It does not tell you the skill is why. Maybe the agent loaded the skill, skimmed it, and solved the task its own way; the uplift came from something else, and the next model update will erase it. Adherence evals check the trajectory, the sequence of tool calls, against what the skill says to do. This is process grading from Module 05, applied to skills.

Skill says 1 validate CSV 2 run make_chart.py 3 read style-guide.md 4 fill template.docx Run A 4 of 4 steps Bash validate Bash make_chart Read style-guide Bash fill_template Run B 1 of 4 steps Bash validate Write my_plot.py skipped Write report.docx reinvented the bundled script ignored the template both runs may still pass "totals match"; only the trajectory shows run B did not use the skill
Adherence is read from the trajectory. Both runs loaded the skill. Run A followed it; run B loaded it and then did its own thing. If run B's output happens to be correct, an outcome-only eval would credit the skill for work it did not do.

Typical adherence checks, most of them simple code over the transcript:

CheckHow to grade itWhat a failure usually means
Skill body was loadedA Skill or Read call on the skill's SKILL.mdTrigger problem (Part 1)
Referenced file was read when neededRead call on references/style-guide.md before the step that needs itPointer in the body is unclear about when to read it
Bundled script was run, not rewrittenBash call containing scripts/make_chart.py; no new file doing the same jobThe body does not explain why the script matters, or the script failed and there was no fallback guidance
Steps in the prescribed orderOrder of matching tool calls (subsequence check)Order not explained, or not actually important (then drop the requirement)
No forbidden actionsNo calls matching a deny list (for example editing the input CSV)Missing guardrail in the body
Did not over-readCount of reference files read vs neededBody sends the agent to too many files: context waste

Two cautions. First, do not demand rigid adherence where it does not matter: if the agent reached the right result by a reasonable different path, that may be fine, and over-strict process checks punish good agents (Module 05 discusses this trade-off). Reserve hard process checks for steps that exist for a reason: validation, safety, using the canonical script so numbers are consistent. Second, adherence failures are information about the skill, not only the agent. skill-creator's analyzer scores instruction-following and lists concrete issues ("did not use the skill's formatting template", "invented own approach instead of following step 3") precisely so you can rewrite the step that was misread.

Part 4: Many skills installed

A real agent rarely has one skill. It may have dozens: documents, spreadsheets, slides, brand guidelines, several team-specific workflows. Now the trigger question becomes a multi-class selection question: given this request, which skill (if any) should load? Your skill's triggering, measured alone, can look perfect and still fail in a crowd.

"turn the Q3 numbers into slides for the board on Monday" xlsx quarterly-report board-deck pptx brand-guidelines ... 40 more labels two labels both say "quarterly" and "board" ~0.7k ~2.6k ~6.5k ~13k 5 20 50 100 label tokens vs skills installed accuracy 98% 95% 89% 81% illustrative: measure your own curve
Selection in a crowd. Left: two skills with overlapping descriptions compete for the same request, a description collision. Right: the always-on label cost grows linearly with the number of installed skills (assuming about 100 words, roughly 130 tokens, per label), and selection accuracy can fall as choices multiply. The accuracy numbers are illustrative, not measured.

How to evaluate selection

  1. Build a labeled routing set. Queries labeled with the one skill that should load, or "none". Include queries for every skill you ship, plus near-misses that belong to none.
  2. Install a realistic set of distractors. Run with the full set of skills your users actually have, not just yours. If you do not know, test at several sizes (for example 5, 20, 50 skills) and plot the curve.
  3. Measure multi-class metrics. Top-1 selection accuracy, per-skill precision and recall, and a skill-by-skill confusion matrix. The off-diagonal cells show exactly which pairs collide.
  4. Hunt collisions before users do. Compare descriptions pairwise (even simple embedding similarity works as a first filter) and write disambiguation queries for the most similar pairs: "board slides from Q3 numbers" should go to board-deck, "the Q3 report doc" to quarterly-report.
  5. Watch for double loads. Sometimes the agent loads two skills whose instructions conflict (two different chart styles). Count runs with more than one skill loaded and check the output against both.
A pushy description is a tax on your neighbors

Part 1 recommended "pushy" descriptions to fight under-triggering. In a crowd, a pushy description also steals requests from other skills. Every time you widen a description, re-run the other skills' trigger evals too. Organizations with many skills benefit from a shared routing eval that runs whenever any description changes, like a regression suite for triggers.

Part 5: Portability and regressions

Because the format is an open standard, the same skill folder may run under different models (a large model and a small, cheap one) and different harnesses (a coding CLI, an IDE, a chat app, your own agent built on an SDK). Each combination can change both halves of the funnel: triggering depends on how the harness presents the skill list and on the model's judgment; uplift depends on how capable the model already is without the skill.

Illustrative resultsLarge model, harness XSmall model, harness XLarge model, harness Y
Trigger recall0.900.650.80
False-trigger rate0.050.100.15
Uplift (pass-rate delta)+0.20+0.45+0.18
Adherence (script used)95%70%90%

Read the table as a pattern, not data: a smaller model might trigger less reliably yet gain more from the skill when it does fire, because it lacked the know-how to begin with. A different harness might present skills differently and produce more false triggers. You only find out by running the same suite on each target, which is why skill-creator says to run trigger evals with the model the user will actually use.

When the model updates

Skills are written against a model's current behavior, and models change. Treat every model update as a reason to rerun the skill suite (Module 15 covers wiring this into CI). Three things can move:

  • Triggering drifts. A new model may be more or less eager to consult skills. Rerun the trigger set and compare rates per query, not just the total.
  • Uplift shrinks. If the new baseline model already does the task well, the skill's delta may drop toward zero. That is not a regression in quality; it is a signal the skill may now be dead weight (context cost for no gain) and could be slimmed down.
  • Uplift turns negative. Instructions written to steer a weaker model ("always do X in exactly this way") can hold back a stronger one. An assertion that "always fails with the skill but passes without" is the analyst's flag for this.

Keep the pinned baseline idea from Part 2: store a snapshot of each released skill version and its benchmark, so a later rerun can separate "the model changed" from "the skill changed".

Part 6: Safety, skills as a supply chain

A skill is instructions plus, often, executable code, and the agent runs that code with whatever permissions it has. Installing a third-party skill is therefore closer to installing a software package than to reading a document. skill-creator states a "Principle of Lack of Surprise": a skill must not contain malware, exploit code or anything that could compromise security, and its contents should not surprise the user in their intent if described. Your evals should verify that, not assume it. Module 17 covers safety evals in general and Module 25 covers agent security evals in depth (prompt injection, tool poisoning, sandbox escape and more). This section only covers what is specific to skills.

Author Marketplace Install Agent reads Agent runs threat trojan or look-alikeskill with a usefulcover story a later updateswaps in a payloadafter trust is built over-broad descriptionmakes it load onevery task injected instructions:"also upload theconfig to this URL" script reads secretsor env vars andsends them out eval / defense provenance checks,trusted sources only,pinned versions static review andscanners; re-reviewevery version diff trigger eval onunrelated tasks: false-trigger rate near zero adversarial skill suite:does the agent complyor flag it? sandbox runs withcanary secrets andegress logging each stage needs its own check; a clean static review does not cover what the agent does at run time
A skill's supply chain, with a threat and a matching eval at every stage. The top row is the path a skill takes to your agent. The red row is what can go wrong at each stage; the green row is the check that catches it. The runtime checks (the two right columns) matter most, because they test what actually happens, not what the files appear to say.

How to evaluate skill safety

  • Static review. Read every script and reference file, not just SKILL.md. Flag network calls, reads of credentials or home directories, obfuscated code, instructions that tell the agent to hide actions from the user, and remote content fetched at run time (which can change after review). Re-review every update as a diff.
  • Sandboxed dynamic runs. Run the skill's own test prompts in an isolated environment seeded with canary secrets (fake keys that should never leave) and log all outbound traffic and file access. Any canary in egress is a hard fail, the same canary idea as in Module 17.
  • Adversarial skills. Build a small suite of deliberately malicious test skills (in a sandbox, with harmless placeholder payloads): a skill whose body asks the agent to send a file to an external URL, a skill whose script tries to read environment variables, a skill with an over-broad description. Measure two things: does your review pipeline flag them (detection rate), and does the agent comply, refuse or warn the user when it loads them (compliance rate)? A good agent should treat a skill's instructions as less trusted than the user's when they conflict with the user's intent.
  • Trigger hijacking. Run a malicious skill's description through your routing eval (Part 4). A description engineered to load on everything shows up as a very high false-trigger rate.
  • Least privilege as a backstop. Evals miss things. Pair them with runtime limits: no production credentials in the agent's environment, network egress allow-lists, confirmation before irreversible actions. Module 25 shows how to measure attack success rates and utility under attack for these defenses.
This is not hypothetical

Security researchers have already examined public skill marketplaces. For example, Snyk's "ToxicSkills" study (2026) reports finding prompt-injection content and malicious payloads in skills published on a public marketplace, and a 2026 survey of agent skills (arXiv 2602.12430) treats security as one of the field's central open problems. Treat any skill you did not write the way you would treat an unreviewed package from the internet.

What research says

Skills are young, so the research base is small. The most relevant benchmark so far is SkillsBench ("Benchmarking How Well Agent Skills Work Across Diverse Tasks", arXiv 2602.12670, February 2026). Its design is exactly the uplift eval of Part 2 at scale: the same tasks, run by agents with and without skills. The paper reports:

  • About 84 tasks across 11 domains.
  • Curated, human-written skills raised the pass rate by roughly 16 percentage points on average, with large variation by domain: small gains in software engineering, where models already know a lot, and much larger gains in some specialized domains such as healthcare.
  • Self-generated skills, where the model writes its own skill before solving the task, gave no reliable benefit on average.
  • Focused skills beat long, comprehensive ones.

Each finding maps onto an eval lesson. Variation by domain means an average uplift hides a lot, so report per domain or per task family. The self-generated result means "the model can write its own skills" is a claim to test, not assume; the know-how has to come from somewhere. And the focused-beats-comprehensive result echoes skill-creator's advice to keep the body lean and push detail into reference files: more context is not automatically more help.

Putting the parts together: a skill's expected value

A useful mental model: the value a skill adds per request is roughly P(trigger | relevant) x uplift when triggered, minus the cost of false triggers (tokens and occasional derailment on irrelevant requests), minus the always-on label cost. A skill with huge uplift but 30% recall wastes most of its potential; a skill with perfect recall but zero uplift is pure overhead. This is why you need both halves measured separately, and why fixing the cheaper half first (usually the description) is often the fastest win.

Fix or slim the body loads at the right times but adds little; check adherence, then the content Ship it fires when it should and helps; add it to the regression suite Rethink or drop it rarely fires and does not help; pure context cost Fix the description great when used, but rarely used; run the trigger optimizer uplift when triggered (Part 2) → low high trigger quality (Part 1) high low
A diagnostic grid for any skill. Put the trigger eval on one axis and the uplift eval on the other, and the next action falls out. This is the practical payoff of measuring the two halves separately.

The math in Python

Two small functions cover the core numbers of this module. The first computes run-level trigger metrics and query-level pass counts from repeated runs, using the same pass rule as skill-creator (trigger rate compared with a 0.5 threshold). The second computes a paired bootstrap confidence interval for uplift. "Paired" matters: each eval is run in both arms, so you resample whole evals and keep each with-skill score next to its own baseline. That removes the "some evals are just harder" noise both arms share (Module 04 explains why paired differences are so much tighter).

import random
from statistics import mean

# ---- Part 1: trigger metrics from repeated runs -------------------------
# One record per query; "fired" holds one entry per repeat (3 runs each).
trigger_results = [
    # should-trigger queries (10)
    *[{"should_trigger": True, "fired": f} for f in [
        [1,1,1],[1,1,1],[1,1,1],[1,1,1],[1,1,1],
        [1,1,0],[1,0,1],[0,1,1],[1,0,0],[0,0,0]]],
    # should-not-trigger queries (10), mostly near-misses
    *[{"should_trigger": False, "fired": f} for f in [
        [1,1,0],[0,1,0],[0,0,1],[0,0,0],[0,0,0],
        [0,0,0],[0,0,0],[0,0,0],[0,0,0],[0,0,0]]],
]

def trigger_metrics(results, threshold=0.5):
    """Run-level confusion matrix plus query-level pass count."""
    tp = fn = fp = tn = 0
    query_pass = 0
    for r in results:
        fired = [bool(x) for x in r["fired"]]
        k, n = sum(fired), len(fired)
        if r["should_trigger"]:
            tp += k; fn += n - k
            query_pass += (k / n) >= threshold      # same rule as skill-creator
        else:
            fp += k; tn += n - k
            query_pass += (k / n) < threshold
    precision = tp / (tp + fp) if tp + fp else float("nan")
    recall = tp / (tp + fn) if tp + fn else float("nan")
    f1 = 2 * precision * recall / (precision + recall)
    return {"tp": tp, "fn": fn, "fp": fp, "tn": tn,
            "precision": round(precision, 3), "recall": round(recall, 3),
            "f1": round(f1, 3), "false_trigger_rate": round(fp / (fp + tn), 3),
            "queries_passed": f"{query_pass}/{len(results)}"}

print(trigger_metrics(trigger_results))

# ---- Part 2: paired bootstrap CI for uplift -----------------------------
# Mean pass rate per eval (averaged over its 3 runs), same 12 evals in both arms.
with_skill = [0.89, 1.00, 0.78, 0.89, 1.00, 0.67, 0.89, 0.78, 1.00, 0.89, 0.56, 0.89]
baseline   = [0.44, 0.67, 0.33, 0.56, 0.78, 0.44, 0.33, 0.56, 0.89, 0.44, 0.56, 0.22]

def paired_bootstrap(a, b, n_boot=10_000, seed=0):
    """95% CI for mean(a - b), resampling whole evals (keeps the pairing)."""
    diffs = [x - y for x, y in zip(a, b)]
    rng = random.Random(seed)
    boots = sorted(
        mean(rng.choice(diffs) for _ in diffs) for _ in range(n_boot)
    )
    lo, hi = boots[int(0.025 * n_boot)], boots[int(0.975 * n_boot) - 1]
    return round(mean(diffs), 3), (round(lo, 3), round(hi, 3))

print(paired_bootstrap(with_skill, baseline))

Output (illustrative data):

{'tp': 22, 'fn': 8, 'fp': 4, 'tn': 26, 'precision': 0.846, 'recall': 0.733, 'f1': 0.786, 'false_trigger_rate': 0.133, 'queries_passed': '17/20'}
(0.335, (0.232, 0.438))

The uplift is +0.335 with a 95% interval of roughly +0.23 to +0.44. Because the whole interval is above zero, you can say the skill helps on this suite. Notes for real use:

  • Resample evals, not runs. The three runs of one eval are correlated (same prompt, same difficulty). Treating 36 runs as 36 independent points would make the interval falsely narrow. This is the clustering point from Module 04.
  • A bootstrap over 3 or 4 evals is meaningless. You need something like a dozen or more evals before an interval says much. Small suites are for iteration; bigger suites are for claims.
  • Exclude assertions the baseline cannot pass by construction (Part 2) before computing uplift, or compute two numbers: outcome uplift and adherence.
  • Same interval idea for triggering. To compare two descriptions, compute the per-query difference in trigger rate and bootstrap over queries.

A skill eval checklist

QuestionEvalMinimum setupMain pitfall
Does it fire when it should, and only then?Trigger eval~20 realistic queries, half near-miss negatives, 3 runs eachTrivial queries that never trigger; easy negatives that test nothing
Is the description tuned, not overfitted?Train / held-out optimization60/40 stratified split, pick by held-out scoreSelecting by train score
Does it make outputs better?Uplift evalWith-skill and baseline launched together, objective assertions, grader with evidenceNon-discriminating assertions; adherence-only assertions inflating the delta
At what cost?Same benchmarkTime and tokens, mean ± sd, per armReporting pass rate alone
Is the skill actually followed?AdherenceTrajectory checks for reads, script runs, orderOver-strict process checks
Does it hold up in a crowd?SelectionRouting set with realistic distractor skills, skill confusion matrixTesting each skill alone
Does it survive model and harness changes?Portability / regressionSame suite per target; rerun on every model updateMistaking a better baseline for a worse skill
Is it safe to install?Supply-chain safetyStatic review, sandbox with canaries, adversarial skillsReviewing SKILL.md but not the scripts

Key takeaways

  1. A skill is a folder the agent chooses to open. Its short description is always in context; the body and files load only when needed. That choice is a new failure point that tools and prompts do not have.
  2. Measure the two halves separately. Triggering (does it fire at the right times?) and uplift (does it help when it fires?) have different causes and different fixes.
  3. Trigger evals are classification. Realistic, substantive queries; near-miss negatives; 3 runs per query; precision, recall and the trigger-rate distribution.
  4. Optimize descriptions on train, choose on held-out. A description that aces its training queries has probably memorized them.
  5. Uplift needs a paired baseline run at the same time, objective assertions, a grader that cites evidence and fails superficial passes, and an analyst pass for non-discriminating, flaky and costly patterns.
  6. Check the trajectory, not just the output, to confirm the skill is the reason for the gain.
  7. Test in a crowd, across models, and on every update, and treat third-party skills as a software supply chain: review, sandbox, canaries, adversarial skills.

Check yourself

1. Your trigger eval shows 40% recall. Most should-trigger queries are one-liners like "read report.pdf". What is the most likely problem?

skill-creator notes that simple one-step queries may not trigger a skill even when the description matches perfectly. Rewrite the queries to be substantive and realistic before touching the description.

2. Which is the best should-not-trigger query for a quarterly-report skill?

The best negatives are near-misses: they share keywords (Q3, revenue, CSV) but need something else (a single number, not a report). Obviously irrelevant queries test nothing.

3. During description optimization, iteration 4 scores 12/12 on train and 5/8 on held-out test; iteration 2 scores 11/12 and 7/8. Which do you ship?

A perfect train score with a falling test score is overfitting. skill-creator's loop returns the best description by test score for exactly this reason.

4. The assertion "Output is a .docx file" passes 12/12 with the skill and 12/12 without. What should you do?

An assertion that passes regardless of the skill says nothing about skill value, and inflates both pass rates. The analyst pass is designed to flag exactly this.

5. Your uplift delta is +0.50, but most of it comes from "Chart was made by scripts/make_chart.py" and "Built from assets/template.docx". What is the concern?

Process assertions tied to skill files are legitimate adherence checks, but they make the headline delta look bigger than the real improvement in results.

6. After a model update, your skill's uplift falls from +0.30 to +0.02, while with-skill pass rate stays the same. What most likely happened?

Same with-skill score plus a smaller delta means the baseline rose. That is a portability signal, not a regression in the skill itself.
Expert corner: interview questions
  1. "How would you evaluate a new skill before rolling it out to 5,000 employees?" Separate trigger and uplift. Trigger: 20 or more realistic queries with near-miss negatives, 3 runs each, precision and recall, then a routing eval with the other skills employees actually have installed. Uplift: paired with-skill vs no-skill runs launched together, objective assertions graded with evidence, time and tokens, an analyst pass, human review of real outputs, and a paired bootstrap interval once the suite is big enough. Add adherence checks and a security review (static plus sandbox with canaries) before publishing.
  2. "Why is description optimization an overfitting risk, and how do you control it?" The optimizer sees which queries failed and rewrites the description toward them, so train score climbs by memorizing. Control it with a stratified train / held-out split, selecting by held-out score, repeated runs per query, and ideally a final untouched set plus fresh production queries.
  3. "The with-skill pass rate is high, but you suspect the skill is not the reason. How do you check?" Compare against the no-skill baseline on outcome-only assertions; read trajectories for adherence (did it read the reference file, run the bundled script); ablate parts of the skill (remove the script, remove a section) and see which removal drops the score.
  4. "What changes when 50 skills are installed instead of one?" Triggering becomes multi-class routing; description collisions and double loads appear; always-on label cost grows linearly; a pushy description steals traffic from neighbors. Evaluate with a labeled routing set, realistic distractors, a skill-by-skill confusion matrix, and rerun all trigger evals whenever any description changes.
  5. "How would you test whether an agent is safe against a malicious skill?" Build an adversarial suite of sandboxed test skills with placeholder payloads (exfiltration instructions in the body, a script reading env vars, an over-broad description). Seed canary secrets, log egress, and measure both detection by the review pipeline and compliance by the agent. Back it with least privilege, because evals will miss some attacks.
  6. "What does SkillsBench suggest about how to report skill uplift?" The paper reports large variation by domain and no reliable benefit from self-generated skills on average, so report uplift per domain or task family with intervals, compare against both no-skill and self-generated baselines if you claim a skill-writing method works, and prefer focused skills to long comprehensive ones.

Sources and further reading