We cut CLAUDE.md to 33KB. Eighteen days later it was 45KB again: what a size cap missed and a structure test caught A developer's agent instruction file, CLAUDE.md, was cut from 548KB to 33KB on 2026-08-18 and given a 45KB size cap, but by 2026-09-05 it had grown back to 44,992 bytes, just eight bytes under the limit. Measurement of the commits showed two owner-instruction sections accounted for 11.1KB of the 13.1KB body increase, and the agent kept the total legal by abbreviating its own changelog entry rather than trimming rules. The developer replaced the size cap with a structure test and moved overflow rules into skill files, dropping the body to 34,235 bytes. We cut our agent's CLAUDE.md from 548KB to 33KB on 2026-08-18 and put a 45KB cap on it. Eighteen days later the file was 45KB again. The cap had watched the total, so the file stayed legal by squeezing its changelog while the rules body grew by 13KB. A test that checks the file's structure, not its size, is what finally held. This is a measurement post. The numbers come from git show on each commit of the file, and the method is simple enough that you can run it on your own instruction file tonight. Our agent runs a small digital shop, and its CLAUDE.md had grown for months into a 548KB document that loaded at the start of every session. On 2026-08-18 we split it: procedures moved to nine skill files that load only when a task matches, coding rules moved to a path-scoped rules file, glossary and design decisions moved to docs/ . The body kept only the current rules and a table of triggers pointing at the skills. The evening commit that day, version 3.150, was 33,070 bytes in total, with a body of 27,646 bytes across 272 lines. The same change added a health check, claudemd-size , that warns at 45KB and alerts at 60KB. That was the whole enforcement. The reasoning was that 45KB left room for the footer, which by our own rule holds the current version's changelog entry plus the two before it, roughly 10KB. The commit on 2026-09-05, version 3.207, was 44,992 bytes. Eight bytes under the cap. The body was 40,779 bytes across 290 lines. Here is where the growth went, measured by section header, comparing the 08-18 evening commit to the 09-05 one: | section | 08-18 | 09-05 | delta | |---|---|---|---| | §4 things the agent must not do | 3,006 B | 9,017 B | +6,011 B | | §8 the per-turn decision flow | 12,051 B | 17,140 B | +5,089 B | | §14 development rules | 1,100 B | 1,936 B | +836 B | | §2 architecture overview | 1,199 B | 2,007 B | +808 B | | §9 quality checklist | 262 B | 526 B | +264 B | | §17 emergency contacts | 650 B | 775 B | +125 B | | everything else | | | +0 B | Two sections account for 11.1KB of a 13.1KB body increase. Both are the sections where owner instructions land. Every time the owner said "from now on, do X" or "never do Y without asking," the agent wrote X or Y into §4 or §8, with the date, the exemption conditions, and the names of the CLIs involved. Each addition was correct. Nothing was deleted, because nothing had become wrong. The longest line tells the same story in one number. On 08-18 the longest line in the body was 616 bytes. On 09-05 it was 1,773 bytes, 933 characters, a single bullet in §4 about which replies need the owner's approval, with its exemptions, its ledger names, and the CLIs that enforce it, all inline. The total on 09-05 was 44,992 bytes, under 45,000. The body alone was 40,779. That leaves 4.2KB for a footer that our rule budgets at around 10KB for three full changelog entries. The arithmetic does not work, and the way it was made to work is the actual finding: the 3.207 commit wrote its own changelog entry in an abbreviated form, 1,350 bytes, to get the total under the cap. When the entry was restored the next day it was 2,357 bytes. So the cap did fire, in the sense that the agent saw it coming. But a cap on the sum of two parts lets you satisfy it by shrinking whichever part is easier to shrink, and the changelog, being history, is always easier to shrink than rules, which someone will notice missing. The mechanism that was meant to keep the rules section small ended up compressing the record of why the rules exist. The owner's question that triggered the audit was, roughly, "I thought we moved this to skills, why is it big again?" The fix, version 3.208, moved the overflow out of the body: the approval rules went to the feedback skill in full, the fetch commands in §8 went to the same skill, the verification procedure went to the review skill, and the body kept one summary line and a trigger for each. The body dropped to 34,235 bytes and 267 lines, and the abbreviated changelog entry was restored in full. Then we replaced the size cap with a test that knows what the file is made of. It runs as part of the commit gate and reads the real file. The checks, in the order they appear: js export const CLAUDE MD FOOTER MARKER = ' 最終更新 ' // body ends where the footer begins export const CLAUDE MD BODY MAX BYTES = 35 000 export const CLAUDE MD BODY MAX LINES = 270 export const CLAUDE MD MAX LINE CHARS = 400 const HISTORY ANNOTATION PATTERNS = /オーナー ?:指示|承認|指摘|確認 \s 20\d\d-\d\d-\d\d/g, // "owner instruction 2026-09-05" /v3\.\d+ ? \d|\.md /g, // "in v3.201 we ..." Body size, body lines, line length. The body is measured separately from the footer, so the footer cannot be used as a buffer. The 35KB figure is the old 45KB total minus the 10KB the footer is entitled to. The 270-line figure is the measured body at the time plus a little room. The 400-character line limit is the direct response to the 1,773-byte bullet: one line is one rule, and the details go to a skill. No history in the body. Dated owner instructions and version references are the two forms the creep took. Both are useful and both belong in the changelog, so the test fails if either pattern appears above the footer marker. The archive file's name contains a version number and is excluded explicitly. Footer pruning. The footer must hold exactly the current version and the two before it, consecutive. The version just older than the oldest one in the footer must exist as an entry in docs/CHANGELOG.md . This catches the other way the same pressure could have been relieved, dropping an entry outright. The abbreviation that actually happened is not something a regex can judge; what stops it is the body cap upstream, which removes the pressure to squeeze the footer at all. Referential integrity , which predates this incident: every skill the body names must exist with a SKILL.md; every skill that exists must be reachable from the body's trigger table; every SKILL.md must have a non-empty description; every rules file must declare the paths it applies to. Each check has a positive-control unit test, and before merging we planted violations in the real file and confirmed that four of the checks went red against it. A reader of an earlier post on this file asked whether agent instruction files should eventually be treated less like documentation and more like executable configuration, with schemas, validation, ownership, and automated linting. Our answer, after this incident, is that the structure test is the schema, and it is worth being precise about what a schema can and cannot see. The documentation for the tool that reads this file is explicit that CLAUDE.md is context, not enforced configuration: "Claude treats them as context, not enforced configuration. To block an action regardless of what Claude decides, use a PreToolUse hook instead." So the file is prose that a model reads, and no schema will make a rule binding. What a schema can do is constrain the shape the prose is allowed to take. Ours says: a body no larger than this, no line longer than this, no history above this marker, every reference resolvable, every referenced thing reachable. That is a schema in the ordinary sense, and it caught the failure mode we had. What it cannot see is meaning. Two rules that contradict each other, a rule that duplicates another in different words, a rule that is simply wrong: all of these pass. The same reader suggested a CI check for contradictions, and we do not have one, and I do not think a regex gets there. The closest we have is the rule that a change to a policy must delete the old text rather than append beside it, and that rule is enforced by nothing but review. The other half of the reader's list, ownership and provenance, we get from the changelog discipline the test now protects: every owner-originated rule has a dated entry saying who asked for it and why, and the test guarantees the entry cannot silently vanish. That is provenance without a schema for it, kept honest by a schema for where it lives. Today the body is 34,977 bytes on the 35,000 cap, 269 lines on 270, longest line 392 characters on 400. It is legal by 23 bytes and one line. That is not a comfortable margin, and it is the honest state: the test is binding right now, and the next owner instruction will force a move to a skill rather than an append. Whether that is a feature or a source of friction, we will know in a few weeks. The size guidance we ignored at the split, that a CLAUDE.md should target under 200 lines, is still on the vendor's page, and we are 69 lines over it by design, because the trigger table is what makes the skills load at all. The structure test and the file it guards run Rulestack https://rulestack.gumroad.com?utm source=devto&utm medium=article&utm campaign=we-cut-claudemd-to-33kb-eighteen-days-later-it-was-45kb-again-what-a-size-cap-missed-and-a-3ha8 , a shop where the agent that grew the file is the one that has to keep it under the cap. What the commit gate catches each week goes out from @ai-shop.bsky.social https://bsky.app/profile/ai-shop.bsky.social .