A subagent in Claude Code is supposed to answer a one-line question about the tool’s documentation. It aborts before taking its first step: Prompt is too long, 347,814 tokens against a limit of 200,000. The question itself had loaded nothing. What had loaded were the project instructions that every session in this repository receives at start. The subagent ran on a model with a context window of 200,000 tokens, and the request that was meant to start it was already almost twice the size of that window. The main session that had started it ran with a window of one million tokens. It did not abort, it was half full before anyone had typed anything.
This article describes how these project instructions in the private DI² project could grow to that size. It shows what a measurement with /context found and what three levers achieved that were applied afterwards. The mechanics and all numbers apply to Claude Code as of October 15, 2026. The measurement was taken with version 2.1.145, the subagent’s failure comes from a session with version 2.1.292. The cause, however, is not limited to Claude Code. It affects every tool that hands an agent project rules at start. In such rule files, two kinds of text accumulate over time: instructions for the next step, say where a new test file belongs, and evidence of past steps, say which test file secures which bug. This article calls the first a rule and the second a record. Both belong in the repository. Into the context that every session loads at start belongs only the rule, because only the rule is needed for the next step.
The essentials up front:
- Project rules are a fixed cost of every session: Everything in the project’s
CLAUDE.md, its imports and the rule files without a file scope loads in Claude Code at start, before the first prompt arrives. In the DI² repository that was a measured 454,900 tokens on October 15, 2026, just under half of a one-million-token context window and more than twice a 200,000-token window. - The dividing line is rule versus record, not long versus short: A rule says where a file belongs and which pattern it uses, a record proves what was checked back then and why. After a good six months of maintenance, the largest rule file consisted to 84 percent of an inventory table with one row per test file. Nobody needs it while writing a test, yet every session paid for it at start.
- Three levers, measured before and after: Bind rules with a file scope to paths, move inventories into a neighboring file, and phrase instructions so that the agent looks things up instead of preloading everything. Afterwards the project instructions stood at 64,700 instead of 454,900 tokens, and the subagent that had aborted before started again.
- Binding to paths has a limit: A bound rule arrives only with the result of the tool call, and for a file that is just being created, that is too late. Three safety nets help against that, and without a guardrail in CI the growth comes back.
Prerequisite: Claude Code basics are explained briefly where they are needed. All token numbers come from /context in a fresh session, with date and version. Where a number is only estimated or extrapolated, it says so.
Contents
- What a Session Costs Before Anyone Types
- Grown With the Work
- Rule or Record: The 84 Percent Case
- What Gets Added While Working
- Three Levers, and What They Cost
- Before and After: 454,900 to 64,700 Tokens
- Without a Guardrail, the Growth Comes Back
- What the Solution Is Not
- Summary
- FAQ
- Related Articles
What a Session Costs Before Anyone Types
At the start of a session, Claude Code reads three kinds of project instructions. The project’s CLAUDE.md in the root directory of the repository. Everything this file pulls in via @ import, up to four levels deep. And every rule file under .claude/rules/ that carries no paths: frontmatter. How a repository ends up with 30 such rule files and what is in them is described in 27 Rule Files Instead of One CLAUDE.md. The point here is that these three items sit in the context before the first prompt is typed, and they do so in every session, regardless of whether the task takes one line or a whole day.
How much that is, the /context command shows. It lists every loaded instruction file individually with a token count that the tool determines itself, rounded to the hundred. The count depends on the session’s model: A probe counted the same files noticeably lower under Haiku 4.5 than under Opus 4.7. The reason is the tokenizer: Models from Claude 4.7 onwards use a newer one that, according to the documentation, counts the same text at roughly 30 percent more tokens than the older one, somewhat more or less depending on the text. It also depends on the content, per file between 1.9 and 2.2 bytes sat on one token, and it is reproducible for the same state and the same model. For a repeatable measurement, a start without the interactive interface is enough, with /context as the only input:
claude -p "/context"
In the DI² repository, this gave the following on October 15, 2026, with the Opus 5.5 model, before any change:
| Item | Tokens |
|---|---|
CLAUDE.md, its imports and 27 rule files without paths: | 454,900 |
| Auto-memory (user-local memory file, not in the repository) | 4,700 |
| System prompt, tools, skills, agents | 27,900 |
| Occupied before the first prompt | 487,500 |
The first row is the fixed cost this article talks about: the project instructions that load at every start and that the project itself controls. The two other items come on top but do not depend on the repository.
Which window this number fills depends on the model. The main session ran on Fable 5.1 and Opus 5. Both have a window of one million tokens in Claude Code, and both count text like Opus 5.5: A probe with the same two files gave the same values under all three models. There, 487,500 tokens meant 49 percent occupied before anyone had typed anything. For a model with a 200,000-token window, it is 244 percent. After deducting the reserve of 33,000 tokens that Claude Code, in the measured version 2.1.145, keeps free for automatic compaction, nothing is left there, and that is the kind of model the subagent from the opening of this article ran on. Its error message named 347,814 tokens, not 487,500, because the two do not count the same thing: The message counts the subagent’s request with the Haiku 4.5 tokenizer, /context counts the main session’s start with the Opus 5.5 tokenizer. This article therefore compares only within /context with the same model, the error message proves the yes or no. Three files carried just under half: the test rule with 124,000 tokens, the rule for the application’s register layout with 70,700, and the brand rule with 28,600.
The request that led to this measurement came the evening before, with the test rule open in the editor. It comes from the session transcript, was dictated, and is smoothed here for language and translated from German:
I've noticed that the context window fills up pretty quickly when I
develop new features or fix a bug. I just opened the file testing.md
and saw that it is pretty long. It probably gets loaded into the
context window in full whenever a unit test runs, or whenever, and
uses up a lot of it that way. Can you please analyze where and during
which actions the context window fills up quickly, and make
suggestions for how we deal with it?
Requested was an analysis with suggestions, not a measurement. The agent answered with “I’ll first measure what actually loads at session start”, and what came next was an estimate: the size of the loaded files in bytes divided by 3.7, a rule of thumb for bytes per token. The files totaled 902,576 bytes that day, the result was 243,939, rounded 244,000 tokens. Observed rather than estimated was only one part, namely which files were actually loaded. That the agent could look up in its own instructions. Exactly three rule files were missing there, the ones for backend, frontend and security, and those are not the three largest just named, but the three that were already bound to file paths at the time and therefore do not load at start. How that works is shown by the lever “Binding rules to files with paths:”. The measurement came only the day after, and it gave 454,900 tokens for 909,920 bytes, that is 2.0 bytes per token. The rule of thumb had underestimated the token count of German-language Markdown with tables, paths and code identifiers by almost half. All numbers in this article are therefore either measured or explicitly marked as an estimate or extrapolation. Whoever wants to know their own fixed cost measures it.
Grown With the Work
The 454,900 tokens were the result of a good six months of maintenance. The first rule file was created at the end of March 2026, and the repository’s history makes it possible to trace how many tokens the rule files would have cost on the first of each month. These values are not measured, that would have taken a separate session in a separate worktree for every month. They are extrapolated from the file sizes with the ratio of 2.0 bytes per token measured on October 15. Whether the historical states had the same ratio is not checked, per file it lay between 1.9 and 2.2 on the day of measurement. As an order of magnitude that is enough, and the table claims no more.
| State | Rule files total (tokens, extrapolated) | Of which the test rule (tokens, extrapolated) |
|---|---|---|
| April 1, 2026 | 8,600 | does not exist yet |
| May 1, 2026 | 26,600 | does not exist yet |
| June 1, 2026 | 117,300 | does not exist yet |
| August 1, 2026 | 219,000 | 13,000 |
| September 1, 2026 | 250,900 | 22,500 |
| October 1, 2026 | 391,100 | 94,600 |
| October 14, 2026 | 441,100 | 119,800 |
Since June the volume has almost quadrupled, and between September and October alone roughly 140,000 tokens came in, 72,000 of them in the test rule. Behind this jump is a maintenance duty. The test rule required that every new test file add a row to its inventory: which file checks what, why the test came about, and which counter-check proved that it catches the bug. Every single one of these rows makes sense. Whoever later wants to know whether a function already has a test finds the answer there. In sum, however, the duty turned the rule file into a record that every session pays for.
And every one of these rules came out of a real bug, and every one prevents the same bug from coming back. Their value is not in question. What makes them expensive is the moment they load, which is the same for all of them, namely always, and the fact that in the same file two kinds of text grow that are needed at different times.
Rule or Record: The 84 Percent Case
The DI² project’s test rule was, at 124,000 tokens, the largest rule file, measured with /context. Of that, around 104,000 tokens went to the inventory table with one row per test file, 162 rows on the day of measurement. That is 84 percent of the file, calculated via the table’s share of the bytes, because /context counts per file, not per section. The measurement after extracting the table confirmed the arithmetic: Without it, the file cost 20,300 tokens. The remaining 16 percent were the actual rules: where test files go, which test environment to choose, mock patterns, time budgets, load tests, and a list of things one does not do. The longest single table row had 15,129 characters, more than the entire rule file for functions or the one for triggers.
What the inventory rows answer is a record question: What was checked back then, why, and with which counter-check? What is needed when writing a new test is a rule question: Where does the file go, which environment, which mock pattern? The rows are not bad. They are in the wrong place, and in the wrong place they are paid for at every session start, including in the sessions in which nobody writes a test.
| Kind | Answers | Example |
|---|---|---|
| Rule | the next decision: where, how, what not | “Test files sit next to the source file and end in .test.ts.” |
| Record | a past one: what was checked, why, with which counter-check | “This file checks the rounding because an amount was rounded wrongly. Negative control: test red without the fix.” |
The same pattern showed up in four more rule files, there at harmless size. The rule for dropdown menus consisted to 35 percent of an inventory of its users, the one for naming toasts to 33 percent, the one for confirmation dialogs to 33 percent, the one for dialogs to 28 percent. In absolute terms that was only a few thousand tokens each, which is why it never stood out. The test rule differs from them not in pattern but only in scale: one row per test file, with growing test coverage.
The dividing line runs along the function of a text, not its form. The same test rule holds a list of known gaps that are deliberately not covered by tests. It looks like an inventory but is a rule, because it is meant to act at the next intervention: Whoever touches one of these places should know that no test will catch them. This list stayed in the rule file.
What Gets Added While Working
The fixed cost is only the beginning. As soon as a session works, reads come on top, and the largest of them is almost half the size of the fixed cost itself. The following values are extrapolated, not measured with /context, because the command shows only the start. They are based on the file sizes and the measured ratio of 2.0 bytes per token.
| Action | Tokens (extrapolated) |
|---|---|
| Reading the bug register to find the next free number | around 210,000 |
| Reading the application’s largest component once | around 155,000 |
| Loading the tool guide for the bug workflow | around 22,000 |
The first row is the interesting one. What is needed there is a single number, the highest number assigned. The table is sorted in descending order, so the first data row carries the answer. That the entire register wanders into the context instead, more than a full context window, is not the tool’s fault. It comes from the instruction “read the register and determine the next number”. The agent does what it says.
Together with the fixed cost, this explains the observation that stood at the beginning: start, tool guide, one large file, and the session is full before the first line of code is written. For subagents it is more than an annoyance. A subagent usually receives the project instructions, only the built-in Explore and Plan agents start without them. The main session’s context window it does not inherit: Its window follows its own model, and some of the built-in agents run on smaller models than the main session. The agent that answers questions about the Claude Code documentation ran, in version 2.1.292, on Haiku 4.5 with a 200,000-token window. With 454,900 tokens of instructions, it could not start in this repository, while an agent that inherits the main session’s model still had room with one million tokens. The failure from the opening of this article was exactly that, here the message verbatim:
Prompt is too long · the request is ~347814 tokens (limit 200000) but this
conversation is only ~227482 tokens — the rest is system prompt, tool
definitions, and attachment content. A single-exchange conversation cannot
be compacted; reduce attached files/tools or start with less context.
The failure even prevented its own investigation: The attempt to clarify the tool’s loading rules from the documentation via a subagent failed at the very context limit it was supposed to explain. Since version 2.1.293, the same agent runs on Haiku 5.5 with a window of one million tokens. Reproduced on October 20, 2026, with version 2.1.301 on the state before the rework: The agent started, its request had 485,056 tokens, counted with that model’s newer tokenizer, and it answered the probe question. The fixed cost has not become smaller by that. Only the threshold at which it becomes visible has moved back.
The table’s second row, the largest component, applies to the day of measurement. Since then it has resolved itself, for a reason that had nothing to do with the context window. The largest component had 7,103 lines. In September, a bug fix had failed on it because the interaction of two load runs was not visible in any diff, and since then the file was under a lock for any non-trivial change. On the same day as the rework of the rules, its decomposition therefore began, behavior-neutral and in cuts, each its own commit. On October 19, 2026, it consisted of an orchestrator with 1,607 lines and 23 units for load paths, locks, mutations and dialogs, none above 1,000 lines, with 21 test files of their own alongside. The code did not get smaller in the process but grew by a third, because every unit needs a header, an interface and imports. What dropped is what a single task has to load. Whoever changes a load path today reads a hook with 900 lines, extrapolated around 22,000 tokens, instead of the whole file at around 155,000. That is the same dividing line as with the rules, only in source code: Nothing was shortened, what changed is what enters the context and when. The cut was needed first by the human who wants to see an interaction in the diff. The agent that reads the file gets the same benefit.
Three Levers, and What They Cost
The same rule applies to all three: No rule is shortened in substance. Each came from a real bug. What changes is only when it loads and what sits next to it in the same file.
Binding Rules to Files With paths:
Claude Code knows a paths: frontmatter for rule files. A rule that carries it does not load at start but only when the agent reads, writes or edits a file that matches one of the patterns. For Write and Edit this has applied since version 2.1.288 of October 2, 2026, before that only a read loaded the rule. The measurement in this article with 2.1.145 is unaffected, it counts only the start. The patterns are glob patterns, relative to the repository:
---
paths:
- "src/components/**"
- "src/app/**"
- "src/lib/**"
---
In the DI² repository, 3 of 30 rule files used this on the day of measurement, although most rules are tied to a file type. The SQL rules apply to files under db/, the rules for register layout, dialogs and toasts to components. After the rework, 24 of 30 files carried a paths: frontmatter. That was 264,300 tokens and thus a good two thirds of the total savings.
The lever has a limit. A bound rule arrives only with the result of the tool call, because the call on a matching file is what loads it. A probe on October 15, 2026, in Claude Code 2.1.292 showed it: In a session whose context did not yet hold the frontend rule, a read of a component loaded it only in the result of that read. When editing an existing file this is harmless, because Claude Code’s Edit tool requires a prior read of the file and the rule is then already there. If the agent changes the file through the shell instead, it does not load the rule. When writing a new file, the content is written before the rule arrives. A second probe on October 20, 2026, showed this in two versions: A rule required a specific first line for every new file under src/. In 2.1.145 the Write call did not load the rule at all, in 2.1.301 it arrived after the call’s result, and in both cases the file came into being without that line. Whether the agent notices that afterwards depends on the run: In a third probe with 2.1.301 it again wrote the file without the line, saw the rule with the result of the call, and added the line with a second call. That cannot be relied on. Precisely when a convention is meant to prevent something from being created wrongly, it is not there.
Three safety nets close this gap:
- Creation rules stay global: The test rule and the rule for page scaffolds apply when creating files that do not exist yet. Both stay without
paths:and carry a comment at the top of the file that explains why. Without that comment, the next cleanup session tidies them away as well. - Workflow skills read explicitly beforehand: The skill that creates backend code reads the SQL rules before it writes the first file. The skills for frontend and interface design got the same step for the interface rules.
- A rule map in
CLAUDE.md: One line per bound rule with file name and scope, without content. The map costs 1,800 tokens and deliberately sits inCLAUDE.md, because that file is read in again after an automatic compaction of the context. Without it the agent does not know that a rule exists, and therefore does not ask for it either. Then binding would not be a saving but a loss of substance.
The comment at the top of the test rule, translated from German:
<!-- Deliberately WITHOUT paths: frontmatter. A bound rule loads only with
the result of the Read/Write/Edit on a matching file. For a file that
does not exist yet, that is too late. Applies when CREATING a test
file (location, suffix, mocks, budgets). Do not bind. -->
Two lines from the map:
- .claude/rules/sql.md — overarching SQL style guide (naming, alignment), db/**
- .claude/rules/tables.md — table DDL, constraints, comments, db/**
Two pitfalls come with it. The frontmatter is YAML, and Claude Code reads it at the start of every session to decide which rules are bound. A glob pattern that begins with an asterisk and stands without quotes, say - **/*.tsx, is read by YAML as a reference to an anchor, not as text. The parser aborts, and Claude Code then loads the rule as if it had no paths:, that is, at every start. In normal operation it does not report this. A probe in Claude Code 2.1.145 confirmed it: Of three rules with paths:, exactly the one with the unquoted asterisk appeared among the files loaded at start, and only claude --debug reported the parser error, without naming the file. The saving is then gone, and nobody notices. Patterns in the frontmatter therefore always belong in quotes.
The second pitfall is a binding that is too narrow. The brand rule, which among other things governs quotation marks and punctuation in user-facing text, was first bound to the dictionary files and one further single file. User-facing text, however, also lives in other files next to them, and from exactly one such file came the bug that had once triggered the rule. It is now bound to the whole directory. A binding that is too wide costs something only when the rule loads. One that is too narrow never loads it where it is needed.
Separating Rule and Record
The test rule’s inventory table moved into a file of its own under docs/, with all 162 rows unchanged. The test rule itself kept its rules and two sentences about the inventory: where it lives and that every new test file adds a row there. Without this maintenance duty in the rule, the inventory becomes orphaned. The rule file thus shrank from 124,000 to 20,300 tokens. That is 103,700 tokens saved, a good quarter of the total.
This is what the separation looks like in concrete terms. Since then, only the reference remains in the test rule, here shortened and translated from German:
## Inventory
The inventory of test files (one row per file: what it checks,
triggering bug, negative control) lives outside this rule file in
docs/inventories/unit-tests.md. Do not read the whole inventory,
search for the file name instead.
The extracted file begins with a note on what it is and how to read it. Then follows one row per test file, here one of the shorter ones:
# Inventory of Unit Test Files
> Record, not rule. One row per test file: what it checks and why,
> often with triggering bug and negative control. The rules for unit
> tests live in .claude/rules/testing.md.
>
> Read selectively, not in full: The file name is at the start of
> every row, a search for it finds exactly that one row.
>
> Maintenance duty: add a row for every new test file.
| Test file | Project | Tests |
|----------------------------|---------|--------------------------------------------|
| src/lib/topo-order.test.ts | unit | computeProcessingOrder + refineWithinLevel |
| | | (levels, cycles, tie-break, isStale) |
What matters is how the extracted file is built. Moving alone would only have relocated the problem, because reading 104,000 tokens in one go as soon as the agent needs the inventory would hardly be better. The file therefore has one row per test file, and the file name is at the start of the row. A search for a file name finds the row without the whole file being read. The file’s header says so explicitly: do not read, search.
In the DI² project this has become a convention, which itself costs 1,500 tokens because it sits in a globally loaded rule. A new inventory is created under docs/, never as a section of a rule file. The rule names the location and the maintenance duty in one sentence. And large files are not pulled into CLAUDE.md via @ import, because what it imports loads with it at every start. Splitting a large file across several imports therefore saves nothing.
Looking Up Instead of Preloading
The third lever changes no rule but instructions. The skill that creates a new bug read the bug register in full to find the next number. Now it reads at most the first 30 lines, because the register is sorted in descending order and the first data row carries the highest number. Two further places that searched the register for a specific entry now search with grep instead of reading. The same thought led to two changes to CLAUDE.md: The roadmap table of the product description, 89 percent of that file, moved into a file of its own, and the feature index is no longer pulled in via import. The agent reads both when it needs them, and the skills that need them say so explicitly. Together 25,500 tokens.
Not every full read is waste. The review of all skills found three that stay. The feature index is read in full by eight skills because the context there is actually needed. The product description is small after the rework. The test-case index is appended where it is read. The lever applies to instructions of the form “read the file to find a single value”. For a file whose entire content carries the decision, it does not apply.
Before and After: 454,900 to 64,700 Tokens
The rework ran in five steps, each its own commit and individually revertible. First came the purely relocating ones, then binding to paths as the riskiest step, last the guardrail. There was no written-out implementation request. The agent had proposed a spec from the analysis and asked questions about it, which the maintainer answered by selection. Questions and answers are in the transcript verbatim, shown here translated from German:
How far should I take the context rework now?
-> Everything, including the scoping pass
One feature for all four levers, or split up?
-> One feature, four building blocks
Scope the register and layout rules? That is the big item,
but the risky one.
-> Delegate the decision to /architecture
Detach the PRD roadmap and the feature index from the always-includes
of CLAUDE.md?
-> Yes, with a lean remainder
Should a guard against relapse be created? Without it the next rule
file grows into a record dump again without anyone noticing.
-> Yes, as a CI guard with baseline
The four levers in this count are the three of this article and the guard, which this article treats separately as the guardrail. The implementation request after that consisted of two words: “sofort umsetzen”, implement now. During implementation, after the loading semantics had been clarified, one last question came: whether the register and layout rules should be bound as well, by the estimate at the time 115,000 tokens together, among them the register rule with a measured 70,700. The answer was yes, and that became the largest item of the savings.
The acceptance criteria, among them the measurement with /context in a fresh session and the subagent that has to start again, had been written into the spec by the agent. The maintainer accepted them with the spec rather than formulating them, writes no Node, and could not have written the check script. The rework of the rule files, the script, and the decision which rules stay global were worked out by the agent. From the maintainer came the problem description, the answers above, and at the end the measurement itself: /context and the subagent probe in a fresh session, started by hand, because a running session cannot re-measure its own start state. What mattered was that the measurement stood in the spec as acceptance from the beginning and was not set after the result.
The measurement was taken with the tool itself. Both states were available as separate Git worktrees, /context was the first and only input of a new session without the interactive interface, Claude Code 2.1.145 with the Opus 5.5 model, both runs on October 15, 2026, minutes apart on the same machine. A counter-check in an interactive session counted file by file the same.
| Category | Before | After |
|---|---|---|
Project instructions (CLAUDE.md, imports, rule files) | 454,900 | 64,700 |
| Auto-memory (user-local) | 4,700 | 4,700 |
| System prompt, tools, skills, agents | 27,900 | 27,200 |
| Occupied before the first prompt | 487,500 | 96,600 |
| Share of a one-million-token context window | 49 % | 10 % |
| Share of a 200,000-token context window | 244 % | 48 % |
| Free in a 200,000-token window after the 33,000-token reserve for automatic compaction | nothing | 70,400 |
Broken down by lever:
| Lever | What changed | Tokens |
|---|---|---|
| Binding rules to files | 21 more rule files with paths:, 3 → 24 of 30 | −264,300 |
| Separating rule and record | Test rule 124,000 → 20,300, inventory alongside | −103,700 |
| Looking up instead of preloading | Roadmap out of the product description, feature index no longer an import | −25,500 |
| Cost of the implementation | Rule map (+1,800), convention “rule versus record” (+1,500) | +3,300 |
| Total | files loaded at start 30 → 8: CLAUDE.md, imports 2 → 1, unbound rule files 27 → 6 | −390,200 (−85.8 %) |
The second proof is a yes or no. The same subagent that had aborted before started after the rework in a fresh session with 66,887 tokens of context and answered the probe question correctly.
Three things the numbers do not say.
- Measured is the fixed cost, not the working cost: Bound rules are not gone. A session on the register interface still loads the register rule with its 70,700 tokens as soon as it touches the first component. A session on SQL or documentation no longer loads it. Measured is the start, not a typical working day.
- The limit of binding is measurable: Of the 64,700 tokens, 31,300 belong to the two rules that deliberately stay global because they have to act before a file exists. That is just under half of the remainder, and it cannot be removed without shortening the rules.
- Binding shifts who sees a rule: Before the rework, every skill had all rules in its context because all of them loaded at start. After the rework, a bound rule loads only when the agent reads or edits a matching file. The skill that reviews a diff does not do that. It reads the diff via
git diffin the shell and touches not a single file in the process. Its context therefore holds none of the bound rules, for instance not the SQL conventions for files underdb/. It would have checked SQL against conventions it does not know, and found nothing. This came up only in the quality assurance of the rework, not during the rework itself. The skill now matches the paths in the diff against the map and reads the corresponding rules explicitly before it reviews. Behind that is the same reflex as in The Agent Measures Where I Click: do not assume what the agent sees, go and look.
Without a Guardrail, the Growth Comes Back
The growth from the section “Grown With the Work” came from a maintenance duty without a cap. Without a cap, the savings would be a snapshot, and the next well-meant inventory would produce the same number again in half a year. With the rework, a check script therefore went into CI, without dependencies, as a blocking step. It calculates with bytes divided by 2.0, the measured ratio, and on the state of October 15 that put it about two percent off /context. That is an approximation with one day’s ratio, not a measurement. Measuring continues with /context, the guard only draws the line, and the budget of 80,000 tokens leaves room against the measured 64,700 for the error of the approximation. It checks seven things:
| Check | Reports |
|---|---|
| Fixed cost | CLAUDE.md, its imports and all unbound rules above the budget of 80,000 tokens. The total is printed on every run so that a new import does not add fixed cost unnoticed. |
| Size | a single rule file above the threshold, with separate thresholds for bound and unbound rules |
| Inventory share | more than 25 percent of a rule file’s lines sit under an inventory heading. This check runs separately from size: A large file of pure rule prose is no finding, a small file that is half inventory is. The guard thus does not force rules to be shortened, but records to be moved. |
| Frontmatter | a paths: block that cannot be parsed, unquoted patterns included. Such a rule counts toward the fixed cost, because that is how the tool loads it. |
| Map | a bound rule without a line in the map, or a map line without a file |
| Orphaned inventory | number of test files against number of inventory rows |
| Baseline | a tolerated legacy that no longer occurs. The baseline may only shrink. |
The guard checks files, not behavior. Whether a skill actually reads a bound rule before it reviews, it cannot see. The review skill without SQL rules from the section before was found by quality assurance, not by the guard.
The first run immediately produced a finding nobody had expected. The orphaned-inventory check found five test files without an inventory row, all from the time before the rework. The maintenance duty “one row for every new test file” had thus let the inventory grow to 104,000 tokens and still had not been kept. A duty that nobody checks therefore keeps neither the inventory complete nor the rule file small.
The decomposition of the largest component ended with a second guard on the same pattern, this time for source files. It checks every file under src/ for at most 1,000 lines, tests included, with a baseline for the legacy that may only shrink, and a margin of two percent so that a bug fix on an old file gets through and a feature does not. Its first run found 33 files above the limit, six were expected. The rule for it does not sit in a file that loads at every start but in the rule for components, pages and hooks, which is bound to exactly these paths. It therefore enters only the sessions in which such a file can grow. For all other files, the guard speaks up itself.
What the Solution Is Not
Shortening rules until they say nothing is not it. After the rework, 20,300 tokens of pure rule prose remained in the test rule, and the acceptance criterion had demanded “at most 12,000”. That number came from the rule of thumb of the day before, with the same error as all the estimates. The number was revised, not the rule. What a rule says, it has earned from a bug, and a token budget is no reason to forget that again.
A product critique is not it either. The binding mechanism existed the whole time, it was documented, and it demonstrably worked. That 3 of 30 files used it is a matter of maintenance discipline. That an agent has a rule in its context when it needs it is, moreover, the precondition for it to follow the rule at all.
And a check that calculates with the same wrong assumption as what it checks is least of all. The guard from the section “Without a Guardrail, the Growth Comes Back”, the check script in CI, was at first calibrated not with the 3.7 of the first estimate but with a second wrong number: The failed subagent’s error message named, next to the 347,814 tokens of the whole request, around 227,000 tokens for the conversation, and the file size divided by that number gave 4.0 bytes per token. Only, the message did not count the same thing as /context. It came from a different model and referred to a part of the request. Measured, it is 2.0. Quality assurance and code review calculated with the same factor, confirmed “33,000 of 45,000 tokens” and considered the goal reached. Correct was 64,700 of 80,000. That came to light only when /context ran in a fresh session. Two checks that report green prove nothing if they take the same number from the same source. Why a green test can confirm, in exactly this way, the very bug it was supposed to find is shown in The Green Test That Proves Nothing.
One caveat belongs here. The analysis that triggered this article ran in the same session whose context window it examined. Measuring itself costs context.
Summary
- Claude Code loads project instructions before the first prompt, in every session. In the DI² repository that was 454,900 tokens, just under half of a one-million window and more than twice a 200,000 window, grown over a good six months with the work on the project.
- The dividing line is rule versus record. A rule governs the next decision, a record proves a past one. Both belong in the repository, only the rule in the context at start. 84 percent of the largest rule file was record.
- Three levers, none of which shortens a rule: bind rules with a file scope to paths, move inventories into a neighboring file with the file name at the start of each row, write instructions to look up rather than preload. Result 64,700 tokens, and the subagent starts again.
- Binding to paths comes too late for new files and shifts who sees a rule. Against that help creation rules that stay global, skills that read explicitly, and a map in
CLAUDE.md. Against the growth helps a guard in CI, because a duty that nobody checks keeps neither the inventory complete nor the rule file small.
FAQ
With the /context command as the first input of a new session. It lists every loaded instruction file individually with its token count. For a repeatable measurement, claude -p "/context" without the interactive interface is enough, checked with Claude Code 2.1.145. In Git Bash, path conversion has to be off for this (MSYS_NO_PATHCONV=1), otherwise /context reaches the model as a file path. The fresh session matters: A running session or a subagent carries the instruction state from its own start.
For English prose that is roughly right, for German-language Markdown with tables, paths and code identifiers it is not. Measured, the rule files sat at 2.0 bytes per token, per file between 1.9 and 2.2. An estimate with the factor 3.7 came in almost half too low, and a guard that calculated with 4.0 halved all results without anyone noticing. On top, the number depends on the model: The newer tokenizer from Claude 4.7 onwards counts the same text at roughly 30 percent more tokens than the older one, somewhat more or less depending on the text.
paths: load in time when the agent creates a new file? No. The rule arrives at the earliest with the result of the tool call, for a new file the content is already written by then, and before version 2.1.288 a Write call did not load it at all. Whether the agent then fixes the file on its own depends on the run. When editing an existing file there is no gap, because the Edit tool requires a prior read. For rules that have to act at creation, three ways remain: leave them global without paths:, read them explicitly beforehand in the creating skill, and keep a map in CLAUDE.md so that the agent knows the rule exists. Checked in Claude Code 2.1.145, 2.1.292 and 2.1.301.
Because every rule came from a bug that comes back without it. The DI² project’s test rule kept 20,300 tokens of pure rule prose after the rework, although the target read “at most 12,000”: The target number came from a wrong estimate, and the number was revised. The savings come from when a rule loads and from no longer mixing it with record text, not from its content. Whoever still wants to shorten first checks whether a section proves a past decision. Then it is record and can move out.
Only if it knows the rule exists. A question about the SQL naming convention in conversation does not load the SQL rule, because no file under db/ is read. The same goes for a review that reads the diff via git diff rather than through a file. That is why CLAUDE.md holds a map with one line per bound rule, and skills that review without file access read the relevant rules explicitly. Otherwise binding would be a loss of conventions that goes unnoticed.
Related Articles
Parent articles:
- Agentic Coding from a User’s Perspective — the sub-hub of this branch: The work does not disappear, it shifts, among other places into the maintenance of rules.
- AI-Assisted SQL Development with Claude Code — the entry point to rules, skills and agents.
Sibling articles:
- The Agent Measures Where I Click — there the agent measures the product, here its own working material. The method is the same: measure first, then interpret.
- The Control Loop — rules are the means by which code can be checked that one does not read oneself. This article shows what that means costs at session start.
Downstream:
- 27 Rule Files Instead of One CLAUDE.md — how the rule files are built whose load time is shifted here.
- Deriving SQL Conventions with Claude Code — how the rules come about whose running cost is measured here.
- The Green Test That Proves Nothing — why two checks with the same wrong assumption confirm the goal they miss.