Makes every change to an agent skill land finished: built to the skill standard, self-contained, routed by a sharp description, and proven by its own checkers and their planted-bug self-test. The usual reader of a skill is an agent working with no human review, so a skill is correct only when the checks here say so.
Rules that must hold
- A skill is done only when its checks pass.
validate-skill.mjs, the skill's ownscripts/selftest.mjsandcheck-skill.mjsall printRESULT: PASS; nobody reads skill files closely, so nothing else counts as review. - Copy knowledge in; never point at it. No repo docs, design files, research notes, other skills' files, maintainer tooling paths, absolute, scratch or
../paths, and no runtime variable that only some agents set (${CLAUDE_SKILL_DIR}in markdown is the one allowed); a skill is installed alone (a plugin cache,.agents/skills/,.claude/skills/) and every pointer dangles there. - The description decides whether the skill is ever used. At most 300 characters (aim for 250), a third-person verb first, "Use when" with the words tasks contain, and "Not for ()"; the agent sees only names and descriptions before it loads a skill, and that listing is budgeted.
- Rules and the definition of done sit at the top, in the six-section order, and SKILL.md stays at 300 lines or fewer. After context compaction only the first 5,000 tokens of a skill survive, so whatever sits lower is lost in long sessions.
- Frontmatter is
nameanddescription, plus the few allowed keys.disable-model-invocation: truebelongs only on a side-effectingrun-*runbook whose workflow stops and asks;allowed-toolsandhooksmake the agent's own invocation need approval (denied in headless runs), andpathsoruser-invocablehide the skill. - Every checker proves itself against planted bugs. A good case and one
bad-*case per rule listed in its--help, each EXPECT naming the rule id, written as overlay cases (amutation.jsonon a shared base); a check that has only ever passed proves nothing, and an EXPECT that says only "FAIL" passes on a crash. - Checkers keep the output contract and never pass on nothing. Positional repo root,
--help, exit 0/1/2, aRESULT:last line; a missing target exits 2; SKIP only throughnotYetCreated()for a file another skill creates, NOT APPLICABLE only for a repo fact, and ship gates never skip; a silent pass turns a gate green on a typo. - Checkers read the app through its layout and contracts, never through hard-coded paths.
resolveLayout()for folders,walkRepo()for scans (it prunes installed skills, whose fixtures plant bugs on purpose), app facts in a repo-ownedcontracts/<name>.jsonwhose template the skill ships, and every device named (maestro --device, a simctl UDID,adb -s, an AVD name); otherwise the checker works in one repo only or reaches another session's simulator. - Templates are real files that name their repo path on line 1 and pass a real app's gates.
tsc, ESLint and Jest in a reference app; a template that does not compile teaches the bug it was meant to prevent. - Edit the canonical copy, never a synced or generated file. Shared files change in the library's shared folder and arrive through its sync; generated files change through their generator; the next sync or render erases a hand edit, and the validator fails on drift first.
- Every version-sensitive fact carries a "verified on" date and a re-verify command. Expo, React Native and agent tooling move faster than memory; a dated fact with its command is refreshed in minutes, an undated one rots silently.
- Touch only the skill you were asked to change. Other people and agents work on other skills in parallel, writes under
.claude/need the developer, and shared or generated files have owners; put every other needed change in the report.
Workflow
- Decide: a new skill or a change to an existing one. Run
node ${CLAUDE_SKILL_DIR}/scripts/check-routing.mjs . --prompt "<the task>"from the repo root to see which skill already wins the task: it prints the top three asrank #1 <skill> (score ...)and judges nothing (add--expect <skill>to make it an eval); in a repo whose only skills are installed ones the root prints NOT APPLICABLE, so pass.agents/skillsinstead of.. Read references/descriptions-and-routing.md when the boundary is unclear; extend a skill rather than adding a near-duplicate. - New skill:
node ${CLAUDE_SKILL_DIR}/scripts/new-skill.mjs <name> --skills-root skills(add--runbookfor a side-effectingrun-*runbook; a repo withoutskills/yet gets it created). The scaffold holds the six sections, a reference, a working pattern checker with its overlay self-test, and__FILL_...__placeholders wherever a person must write. Read references/authoring-standard.md before filling it; examples/new-skill-walkthrough.md shows a complete run. - Knowledge: copy it into
references/(a Contents list when over 100 lines),templates/(line 1 names the repo path; prove each in a real app withtsc, ESLint and Jest),examples/(shapes only) andassets/. Date every version fact and give its re-verify command. - Scripts: read references/writing-scripts.md for the contract and the check-lib API, then references/self-tests-and-fixtures.md for the overlay cases. Write one
bad-*case per rule, then run each checker on a real app repo and read every finding: a rule that fires on correct code gets ignored. - Description: write it with the descriptions reference, then add two realistic prompts for the skill and one near miss for its closest neighbour to the repo's routing evals (
contracts/routing-evals.json): copy both templates/contracts/routing-evals.json andtemplates/contracts/routing-evals.schema.jsonintocontracts/(the evals file names the schema beside it), then replace its__PLACEHOLDER__seeds with the new skill, its neighbour and their prompts;check-routing.mjsfails on a seed left in (eval-placeholder). - Check the skill:
node ${CLAUDE_SKILL_DIR}/scripts/validate-skill.mjs skills/<name>, thennode skills/<name>/scripts/selftest.mjswhen it has scripts, thennode ${CLAUDE_SKILL_DIR}/scripts/check-skill.mjs skills/<name>. Fix everyFAILline and rerun until all three pass. In a skill library with a canonical shared folder, add--shared <that folder>to the validator so it also proves the synced copies. - Check the set:
node ${CLAUDE_SKILL_DIR}/scripts/check-skill-set.mjs .(the repo root, so skills installed under.agents/skillscount for collisions and the listing) andnode ${CLAUDE_SKILL_DIR}/scripts/check-routing.mjs .. A problem your change caused is yours; one in a skill someone else is building goes in the report (--skip-missingturns evals for unbuilt skills into notes). After a description rewrite, confirm with the agent itself:check-routing.mjs . --live --only <name> --runs 3 --plugin-dir .(a few short model calls;--plugin-dir .loads skills authored inskills/, which the agent otherwise never sees, and the run stops with exit 2 when an expected skill is not loaded). - Library round, when the repo is a skill library with its own tools (shared-file sync, a catalogue, all self-tests): follow references/library-round.md, which also covers the development loop and the settings that keep skill scripts from prompting.
- When a skill does not load, trigger or run, match the symptom in the pitfalls table of references/how-skills-load.md.
- Report: the skill, its description, what each script checks, the
RESULTlines, and everything left for someone else (another skill's files, a shared file's owner, settings, a human step). Name the review items no checker can see (the heuristic limits in the authoring-standard reference): a prose pointer to a repo document, a reference copied without its source and date, a skip whose rule id is not a literal.
Definition of done
-
node ${CLAUDE_SKILL_DIR}/scripts/validate-skill.mjs skills/<name>printsRESULT: PASS, andnode skills/<name>/scripts/selftest.mjsprintsRESULT: PASSwhen the skill has scripts, with everybad-*case failing for its own rule. - Every template names its repo path on line 1 and passed
tsc, ESLint and Jest in a real app; every version fact is dated with a re-verify command. - The repo's routing evals hold two prompts for the skill;
node ${CLAUDE_SKILL_DIR}/scripts/check-skill-set.mjs .shows no problem caused by this change andnode ${CLAUDE_SKILL_DIR}/scripts/check-routing.mjs .passes (--skip-missingonly while other skills are still being built). -
node ${CLAUDE_SKILL_DIR}/scripts/check-skill.mjs skills/<name>printsRESULT: PASS
These scripts print no SKIP lines. In a repo with no project skills (none in skills/, .claude/skills/ or .agents/skills/) each prints NOT APPLICABLE: no project skills ... and passes; in a repo whose only skills are installed ones it prints NOT APPLICABLE: only installed skills (...) (pass .agents/skills to check those).
Anti-patterns
- "See the handbook, chapter 14, for the details." The chapter is not there when the skill runs elsewhere; copy the rows you need into a reference.
- Fixing a routing miss by pasting the eval's words into the description. Name the category of work, or add a "Not for" to the skill that won.
- Editing
scripts/check-lib.mjs(or any synced file) inside a skill. The next sync reverts it, and the validator fails first; change the canonical copy. - A checker that exits 0 when its folder is missing, or SKIPs its own target. A typo then turns the gate green; exit 2, and SKIP only a file another skill creates.
walk(root, { ignore: REPO_SCAN_IGNORES }), a constant built from it, or a hand-rolledreaddirloop for a repo scan. It descends into installed skills and fails on their planted fixtures; usewalkRepo(), and mark a deliberately wider scan// repo-scan-walk: deliberate (<why>).- App facts (screen ids, budgets, folder names) written into a checker. The checker then fits one app; read them from
contracts/<name>.jsonand ship its template. xcrun simctl boot booted,adb shellwithout-s,maestro testwithout--device. Several sessions share one machine; name the device every time.- A full-copy fixture per case. Hundreds of near-identical trees blow the fixture budget and hide what each case plants; write a
mutation.jsonon one base. - An undated "works with SDK 57". Nobody can tell when it stopped being true; date it and give the command that re-checks it.
- Writing the skill straight into
.claude/skills/. Every write there asks the developer; author inskills/and load it as a plugin or link it.
Files in this skill
| File | What it is | Read/run when |
|---|---|---|
| references/authoring-standard.md | The whole standard: placement, self-containment, anatomy, frontmatter, section order, templates, quality bar, every validator and check-skill rule | Workflow step 2, before writing any skill file |
| references/descriptions-and-routing.md | The description formula, good and bad examples, keeping a set apart, routing evals offline and live | Workflow steps 1, 5 and 7 |
| references/writing-scripts.md | The script contract, a complete checker, the check-lib API, layout, contracts, repo scans, devices, packages | Workflow step 4 |
| references/self-tests-and-fixtures.md | runSelftest, case kinds, the overlay format (SUITE.json, mutation.json ops, DELETE.txt), hygiene and budget | Workflow step 4, and when a fixture goes stale |
| references/library-round.md | This skill's commands in order, a skill library's own round (sync, all self-tests, catalogue), the development loop, settings | Workflow step 8 |
| references/how-skills-load.md | Discovery, parsing, listing budget, invocation, compaction, permissions, diagnostics, pitfalls by symptom | Workflow step 9, and when a rule needs its reason |
| examples/new-skill-walkthrough.md | A new skill from scaffold to PASS, and a rule added to an existing checker | Workflow step 2 |
templates/contracts/routing-evals.json |
The repo's routing evals: prompts and the skill each should load, seeded with placeholders to replace | Workflow step 5 |
templates/contracts/routing-evals.schema.json |
JSON Schema of the routing evals contract | Workflow step 5, when editing the evals |
assets/scaffold/ |
What new-skill.mjs copies: SKILL.md.tmpl, a reference, check-rules.mjs, its self-test, overlay fixtures, assets/shared.json |
Workflow step 2 (through the script) |
assets/standard.json |
The standard's constants (description cap, sections, allowed keys, reserved names, ban lists, budgets, device rules) | When a project tightens the standard (--standard <file>) |
scripts/new-skill.mjs |
Creates a skill from the scaffold; refuses bad, reserved or taken names | Workflow step 2 |
scripts/validate-skill.mjs |
The standard's checker for one skill, a skills folder or a repo | Workflow step 6 and the definition of done |
scripts/check-skill.mjs |
Checks one skill is finished (boundaries, reasons, used files, placeholders, template headers, self-test coverage, a bad case per rule, EXPECT ids, SKIP lines, contracts, deprecated helpers, repo scans) | Workflow step 6 and the definition of done |
scripts/check-skill-set.mjs |
Checks the set: overlapping or duplicate descriptions, hand-offs, listing budget (scaled by the repo's skillListingBudgetFraction), average length, index coverage |
Workflow step 7 |
scripts/check-routing.mjs |
Routing evals, offline ranking or live with Claude Code; --prompt alone prints the ranking |
Workflow steps 1 and 7 |
scripts/lib/skill-standard.mjs |
The validator's rule engine | When changing a validator rule |
scripts/lib/frontmatter.mjs |
The strict one-line YAML subset parser | Never by hand |
scripts/lib/skill-md.mjs |
SKILL.md reader and path resolution used by the checkers | Never by hand |
scripts/lib/router.mjs |
The offline ranking used by check-routing | When tuning the offline router |
scripts/lib/live-route.mjs |
Runs claude -p, reads the session's skill list and the first tool call |
Never by hand |
scripts/selftest.mjs |
Proves every script passes its good case and catches every planted bug | After changing a script, the scaffold or a fixture |
scripts/check-lib.mjs |
Shared script helper, synced (do not edit here) | Never by hand |
assets/shared.json |
Declares the shared files this skill copies in | When adding a shared file |
tests/fixtures/ |
Overlay cases: sample skills, skill sets, eval files, scaffold runs | When adding a rule |
Related skills
troubleshooting-playbook- failures in the app or its tooling rather than in a skill.quality-gates- the repo's own gates and hooks, which may run this skill's checkers.git-commits-and-reporting- committing a skill change and reporting it.react-native-conventions-index- the index skill whose table must name every skill.