SKILL 23 / DATA · DEEP-DIVE

Domain-Split Reference Structure

One SKILL.md that holds only navigation · Read the file the task needs · Which layer schemas and metric definitions belong in

2026-09-24 · BigCat

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.

Prerequisite → Skill 22, the data analysis skills roundup (nobody can sell you your metric definitions)

// WHY THIS MATTERS

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.

// 01

What it kills for you: the tax of a whole schema resident in context

Claim: splitting references saves tokens, not disk. The docs say it plainly — a file sitting there costs nothing at all; it costs only once it is read.

Three loading rules the docs pin down

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.

Install: four loading locations, pick one

# 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

The first usage you can run today: copy official Pattern 2

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.

Sources · Anthropic · Skill authoring best practices (Progressive disclosure patterns and Runtime environment) · Claude Code · Skills (the four loading paths); both retrieved 2026-09
// 02

Mechanism: SKILL.md is the table of contents, the filesystem is the index

Claim: whether a split works comes down to whether the routing table says under what condition to read which file. Files split without routing are files that were never split — the model will never open them, and nothing errors.

Four official skill directories, laid side by side

pdf/ SKILL.md (~270 lines) + forms.md + reference.md + scripts/ <- flat, one level deep mcp-builder/ SKILL.md + scripts/ + reference/ mcp_best_practices.md evaluation.md node_mcp_server.md python_mcp_server.md <- one reference dir, 4 files skill-creator/ SKILL.md + references/schemas.md + agents/ assets/ eval-viewer/ scripts/ <- dirs by purpose, not just md claude-api/ SKILL.md + shared/ (28 .md files) + python/ typescript/ java/ go/ ruby/ csharp/ php/ curl/ <- two axes: topic x language docx/ xlsx/ pptx/ web-artifacts-builder/ SKILL.md + scripts/ and nothing else <- never split at all template/ a single SKILL.md <- the starting position
Source · per the directory pages of anthropics/skills · skills/, 2026-09 (19 skill directories in total)

Three ways to route, each readable in the files above

  1. Route by task — the Reading Guide in 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.
  2. Route by condition — 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.
  3. Route by domain — the bigquery example under Pattern 2: finance, sales, product, marketing, and you read the one you were asked about. The definitions problem from the last issue lands exactly in this slot.

Two hard constraints, both aimed at preview-style reads

A real trap: in the repo, 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.
// 03

Real usage and limits: which layer schemas and definitions actually belong in

Claim: these are three different layers — SKILL.md holds navigation plus cross-domain hard rules, 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.

The three layers

Three criteria for choosing among the four shapes

  1. Do the domains get asked about together? If yes, do not split — you would read both files every time and add a hop. If no, split by domain. This is exactly why claude-api keeps shared/ apart from the language directories: someone asking about Python has no use for the eight Ruby files.
  2. Is there a second axis? One axis, topic only, and a flat 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.
  3. Will this file go stale? If yes, date it inside the file. The 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.

Where this stops holding

// 04

Costs and alternatives: every file you split out adds a routing line to maintain

Claim: rename a file, merge two domains, add a category — the moment the routing table falls out of sync, the model is reading a table of contents pointing at thin air, and nothing errors along the way.

Three ledgers

What you can do instead of splitting

  1. Don't split. Run a single file until it approaches 500 lines. The official template/ is still one SKILL.md and nothing else; that is what the starting position looks like.
  2. Put the definitions in CLAUDE.md. The cost is that it is unconditionally resident and every session pays those tokens, so it fits only the two or three rules you genuinely need every time.
  3. Serve schemas over MCP instead of from files. The server answers with the live table structure, so it never goes stale; the cost is a tool round trip per question and one more source of context tax (see Skill 9 and Skill 22).
  4. Give each domain a subagent. Stronger context isolation, higher coordination tax — that one is Skill 26's subject.
The honest cost, last: this structure optimizes tokens, not correctness. A wrong definition inside 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.

// THE ONE-PAGE PICK LIST

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.

// GLOSSARY

progressive disclosure
The three-stage loading model: only name and description stay resident, SKILL.md is read on trigger, everything else on demand. It is what makes "carrying a pile of material" stop meaning "burning a pile of tokens."
SKILL.md
A skill's entry file. Its YAML frontmatter requires name (max 64 characters, lowercase letters, digits and hyphens) and description (max 1,024 characters). The docs recommend keeping the body under 500 lines.
routing table
The lines in SKILL.md that say "under this condition, read that file." The central claim of this issue: with no clear routing table, the files you split out may as well not exist.
one level deep
Every reference links directly from SKILL.md; A linking to B linking to C is not allowed. One of the official anti-patterns.
shared/ and {lang}/
The two-axis split in 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.
metric definition
Rules of the "how exactly do we count an active user" kind. This is domain knowledge, so it belongs in reference/<domain>.md, not in SKILL.md.
bundled resources
Everything in a skill directory other than SKILL.md: markdown, scripts, templates, assets. The official test for whether one is surplus has a single criterion — has it ever actually been accessed.

// FURTHER READING