2.1.x 路 macOSThe four layers#
Claude Code has four extension points, and they answer four different questions:
| Layer | Question it answers | Who decides it runs |
|---|---|---|
CLAUDE.md | What should the model always know here? | Nobody; it is always in context |
| Hook | What must happen every time, no exceptions? | Your shell script, deterministically |
| Skill | How do we do X around here? | The model, when it judges X is relevant |
| Subagent | What deserves its own context, tools, or model? | The model delegates, or you ask it to |
The column that matters is the last one. Every misplacement I have made comes down to ignoring who actually decides whether the thing runs.
Hooks: when it must happen every time#
A hook is a script that fires on an event: session start, before a tool call, after one. No model judgment anywhere in the path. That makes it the only layer that can make promises.
My test case was session naming. Part 2 of this series covers the hook itself; the short version is that I wanted every session titled by project and branch so I could find it again. As a habit, it decayed. As a prompt instruction, it worked most of the time, and “most of the time” is exactly the property that makes a pile of unnamed sessions. As a SessionStart hook, it has run on every session since, including the ones launched over SSH and from tools that never show me a prompt at all.
The rule that fell out of this: if you would be upset by one missed execution, it cannot rely on model judgment. Naming, notifications, permission gates, lint-before-commit. Hook territory, all of it.
Skills: when it is knowledge, not a guarantee#
A skill is a folder of instructions the model loads when the task looks relevant. It is the right home for how-to knowledge that would bloat CLAUDE.md if it sat in context permanently.
The clearest example in my setup is this blog. Writing conventions, shortcode rules, a sourcing policy, a pre-publish checklist: around two hundred lines. They matter only when writing a post, which is a small fraction of my sessions. As a skill, those lines cost nothing until the moment they are needed. My dotfiles repo went through the same migration; its CLAUDE.md keeps a short list of hard rules and points at six skills for the detailed workflows.
The trap runs the other way. A skill triggers on the model’s judgment of relevance, which means a skill is a suggestion with good packaging. When I catch myself writing MUST in a skill description, that is usually the tell that the thing belongs in a hook instead.
Subagents: when the work needs its own context, or a cheaper model#
A subagent runs in a separate context window, with its own tool permissions and, since this is the part that changed my usage bill, its own model.
I run the main loop on the most capable model available and kept burning through its usage limits, because that model was also doing the grep runs, the batch edits, and the checklist verification. So I defined three agents in ~/.claude/agents/: a bulk worker and a scout on a mid-tier model, and a verifier on the cheapest one. Per Anthropic’s model pricing, the cheapest tier costs a tenth of the top one per token. An eight-point pre-publish check on this very post ran on the cheap verifier; the transcript that check produced never touched my main context window either, which is the second, quieter benefit.
One gotcha from setting this up: agent definitions register when a session starts. I created the files, dispatched work to them in the same session, and got “agent type not found.” Restart, and they were there.
The decision, as a flowchart#
flowchart TD
Q1{"Must it happen
every single time?"} -- yes --> H["Hook
(deterministic script)"]
Q1 -- no --> Q2{"Is it needed in
every session?"}
Q2 -- yes --> C["CLAUDE.md
(always in context)"]
Q2 -- no --> Q3{"Is it knowledge the model
should load on demand?"}
Q3 -- yes --> S["Skill
(loaded when relevant)"]
Q3 -- no --> Q4{"Does it need isolated context,
different tools, or a cheaper model?"}
Q4 -- yes --> A["Subagent
(own context and model)"]
Q4 -- no --> P["Just put it in the prompt"]
Worked examples from my own config, one per branch: claude-name-session had to happen every time, so it is a hook. “Use yadm, not git” applies to every dotfiles session, so it lives in that repo’s CLAUDE.md (memory docs). Blog conventions load on demand, so they are a skill. Checklist verification wants a cheap model and a disposable context, so it is a subagent. And a one-off “rename this variable” needs none of the above.
Lessons#
- The layer question is “who decides this runs,” not “where does config go.” Hooks decide with code, skills and delegation decide with model judgment,
CLAUDE.mdnever decides at all. - If one missed execution would annoy you, it is a hook. Writing MUST in a skill description is the smell that you picked the wrong layer.
- Skills are for knowledge that is expensive to carry and cheap to load. If it applies to every session, promote it to
CLAUDE.md; if it applies to one task family, keep it a skill. - Subagents are a pricing feature as much as a context feature. Route mechanical work to cheap models and keep the expensive context for judgment.
- Agent definitions load at session start. Create, restart, then dispatch.
References#
- Claude Code hooks documentation
- Claude Code skills documentation
- Claude Code subagents documentation
- Claude Code memory (CLAUDE.md) documentation
- Anthropic model pricing
- Configs referenced:
claude-name-session(SessionStart hook) and the skills layout in my dotfiles;~/.claude/agents/fleet created and tested on this machine
