Last issue ended on this: nobody can sell you your metric definitions, you have to write them yourself. This issue answers what comes next — where to put them. Four real official skill directories side by side, from "never split at all" to "a two-axis matrix," and where the lines between them fall.
Once the definitions and schemas are written down, a very practical problem follows: where do they live. Put them in SKILL.md and a single warehouse schema can push it past a thousand lines, all of which loads on every trigger. Put them in CLAUDE.md and it is worse, because that is unconditionally resident and every session pays for it. The official answer is to split by domain and leave SKILL.md holding nothing but a routing table. This issue starts from the artifacts rather than the theory: four real directories in anthropics/skills — pdf with two flat files, mcp-builder with one reference/, skill-creator with a references/, and claude-api built as an outright two-axis matrix — and only then how to choose among them.
The Runtime environment section of Skill authoring best practices lists three, and they are the entire economic case for this structure: (1) Metadata pre-loaded — at startup only every skill's name and description enter the system prompt; (2) Files read on-demand — SKILL.md is read once the skill is relevant, everything else only as needed; (3) No context penalty for large files, verbatim: Reference files, data, or documentation don't consume context tokens until actually read.
# Personal: visible to every project on this machine
~/.claude/skills/<skill-name>/SKILL.md
# Project: travels with the repo, can be committed (use this to share definitions)
.claude/skills/<skill-name>/SKILL.md
# Plugin: invoked namespaced, as /plugin-name:skill-name
<plugin>/skills/<skill-name>/SKILL.md
# Nested: loads only in sessions started in or below <subdir>
<subdir>/.claude/skills/<skill-name>/SKILL.md
# To read the shipped ones first (all 19 official skills are in here)
/plugin marketplace add anthropics/skills
/plugin install example-skills@anthropic-agent-skills
Pattern 2: Domain-specific organization in the best-practices page ships a bigquery-skill skeleton you can use as-is: a SKILL.md plus a reference/ holding finance.md, sales.md, product.md and marketing.md. The SKILL.md body carries three things only — one line on what the skill covers, a routing table by domain, and a grep hint:
mkdir -p .claude/skills/warehouse/reference
# The routing table inside SKILL.md, exactly as the docs write it:
**Finance**: Revenue, ARR, billing → See [reference/finance.md](reference/finance.md)
**Sales**: Opportunities, pipeline, accounts → See [reference/sales.md](reference/sales.md)
# Plus a quick-search hint so it can fish for itself:
grep -i "revenue" reference/finance.md
Metric rules go into the matching domain file, not into SKILL.md — the docs' own example is a rule like "always exclude test accounts," which lands in whichever domain owns it. When not to split: SKILL.md is still short of 500 lines, there is only one domain, or the domains are tightly coupled (asking about A always requires B) — splitting then only adds a hop. The official line is hard: Keep SKILL.md body under 500 lines... Split content into separate files when approaching this limit.
claude-api: start at {lang}/claude-api/README.md, then add per task — a chat UI adds streaming.md, tool use adds shared/tool-use-concepts.md plus {lang}/claude-api/tool-use.md, caching reads shared/prompt-caching.md, cost work reads shared/cost-optimization.md.mcp-builder: the same step forks, TypeScript reading ./reference/node_mcp_server.md and Python reading ./reference/python_mcp_server.md, while mcp_best_practices.md and evaluation.md are read unconditionally. The value of conditional routing is that the file you should not read never even gets named.head -100 rather than reading the whole file, and the result is incomplete information. Every reference should link directly from SKILL.md.skills/pdf holds lowercase forms.md and reference.md, while its SKILL.md body says "read FORMS.md" and "see REFERENCE.md." On the default macOS filesystem, case-insensitive, it works. In a Linux container those are two different names. When you split your own references, copy the paths in your routing lines out of ls output instead of typing them.reference/<domain>.md holds schemas and definitions, scripts/ holds whatever a machine can check. Put something in the wrong layer and you either pay tokens for nothing or never read what mattered.reference/<domain>.md (read on demand): table and field names, metric definitions, the edges of each definition, common query templates. skill-creator pulls schemas out into references/schemas.md, which is the official demonstration of this slot.scripts/ (executed, never loaded): anything that can be a check should not be a reminder. Verbatim: Only the script's output consumes tokens. docx turns XSD validation, run merging and accepting tracked changes into scripts under scripts/ and splits out no .md at all.claude-api keeps shared/ apart from the language directories: someone asking about Python has no use for the eight Ruby files.reference/ is enough — mcp-builder tops out at four files. Two axes present, topic by language or topic by environment, and only then is a claude-api-style matrix worth it. The cost of two axes is that the routing table has to be written with placeholders like {lang}/xxx.md, which is harder to read and harder still to change.claude-api SKILL.md marks its model and pricing table as of 2026-06-24, and that is the single smallest habit worth stealing from this issue: let volatile content declare its own shelf life instead of making the reader guess.docx, xlsx, pptx and web-artifacts-builder are to this day SKILL.md plus scripts/, with no .md split out of any of them. Do not split because "the official ones split" — most of the official ones do not.grep -i "revenue" reference/finance.md; with no shell it degrades to reading whole files.scripts/ depends on has to be there already. Only claude.ai can pull from npm, PyPI and GitHub.FORMS.md versus forms.md case above is this ledger in the wild.head -100, so a deep chain plus a long file with no contents equals half a schema — and half a schema is more dangerous than none, because half a definition triggers no suspicion.template/ is still one SKILL.md and nothing else; that is what the starting position looks like.reference/finance.md will only be caught later under on-demand loading, because you do not even know whether it was read. Before you split references, make sure what is in those files is right.Read these three first: (1) the Progressive disclosure patterns section of the official best-practices page — Patterns 1/2/3, one level deep, the 100-line contents rule; nearly every rule in this issue comes from there. (2) the SKILL.md of skills/claude-api — see how one Reading Guide compresses 28 shared/ files and eight language directories into a single table. (3) skills/mcp-builder, its SKILL.md plus reference/ — four files, the model answer for the smallest useful domain split.
Then as needed: skills/pdf (what two flat md files look like) · skills/skill-creator (how references/schemas.md divides work with agents/, assets/, scripts/) · skills/docx and skills/xlsx (counterexamples: heavy without splitting) · template/SKILL.md (the starting position) · code.claude.com/docs/en/skills (the four loading paths).
Explicitly don't: split because "the official ones do it" (most of the 19 official skills do not) · write a whole schema into SKILL.md · nest references more than one level · hand-type routing paths without checking their case · finish a split without dating the volatile files.
name (max 64 characters, lowercase letters, digits and hyphens) and description (max 1,024 characters). The docs recommend keeping the body under 500 lines.claude-api: language-independent concepts live in shared/, language-bound material in each language directory, and the routing table addresses both with a {lang} placeholder.reference/<domain>.md, not in SKILL.md.shared/ plus eight language directories; a model table that carries its own as of 2026-06-24 stampreference/: mcp_best_practices.md and evaluation.md read unconditionally, node_mcp_server.md / python_mcp_server.md read per languagereferences/schemas.md isolates the schema; agents/, assets/, eval-viewer/ and scripts/ divide the rest by purposeforms.md, reference.md and scripts/; note the case mismatch between the FORMS.md / REFERENCE.md written in the body and the lowercase files on diskscripts/ only, with every deterministic operation scriptedtemplate/ directory still holds only this one file