Decoding Anthropic's Skill authoring guide: lean SKILL.md files that actually trigger

Anthropic's guide to writing Agent Skills, decoded for Claude Code users who want reusable procedures without bloating every session's context.

Take a small blog project. Ours is a Flask + SQLite site, public on GitHub as troyscott/skills-sample-blog. Put everything Claude might need into its CLAUDE.md: how to install, run and test the site, then the release steps, how to write a post, and the exact steps for a database migration. Claude Code loads all of it into every session, whether the task is a typo fix or a schema change. The migration steps get followed loosely. A release goes out without its changelog entry. You can't tell which lines Claude is actually using.

The fix is to split the file by one question: does Claude need this for any task on the project, or only for one kind of task? What it needs for any task stays in CLAUDE.md. Each procedure moves into its own Skill, a folder with a SKILL.md that Claude loads only when a request matches the Skill's description. Anthropic's Skill authoring best practices is the manual for writing those Skills well. Its most important lesson is the easiest to skip: Claude uses the description to decide whether to use a Skill, so the description has to match how you ask. We built the split and tested it. Some runs had no call to the Skill, but in every one of those Claude read the Skill's file anyway.

What it is

A Skill costs almost nothing until it's used. Claude reads each Skill's name and description at startup, about 100 tokens per Skill, and loads the body (under 5k tokens) only when the Skill triggers, per the Agent Skills overview. So on the day you fix a typo, the migration steps cost nothing.

The guide comes down to four habits.

The description decides whether the Skill runs. Claude picks from potentially 100+ Skills by description alone, so a description has to say what the Skill does and when to use it. The guide's example:

description: Extract text and tables from PDF files, fill forms, merge documents. Use when working with PDF files or when the user mentions PDFs, forms, or document extraction.

Against that it sets "Helps with documents" and "Processes data". The second sentence of the good one lists the words a person would actually say.

The body assumes Claude already knows the basics. The guide's bad example spends about 150 tokens explaining what a PDF is and why one library beats another. The good one is three lines:

import pdfplumber
with pdfplumber.open("file.pdf") as pdf:
    text = pdf.pages[0].extract_text()

Our migration Skill below does the same. It never explains what a migration is; it says where the file goes and what to run. Keep the body under 500 lines; longer reference material goes in files the SKILL.md links directly.

Where a mistake is costly, give one exact command. The guide says to decide, step by step, how much room Claude gets. For a code review, leave it room. If it reviews the wrong way, you ask again and nothing has changed. For a database migration, a wrong guess can drop a column or lose rows, and you can't ask again. So the Skill doesn't describe what to do. It gives the command:

python scripts/migrate.py --verify --backup

--backup copies the database before anything changes. --verify checks that no applied migration was edited since it ran. If Claude writes its own version of the command and leaves one flag off, that protection is gone. That's why the Skill says to run exactly this and not change it.

Test with the prompts you actually type, on every model you use. What works for Opus might need more detail for Haiku.

The alternatives each keep one cost. Leaving everything in CLAUDE.md means it's always loaded; the author of a DEV Community write-up gave up on that because it "would be enormous, and most of it irrelevant to any one task." AGENTS.md is the cross-tool version of the same always-loaded file. The third option is a hook: a small script Claude Code runs at a fixed moment, in this case every time you send a prompt. A developer at the Claude Fast blog built one, described in "Claude Code Skills Not Activating? The Real Fix": it scans your prompt for keywords and adds a line naming the Skills that match, so Claude sees the hint before it answers. Their own caveat: "a hook is a keyword matcher. It does not know what you are doing." If your prompt doesn't contain one of its listed words, there is no hint.

The format also travels: Anthropic handed Agent Skills to the Agentic AI Foundation on 22 October, with more than 70 platforms loading them, per Max Nardit.

Who it's for

Pick it up if you repeat the same procedure across sessions, if some steps need an exact command, or if you can't say what each paragraph of your CLAUDE.md is for.

Skip it when the step must run every time. Claude chooses whether to use a Skill, and as Agentkitbest puts it, "availability is not activation." A rule Claude should always see goes in CLAUDE.md; a step that has to be enforced goes in a hook or a script. Skip it too when the rule applies to every task, which is what CLAUDE.md is for, or when a one-off prompt is enough.

Before and after

We split the blog's CLAUDE.md the guide's way. This section comes from the repo and our own test runs, not from a source.

CLAUDE.md keeps what Claude needs for any task: how to run the project, where things are, and the rules every change follows, in 33 lines. Two of its rules matter for this story:

- Every schema change is a new migration. Never change the database by hand.
- One branch per change. The only commit made on `main` is a release (see the `releasing` Skill).

Both are true on every task. Even a typo fix must not touch the database by hand, and even a typo fix goes on a branch. That is the test for CLAUDE.md: if it has to hold on a task that has nothing to do with it, it goes here.

How to do a migration is not true on every task. It only matters when the schema changes, so it's a Skill. There are three in the repo: writing-a-post, releasing and adding-a-migration. This is the strict one:

---
name: adding-a-migration
description: Change the database schema by adding a numbered SQL migration and applying it with a backup. Use when the user asks to add, change or drop a table or column, change the schema, or run migrations.
---
1. Create `migrations/NNN_short_name.sql`, numbered after the highest existing one.
2. Never edit, rename or delete an applied migration. To fix one, add a new migration.
3. Apply it. Run exactly this command; don't change it or run SQL by hand:
   python scripts/migrate.py --verify --backup
4. If it prints `error:`, stop and show the user the whole message.
5. Update the code and tests that use the changed table, then run `pytest`.

Step 3 is the exact command from earlier. Step 4 is the stop rule: on a failure Claude shows the message rather than trying something else. Neither line is in CLAUDE.md, because on a typo fix they would be noise.

Then we tested each Skill the guide's way: plan mode, prompts that don't name it, the session log as the judge. Claude Code has a Skill tool, and the log records each call to it. Below, a Skill call means that entry is in the log, and a miss means it isn't. "Draft a post about why we moved the blog to SQLite" got a Skill call for writing-a-post. "Ship a new version" got one for releasing. "Posts need a short summary field shown on the index page. Plan the change; don't edit anything." had no Skill call for adding-a-migration on the first try. One possibility was the wording: the description said "table or column" and the prompt says "field". So we widened the description:

description: Change the database schema by adding a numbered SQL migration and applying it with a backup. Use when the user asks to add, change or remove a field, column or table, store something new about posts, change the schema, or run migrations.

After that, the Skill call appeared in three of four runs. We went back to the original description for three more runs, and all three had the call. That's eight runs, some with the shorter "Posts need a short summary field.", and two misses, one with each description. Eight runs can't tell us whether the wording matters.

In both misses Claude still read the Skill's file. It opened .claude/skills/adding-a-migration/SKILL.md with cat, along with the rest of the code, and planned the exact command. That is how the Agent Skills overview describes loading a Skill: "Claude uses bash to read SKILL.md from the filesystem." So a miss means only that the log had no Skill call, not that Claude went without the steps.

We then ran the prompt 40 more times, 10 per setup, changing two things: whether the sample's CLAUDE.md names the Skills, and whether the prompt ends "Plan the change; don't edit anything." The Skill call showed up in 32 of 40 runs. In the other 8, Claude read SKILL.md as a file. All 40 plans had the exact command. Those 8 all came from runs without the CLAUDE.md line, but our first eight runs had the line and still missed twice, so we can't say the line is the cause.

What holds: the plan looks the same either way, so the session log is the only place to see how Claude got the steps. This covers Skills stored in a repo and used in Claude Code; we didn't test claude.ai. A Skill call isn't certain, which is why the guide pairs the description with testing. A step that has to happen every time goes in a hook or a script, not a Skill.

Corrected 2026-11-02: an earlier version said the misses came from "field" vs "column" and that Claude never read the Skill's steps. The run logs show neither. A second pass the same day fixed lines elsewhere that still said the steps were skipped, gave the wrong run count, and said the wording was ruled out.

The rule of thumb: CLAUDE.md holds what's true for every task (how to run and test, where things are, the rules), short and always loaded. A Skill holds the steps for one kind of task (release, migrate), longer and loaded only when it matches.

Try it

Clone the sample; its README has the setup:

git clone https://github.com/troyscott/skills-sample-blog

Then open the folder in Claude Code and test one Skill, in the order Agentkitbest recommends:

  1. Name it. Type /adding-a-migration add a summary column to posts. This only proves the Skill's steps work when it's loaded.
  2. Don't name it. Start a new session and type what you'd really say: "Posts need a short summary field." This is the test that matters: does the description match how you ask?
  3. Check how Claude got the steps; don't judge the answer. In a session, Claude Code shows the Skill being read before Claude replies. In a scripted run (claude -p --output-format stream-json), search the output for a Skill tool call, or for a read of the Skill's SKILL.md. The plan won't tell you: all 40 plans in our second round had the exact command, with or without a Skill call.
  4. Edit the description, restart the session, test again. Claude reads descriptions at startup, so an edit doesn't count until the restart.

Then take one procedure you already repeat in your own project, write its description first, and test it with the prompt you'd really type before you write the body. For Anthropic's own Skills, Lilach Bullock's guide to the skills repo gives the install commands; its skill-creator Skill helps write test prompts and tune a description.

Where it breaks

Sources