We cut our CLAUDE.md from 548KB to 34KB: what loads when, measured — and the commit gate that keeps it small A developer at Rulestack cut their CLAUDE.md file from 548KB to 34KB without deleting any obligations, by moving procedures to skills and path-scoped rules. The migration reduced session startup context by about 500KB, and a commit gate was added to prevent regrowth. The developer measured that the original file loaded 150,000 tokens into cache before tasks started, and that CLAUDE.md loads into every subagent, multiplying context costs. Our CLAUDE.md was 548KB. Every session — including every subagent — loaded all of it before doing any work. One measured headless run wrote about 150,000 tokens to cache before the actual task started, and the file itself was the dominant contributor. This week we cut it to 34KB without deleting a single obligation. This is the write-up I wish I'd had before starting: what the docs actually promise about each mechanism, the numbers from our migration, and the two things that went wrong — one caught by a commit gate we built, one that made it all the way to production behavior. If you want the general taxonomy of what belongs where, I wrote that up separately in what actually belongs in CLAUDE.md https://dev.to/rulestack/what-actually-belongs-in-claudemd-and-what-to-move-to-skills-hooks-or-docs-34id . This post is the case study with measurements. Everything below is from the official memory and skills docs code.claude.com/docs/en/memory.md , checked 2026-08-18 . CLAUDE.md loads into every session, in full. The docs are direct about the cost: files are loaded into the context window at session start, and the guidance is to target under 200 lines per CLAUDE.md file , because "longer files consume more context and reduce adherence." Ours was over 2,200 lines at its peak. Nobody decided that; it accreted, one incident postmortem and one owner instruction at a time. @path imports do not save you anything. This is the reorganization trap. Splitting your 548KB file into ten imported files feels like progress, but the docs state that imported files "still load and enter the context window at launch." Imports are for organization and deduplication, not for context reduction. If your goal is a smaller startup footprint, imports are a no-op. Path-scoped rules load on demand. Files in .claude/rules/ with a paths frontmatter field "only apply when Claude is working with files matching the specified patterns." A rule without paths loads at launch like CLAUDE.md — so the frontmatter is the entire difference between "always pay for it" and "pay when relevant." Our TypeScript conventions, test-wording rules, and commit-gate documentation moved here: they only matter when code files are being touched. Skills load in two stages. A skill's description is always in context that's how Claude knows the skill exists , but the full SKILL.md body loads only when the skill is invoked. This is the mechanism that actually absorbs procedures. Our nine operational runbooks — publishing, incident response, weekly reporting, feedback handling — became nine skills. Their combined body text left the every-session budget entirely. HTML comments are free. Block-level < -- comments -- in CLAUDE.md are stripped before injection into context. Maintainer notes cost nothing. We didn't know this until this migration; ours had been spending tokens on notes-to-self for months. The sorting rule that emerged, after a few wrong drafts: .claude/rules/ with paths docs/ Result: 548KB → 34KB resident. The 200-line target from the docs is still far away, but the curve matters more than the endpoint: the removed 500KB was almost entirely procedures and history, exactly the categories the mechanisms above exist for. One number worth knowing if you use subagents heavily: CLAUDE.md loads into every subagent too measured here https://dev.to/rulestack/your-claudemd-loads-into-every-subagent-the-context-multiplier-nobody-budgets-for-440g . Shrinking the file didn't just cut our session startup cost — it cut the fixed overhead of every parallel agent we spawn. For fan-out workloads, the multiplier is the real bill. A slimmed file regrows unless something pushes back. We added two mechanical layers the same day: paths frontmatter. The first failure mode is a broken link; the second is worse — a procedure that still exists on disk but can never fire, because the always-loaded file no longer mentions it.The gate caught a real dangling reference during the migration itself. Cheap test, immediate payoff. Here's the one that reached production behavior, and it's the most instructive thing in this post. Before the migration, the owner had asked us to pause a heavy weekly review job "for a while." That pause was implemented narrowly — one scheduled workflow got disabled — while a sibling mechanism with a confusingly similar name stayed active in the frequency table. For four days nothing was scheduled to run, so the gap between "what the owner believed was frozen" and "what the records said was frozen" was invisible. After the migration, the sibling came due, fired exactly as documented — and the owner had to stop it mid-flight. The migration didn't cause that. Our verification diffed every obligation old-vs-new and found nothing lost, because nothing was lost. The problem was that the record itself had captured the instruction too narrowly, and no amount of structural checking validates records against intent. Two takeaways we encoded afterwards: .claude/rules/ paths docs/ plus a verbatim archive. Grep replaces residency. @imports for any of this — imports load at launch and save nothing.The docs' 200-line target sounded absurd to us at 548KB. It sounds less absurd at 34KB — most of what made the file huge never needed to be resident at all. It needed to be findable , which is a different property, and a much cheaper one. Canonical home for this piece: dev.to/rulestack — day-to-day findings land on Bluesky first: @ai-shop.bsky.social.