Nested CLAUDE.md token scope: when a subdirectory file actually costs you.
The advice you have read about subdirectory CLAUDE.md files is half a sentence. "They load on demand, so they save tokens" is true, and it stops exactly where the interesting part starts. On demand is a trigger, not a budget. Once that trigger fires, a nested file behaves nothing like the lightweight thing the guides imply. This page traces one nested file through one session, state by state, with the token count at each, so the word scope means something you can act on.
1. "Scope" means two things here
A nested CLAUDE.md has a spatial scope and a temporal scope, and most write-ups only discuss the first. Spatial scope is which subtree the file governs: a packages/api/CLAUDE.md applies to work under packages/api. Temporal scope is the part that costs money: when its tokens are actually inside the context window.
The memory docs draw the line in one sentence: "CLAUDE.md and CLAUDE.local.md files in the directory hierarchy above the working directory are loaded in full at launch. Files in subdirectories load on demand when Claude reads files in those directories." Walk up the tree from where you launched, and every file is launch cost. Walk down, and every file is deferred cost. Same file format, opposite billing.
In the layout above, three of the four CLAUDE.md files contribute nothing to context when you run claude in the repo root. They are discovered, indexed, and left on disk. The root file is the only one in your per-turn floor from turn one. That is the whole appeal of nesting, and it is real. What follows is the half nobody finishes.
2. The four states of the token bill
Follow one file, packages/api/CLAUDE.md, assume it is 1,800 tokens, and watch what it costs across a single session.
packages/api/CLAUDE.md, one session
State 0 — Launch
Claude walks up from your working directory and loads every CLAUDE.md above it, in full. A CLAUDE.md sitting below your working directory is discovered but not loaded. The nested file contributes nothing yet.
State 1 — First touch
Claude reads packages/api/server.ts. That single file read is the trigger. packages/api/CLAUDE.md is now expanded and injected, in full, along with any @import targets it references. The 1,800 tokens land in context in one step.
State 2 — Resident
An injected nested CLAUDE.md is now an ordinary part of the conversation. It does not unload when Claude edits a file in packages/web or anywhere else. Loading is on demand; unloading is not. It rides every following turn at full price.
State 3 — /compact
Compaction rebuilds the context. The project-root CLAUDE.md is re-read from disk and re-injected. The nested file is not. Its token cost, and its instructions, drop out of the session.
State 4 — Reload
The nested file returns only the next time Claude reads a file under packages/api. Until that read happens, both the 1,800 tokens and the rules in that file are gone, and Claude is working without them.
Read States 1 through 4 as a loop, not a line. A session that bounces between packages/api and packages/web with two /compact calls in it can load each nested file three separate times. The deferral the guides promise is real for the turns before State 1. It does nothing for the turns after, and it resets every time you compact.
3. The state every other guide skips
The pages that currently rank for this topic stop at State 1. Subdirectory files load on demand, the reasoning goes, therefore they save context, therefore put more of your instructions in nested files. State 3 is where that advice quietly breaks, and Claude Code's own docs spell it out in the troubleshooting section, under "Instructions seem lost after /compact."
"Project-root CLAUDE.md survives compaction: after /compact, Claude re-reads it from disk and re-injects it into the session. Nested CLAUDE.md files in subdirectories are not re-injected automatically; they reload the next time Claude reads a file in that subdirectory."
Claude Code memory docs, "Instructions seem lost after /compact"
Two consequences fall straight out of that paragraph. The first is a cost consequence: a nested file is not a one-time spend deferred, it is a recurring spend. Every /compact resets it to zero, and every re-entry into its subtree re-bills the whole file. On a long session that is the same 1,800 tokens loaded and paid for two or three times over.
The second is a correctness consequence, and it is the one that bites harder. The docs continue: "If an instruction disappeared after compaction, it was either given only in conversation or lives in a nested CLAUDE.md that hasn't reloaded yet." So a rule you wrote into a subdirectory file can be silently absent for a stretch of turns after a compaction, while Claude keeps editing files in that exact directory through any path that is not a fresh file read. The token scope and the instruction scope move together. When the tokens are gone, so are the rules.
4. See the scope in your own session
You do not have to take the four states on faith. Two built-in tools show them. /memory lists every CLAUDE.md, CLAUDE.local.md, and rules file loaded right now, a snapshot. For the timing, the docs point at the InstructionsLoaded hook, which "logs exactly which instruction files are loaded, when they load, and why," and they recommend it specifically for debugging lazy-loaded files in subdirectories. Here is the shape of what it records across the session from Section 2.
The three lines that matter are the launch line (the nested file discovered, not loaded), the first-read line (loaded on a file read, not on a directory change or a glob), and the /compact pair (root re-injected, nested not). If you have ever wondered why a subdirectory rule worked for the first hour and then stopped, that last pair is the answer, and the hook log is where you would have caught it.
5. Root file versus nested file, by token behavior
The two files look identical on disk. They are billed on opposite rules. This is the table to keep in your head when you decide what goes in the root file and what goes in a subdirectory.
| Feature | Project-root CLAUDE.md | Nested CLAUDE.md |
|---|---|---|
| Loads at launch | Yes, in full | No, discovered but deferred |
| In your per-turn floor from turn one | Yes | No, only after the first touch |
| Token cost before Claude reads its subtree | Full | Zero |
| Re-injected automatically after /compact | Yes, re-read from disk | No, reloads on the next subtree read |
| Can drop out of context mid-session | No | Yes, after every /compact until re-triggered |
Both are loaded as context, not enforced configuration. Loading a file is not the same as Claude following it.
The decision the table drives: put an instruction in a nested file when it is genuinely irrelevant outside that subtree and the cost of it being briefly absent after a /compact is low. Keep an instruction in the root file when it must hold on every turn no matter where Claude is working. Nesting is a scoping tool, not a free token discount. For the launch-time arithmetic of the files that are not nested, the layered cost guide walks the whole stack.
6. Reading the analyzer number for a nested file
Paste a subdirectory CLAUDE.md into the analyzer on ccmd.dev and you get a token count and a per-turn estimate, same as for any file. The estimate is built on one assumption, and for a nested file you have to read it correctly. Here is the line.
The comment says it plainly: assume CLAUDE.md fires every turn. For a project-root file that assumption is literal. For a nested file it is not. A nested file does not fire every turn of the session, it fires every turn within its in-scope window: from State 1 to the next /compact.
So treat the analyzer's estimatedTokensFireEveryTurn for a subdirectory file as the in-scope ceiling, the cost while Claude is working inside that subtree, and treat the 30-turn dollar figure as the worst case where Claude never leaves the subtree and never compacts. The real session cost sits below that ceiling and depends on your navigation, not on the file. That is also the actionable takeaway: keep nested files short, not because short files save the session, but because once loaded a nested file costs full price every turn it is in scope, and a long session can re-bill it after every compaction. A 200-line nested file loaded three times is paid for three times.
Want a real per-turn number for a nested-heavy repo?
15 minutes, free. We paste through your root file and your subdirectory files together and map the in-scope cost of each one.
Frequently asked questions
When does a nested CLAUDE.md actually cost tokens?
Zero tokens at launch. The full file the first time Claude reads any file inside that subdirectory. Resident, at full cost, on every turn after that. Zero again after /compact, until Claude next reads a file in the subtree. Its token scope is conditional: it depends on where Claude has been working and whether the session has compacted since.
Do nested CLAUDE.md files lower my token cost?
Only by deferral. A nested file costs nothing until Claude touches its subtree, so a session that never enters packages/api/ never pays for packages/api/CLAUDE.md. But it does not cost less once loaded. A 1,800-token nested file is 1,800 tokens a turn while it is in scope, exactly as if those lines sat in the root file. The saving is the turns before the first touch, not a discount on the file.
Does a nested CLAUDE.md leave context when Claude moves to another directory?
No. Loading is on demand; unloading is not. Once a subdirectory CLAUDE.md is injected it becomes an ordinary part of the conversation and rides every following turn, even while Claude edits files in completely unrelated directories. The only things that remove it are /compact and the end of the session. Lazy load is not lazy unload.
Why did my subdirectory instructions stop working after /compact?
Because the nested file was dropped at compaction and has not reloaded yet. Claude Code's memory docs are explicit: 'Project-root CLAUDE.md survives compaction: after /compact, Claude re-reads it from disk and re-injects it into the session. Nested CLAUDE.md files in subdirectories are not re-injected automatically; they reload the next time Claude reads a file in that subdirectory.' The instructions return the next time Claude reads a file in that subtree.
How do I see which nested CLAUDE.md files are loaded right now?
Run /memory in a session. It lists every CLAUDE.md, CLAUDE.local.md, and rules file loaded in the current session. For timing rather than a snapshot, the InstructionsLoaded hook logs exactly which instruction files load, when, and why. The docs call it out specifically for debugging lazy-loaded files in subdirectories, because a snapshot alone will not tell you a nested file was dropped at the last /compact.
How does ccmd's analyzer treat a nested CLAUDE.md?
It scores the file's content the same way it scores any agent-config file. The catch is one line: analyzer.ts sets estimatedTokensFireEveryTurn to the file's full token count. For a project-root file that is literal. For a nested file, read that number as the in-scope ceiling, the per-turn cost while Claude is working inside that subtree, not the session average. The 30-turn cost figure is the worst case where Claude never leaves the subtree and never compacts.
Related: the layered cost of every CLAUDE.md loaded at launch, which rules fire on every turn, or paste a file on the analyzer.
Comments (••)
Leave a comment to see what others are saying.Public and anonymous. No signup.