ai newsFr., 23. Okt.
EilmeldungCanva stellt eigenes KI-Bildmodell Canva Craft vor

Claude Code: Projektregeln füllen das Kontextfenster, bevor die erste Anfrage startet

Ein Subagent in Claude Code bricht ab, weil der Prompt mit 347.814 Tokens das Limit von 200.000 überschreitet, obwohl die eigentliche Frage leer ist. Verantwortlich sind die Projektanweisungen, die bei jeder Session mitgeladen werden.

dev.toDonnerstag 22:23 · 22.10.DE26 Min. Lesezeit
Bild: dev.to

Veröffentlicht am 22. Oktober • Zuerst erschienen auf sql.marcus-belz.de

Ein Subagent in Claude Code soll eine einzeilige Frage zur Dokumentation des Tools beantworten. Er bricht ab, bevor er seinen ersten Schritt macht: Prompt is too long, 347.814 Tokens gegen ein Limit von 200.000. Die Frage selbst hatte nichts geladen. Geladen hatten die Projektanweisungen, die jede Sitzung in diesem Repository beim Start erhält. Der Subagent lief auf einem Modell mit einem Kontextfenster von 200.000 Tokens, und die Anfrage, die ihn starten sollte, war bereits fast doppelt so groß wie dieses Fenster. Die Hauptsitzung, die ihn gestartet hatte, lief mit einem Fenster von einer Million Tokens. Sie brach nicht ab, sie war halb voll, bevor irgendjemand etwas eingetippt hatte.

Dieser Artikel beschreibt, wie diese Projektanweisungen im privaten DI²-Projekt so groß werden konnten. Er zeigt, was eine Messung mit /context ergab und welche drei Hebel danach angesetzt wurden. Die Mechanik und alle Zahlen beziehen sich auf Claude Code vom 15. Oktober 2026.

Die Messung wurde mit Version 2.1.145 durchgeführt, der Fehler des Subagenten stammt aus einer Sitzung mit Version 2.1.292. Die Ursache beschränkt sich jedoch nicht auf Claude Code. Sie betrifft jedes Tool, das einem Agenten beim Start Projektregeln mitgibt.

In solchen Regeldateien sammeln sich im Laufe der Zeit zwei Arten von Text an: Anweisungen für den nächsten Schritt, etwa wohin eine neue Testdatei gehört, und Belege vergangener Schritte, etwa welche Testdatei welchen Bug absichert. Dieser Artikel nennt das erste eine Regel und das zweite einen Eintrag. Beide gehören ins Repository.

In den Kontext, den jede Sitzung beim Start lädt, gehört nur die Regel, denn nur die Regel wird für den nächsten Schritt benötigt.

Voraussetzung: Die Grundlagen von Claude Code werden dort kurz erklärt, wo sie benötigt werden. Alle Tokenzahlen stammen aus /context in einer frischen Sitzung, mit Datum und Version. Wo eine Zahl nur geschätzt oder hochgerechnet ist, wird das gesagt.

Beim Start einer Sitzung liest Claude Code drei Arten von Projektanweisungen. Die CLAUDE.md des Projekts im Wurzelverzeichnis des Repositorys. Alles, was diese Datei über @-Importe hereinzieht, bis zu vier Ebenen tief. Und jede Regeldatei unter.claude/rules/, die kein paths-Frontmatter trägt. Wie ein Repository zu 30 solchen Regeldateien kommt und was in ihnen steht, ist in 27 Rule Files Instead of One CLAUDE.md beschrieben. Entscheidend ist hier, dass diese drei Punkte im Kontext liegen, bevor der erste Prompt eingetippt wird, und zwar in jeder Sitzung, unabhängig davon, ob die Aufgabe eine Zeile oder ein ganzer Arbeitstag dauert.

Wie viel das ist, zeigt der Befehl /context. Er listet jede geladene Anweisungsdatei einzeln mit einer Tokenzahl auf, die das Tool selbst ermittelt und auf Hundert gerundet. Die Zählung hängt vom Modell der Sitzung ab: Eine Messung zählte dieselben Dateien unter Haiku 4.5 spürbar niedriger als unter Opus 4.7. Der Grund ist der Tokenizer: Modelle ab Claude 4.7 verwenden einen neueren, der laut Dokumentation denselben Text mit rund 30 Prozent mehr Tokens zählt als der ältere, je nach Text etwas mehr oder weniger. Es hängt auch vom Inhalt ab, pro Datei lagen zwischen 1,9 und 2,2 Bytes auf einen Token, und für denselben Stand und dasselbe Modell ist das Ergebnis reproduzierbar. Für eine wiederholbare Messung genügt ein Start ohne die interaktive Oberfläche, mit /context als einziger Eingabe:

claude -p "/context" Enter fullscreen mode Exit fullscreen mode Im DI²-Repository ergab das am 15. Oktober 2026 mit dem Modell Opus 5.5, vor jeder Änderung, Folgendes:

Die erste Zeile ist die fixe Kostenposition, um die es in diesem Artikel geht: die Projektanweisungen, die bei jedem Start geladen werden und die das Projekt selbst steuert. Die beiden anderen Posten kommen hinzu, hängen aber nicht vom Repository ab.

Welches Fenster diese Zahl füllt, hängt vom Modell ab. Die Hauptsitzung lief auf Fable 5.1 und Opus 5. Beide haben in Claude Code ein Fenster von einer Million Tokens, und beide zählen Text wie Opus 5.5: Eine Messung mit denselben beiden Dateien ergab unter allen drei Modellen dieselben Werte.

Dort bedeuteten 487.500 Tokens eine Belegung von 49 Prozent, bevor irgendjemand etwas eingetippt hatte. Bei einem Modell mit einem 200.000-Token-Fenster sind es 244 Prozent. Nach Abzug der Reserve von 33.000 Tokens, die Claude Code in der gemessenen Version 2.1.145 für die automatische Kompaktierung freihält, bleibt dort nichts übrig, und genau auf so einem Modell lief der Subagent aus der Einleitung dieses Artikels.

Seine Fehlermeldung nannte 347.814 Tokens, nicht 487.500, weil beide nicht dasselbe zählen: Die Meldung zählt die Anfrage des Subagenten mit dem Haiku-4.5-Tokenizer, /context zählt den Start der Hauptsitzung mit dem Opus-5.5-Tokenizer. Dieser Artikel vergleicht daher nur innerhalb von /context mit demselben Modell, die Fehlermeldung belegt das Ja oder Nein. Drei Dateien trugen knapp die Hälfte: die Testregel mit 124.000 Tokens, die Regel für das Registerlayout der Anwendung mit 70.700 und die Markenregel mit 28.600.

Die Anfrage, die zu dieser Messung führte, kam am Vorabend, mit der Testregel im Editor geöffnet. Sie stammt aus dem Sitzungsprotokoll, wurde diktiert und ist hier sprachlich geglättet und aus dem Deutschen übersetzt:

Mir ist aufgefallen, dass sich das Kontextfenster ziemlich schnell füllt, wenn ich neue Features entwickle oder einen Bug behebe. Ich habe gerade die Datei testing.md geöffnet und gesehen, dass sie ziemlich lang ist. Sie vermutlich jedes Mal vollständig ins Kontextfenster geladen, wenn ein Unittest läuft, oder überhaupt, und verbraucht so einen großen Teil davon.

Kannst du bitte analysieren, wo und bei welchen Aktionen sich das Kontextfenster schnell füllt, und Vorschläge machen, wie wir damit umgehen? Enter fullscreen mode Exit fullscreen mode Gefragt war eine Analyse mit Vorschlägen, keine Messung. Der Agent antwortete mit „Ich messe zuerst, was beim Sitzungsstart tatsächlich geladen wird“, und was folgte, war eine Schätzung: die Größe der geladenen Dateien in Bytes geteilt durch 3,7, eine Faustregel für Bytes pro Token.

Die Dateien sumsammten sich an jenem Tag auf 902.576 Bytes, das Ergebnis war 243.939, gerundet 244.000 Tokens. Beobachtet statt geschätzt war nur ein Teil, nämlich welche Dateien tatsächlich geladen wurden. Das konnte der Agent in seinen eigenen Anweisungen nachschlagen.

Genau drei Regeldateien fehlten dort, die für Backend, Frontend und Security, und das sind nicht die drei eben genannten größten, sondern die drei, die zu diesem Zeitpunkt bereits an Dateipfade gebunden waren und daher beim Start nicht laden. Wie das funktioniert, zeigt der Hebel „Regeln mit Pfaden an Dateien binden“. Die Messung kam erst am Tag darauf, und sie ergab 454.900 Tokens für 909.920 Bytes, also 2,0 Bytes pro Token.

Die Faustregel hatte die Tokenzahl des deutschsprachigen Markdowns mit Tabellen, Pfaden und Codebezeichnern um fast die Hälfte unterschätzt. Alle Zahlen in diesem Artikel sind daher entweder gemessen oder ausdrücklich als Schätzung oder Hochrechnung gekennzeichnet. Wer seine eigene Fixkostenposition kennen will, misst sie.

Die 454.900 Tokens waren das Ergebnis von etwas mehr als einem halben Jahr Pflege. Die erste Regeldatei entstand Ende März 2026, und die History des Repositorys erlaubt es nachzuvollziehen, wie viele Tokens die Regeldateien am Ersten eines jeden Monats gekostet hätten. Diese Werte sind nicht gemessen, das hätte für jeden Monat eine eigene Sitzung in einem eigenen Worktree gebraucht. Sie sind aus den Dateigrößen hochgerechnet mit dem am 15. Oktober gemessenen Verhältnis von 2,0 Bytes pro Token. Ob die historischen Zustände dasselbe Verhältnis hatten, wurde nicht geprüft, pro Datei lag es am Messtag zwischen 1,9 und 2,2. Als Größenordnung reicht das, und die Tabelle beansprucht nicht mehr.

Seit Juni hat sich das Volumen fast vervierfacht, und allein zwischen September und Oktober kamen rund 140.000 Tokens hinzu, davon 72.000 in der Testregel. Hinter diesem Sprung steht eine Pflicht. Die Testregel verlangte, dass jede neue Testdatei eine Zeile in ihrem Bestandsverzeichnis ergänzt: welche Datei was prüft, warum der Test entstanden ist und welcher Kontrolllauf bewies, dass er den Bug abfängt. Jede einzelne dieser Zeilen ergibt für sich Sinn. Wer später wissen will, ob eine Funktion bereits einen Test hat, findet die Antwort dort. In der Summe machte die Pflicht aus der Regeldatei jedoch einen Eintrag, den jede Sitzung bezahlt.

Und jede dieser Regeln ging aus einem echten Bug hervor, und jede verhindert, dass derselbe Bug zurückkehrt. Ihr Wert steht nicht zur Debatte. Teuer macht sie der Zeitpunkt, zu dem sie laden, der für alle derselbe ist, nämlich immer, und die Tatsache, dass in derselben Datei zwei Arten von Text wachsen, die zu unterschiedlichen Zeitpunkten gebraucht werden.

Die Testregel des DI²-Projekts war mit 124.000 Tokens die größte Regeldatei, gemessen mit /context. Davon gingen rund 104.000 Tokens auf die Inventartabelle mit je einer Zeile pro Testdatei, am Messtag 162 Zeilen. Das sind 84 Prozent der Datei, berechnet über den Anteil der Tabelle an den Bytes, weil /context pro Datei und nicht pro Abschnitt zählt. Die Messung nach dem Auszug der Tabelle bestätigte die Rechnung: Ohne sie kostete die Datei 20.300 Tokens. Die restlichen 16 Prozent waren die eigentlichen Regeln: wohin Testdateien gehören, welche Testumgebung zu wählen ist, Mock-Muster, Zeitbudgets, Lasttests und eine Liste von Dingen, die man nicht tut. Die längste einzelne Tabellenzeile hatte 15.129 Zeichen, mehr als die gesamte Regeldatei für Functions oder die für Triggers.

Was die Inventarzeilen beantworten, ist eine Protokollfrage: Was wurde damals geprüft, warum und mit welchem Gegencheck? Was beim Schreiben eines neuen Tests gebraucht wird, ist eine Regelfrage: Wohin gehört die Datei, welche Umgebung, welches Mock-Muster? Die Zeilen sind nicht schlecht. Sie stehen am falschen Ort, und am falschen Ort werden sie bei jedem Sitzungsstart bezahlt, auch in den Sitzungen, in denen niemand einen Test schreibt.

Dasselbe Muster zeigte sich in vier weiteren Regeldateien, dort bei harmloser Größe. Die Regel für Dropdown-Menüs bestand zu 35 Prozent aus einem Inventar ihrer Nutzer, die für das Benennen von Toasts zu 33 Prozent, die für Bestätigungsdialoge zu 33 Prozent, die für Dialoge zu 28 Prozent. Absolut waren das jeweils nur einige tausend Tokens, weshalb es nie auffiel. Von ihnen unterscheidet sich die Testregel nicht im Muster, sondern nur in der Größenordnung: eine Zeile pro Testdatei, mit wachsender Testabdeckung.

Die Trennlinie verläuft entlang der Funktion eines Texts, nicht seiner Form. Dieselbe Testregel enthält eine Liste bekannter Lücken, die bewusst nicht von Tests abgedeckt sind. Sie sieht aus wie ein Inventar, ist aber eine Regel, weil sie beim nächsten Eingriff wirken soll: Wer eine dieser Stellen anfasst, soll wissen, dass kein Test ihn auffängt. Diese Liste blieb in der Regeldatei.

Die Fixkosten sind nur der Anfang. Sobald eine Sitzung arbeitet, kommen Lesezugriffe hinzu, und der größte davon ist fast halb so groß wie die Fixkosten selbst. Die folgenden Werte sind extrapoliert, nicht mit /context gemessen, weil der Befehl nur den Start zeigt. Sie beruhen auf den Dateigrößen und dem gemessenen Verhältnis von 2,0 Bytes pro Token.

Die erste Zeile ist die interessante. Benötigt wird dort eine einzige Zahl, die höchste vergebene. Die Tabelle ist absteigend sortiert, also trägt die erste Datenzeile die Antwort. Dass stattdessen das gesamte Register in den Kontext wandert, mehr als ein volles Kontextfenster, ist nicht die Schuld des Werkzeugs. Sie kommt aus der Anweisung „lies das Register und bestimme die nächste Nummer“. Der Agent tut, was dort steht.

Zusammen mit den Fixkosten erklärt das die Beobachtung vom Anfang: Start, Werkzeugleitfaden, eine große Datei, und die Sitzung ist voll, noch bevor die erste Codezeile geschrieben ist. Für Subagents ist das mehr als eine Plage. Ein Subagent erhält in der Regel die Projekthinweise, nur die eingebauten Explore- und Plan-Agents starten ohne sie.

Das Kontextfenster der Hauptsitzung erbt es nicht: Sein Fenster folgt seinem eigenen Modell, und einige der eingebauten Agents laufen auf kleineren Modellen als die Hauptsitzung. Der Agent, der Fragen zur Claude-Code-Dokumentation beantwortet, lief in Version 2.1.292 auf Haiku 4.5 mit einem 200.000-Token-Fenster. Mit 454.900 Tokens an Hinweisen konnte er in diesem Repository nicht starten, während ein Agent, der das Modell der Hauptsitzung erbt, mit einer Million Tokens noch Luft hatte.

Das Scheitern aus der Einleitung dieses Artikels war genau das, hier die Meldung im Wortlaut:

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. Enter fullscreen mode Exit fullscreen mode Das Scheitern verhinderte sogar seine eigene Untersuchung: Der Versuch, die Laderegeln des Werkzeugs über einen Subagenten aus der Dokumentation zu klären, scheiterte genau an der Kontextgrenze, die er hätte erklären sollen.

Seit Version 2.1.293 läuft derselbe Agent auf Haiku 5.5 mit einem Fenster von einer Million Tokens. Nachgestellt am 20. Oktober 2026 mit Version 2.1.301 auf dem Stand vor der Überarbeitung: Der Agent startete, seine Anfrage hatte 485.056 Tokens, gezählt mit dem neueren Tokenizer dieses Modells, und er beantwortete die Testfrage. Kleiner geworden sind die Fixkosten dadurch nicht.

Nur die Schwelle, an der sie sichtbar werden, hat sich nach hinten verschoben.

Die zweite Zeile der Tabelle, die größte Komponente, gilt für den Messtag. Sie hat sich inzwischen von selbst erledigt, aus einem Grund, der mit dem Kontextfenster nichts zu tun hatte. Die größte Komponente hatte 7.103 Zeilen.

Im September war darauf eine Fehlerbehebung gescheitert, weil die Wechselwirkung zweier Lastläufe in keinem Diff sichtbar war, und seitdem lag die Datei unter einer Sperre für jede nichttriviale Änderung. Am selben Tag wie die Überarbeitung der Regeln begann deshalb ihre Zerlegung, verhaltensneutral und in Schnitten, jeder ein eigener Commit. Am 19. Oktober 2026 bestand sie aus einem Orchestrator mit 1.607 Zeilen und 23 Einheiten für Lastpfade, Locks, Mutationen und Dialoge, keine über 1.000 Zeilen, daneben mit 21 eigenen Testdateien.

Der Code ist dabei nicht kleiner geworden, sondern um ein Drittel gewachsen, weil jede Einheit einen Kopf, ein Interface und Imports braucht. Gefallen ist, was eine einzelne Aufgabe laden muss. Wer heute einen Lastpfad ändert, liest einen Hook mit 900 Zeilen, extrapoliert rund 22.000 Tokens, statt der ganzen Datei mit rund 155.000.

Das ist dieselbe Trennlinie wie bei den Regeln, nur im Quelltext: Gekürzt wurde nichts, geändert hat sich, was in den Kontext kommt und wann. Den Schnitt brauchte zuerst der Mensch, der eine Wechselwirkung im Diff sehen will. Der Agent, der die Datei liest, hat denselben Nutzen.

Für alle drei gilt dieselbe Regel: Keine Regel wird in der Substanz gekürzt. Jede entstand aus einem echten Fehler. Geändert wird nur, wann sie lädt und was neben ihr in derselben Datei liegt.

Claude Code kennt ein paths:-Frontmatter für Regeldateien. Eine Regel, die es trägt, lädt nicht beim Start, sondern erst dann, wenn der Agent eine Datei liest, schreibt oder bearbeitet, die auf eines der Muster passt. Für Write und Edit gilt das seit Version 2.1.288 vom 2. Oktober 2026, vorher lud nur ein Lesezugriff die Regel. Die Messung in diesem Artikel mit 2.1.145 ist davon unberührt, sie zählt nur den Start. Die Muster sind Glob-Muster, relativ zum Repository:

--- paths: - "src/components/**" - "src/app/**" - "src/lib/**" --- Enter fullscreen mode Exit fullscreen mode Im DI²-Repository nutzten am Messtag 3 von 30 Regeldateien dies, obwohl die meisten Regeln an einen Dateityp gebunden sind. Die SQL-Regeln gelten für Dateien unter db/, die Regeln für Registerlayout, Dialoge und Toasts für Komponenten. Nach der Überarbeitung trugen 24 von 30 Dateien ein paths:-Frontmatter. Das waren 264.300 Tokens und damit ein gut Drittel mehr als zwei Drittel der Gesamteinsparung.

Der Hebel hat eine Grenze. Eine gebundene Regel kommt erst mit dem Ergebnis des Toolaufrufs, weil der Aufruf auf einer passenden Datei das ist, was sie lädt. Ein Test am 15. Oktober 2026 in Claude Code 2.1.292 zeigte es: In einer Sitzung, deren Kontext die Frontend-Regel noch nicht enthielt, lud ein Lesezugriff auf eine Komponente sie erst im Ergebnis genau dieses Lesezugriffs.

Beim Bearbeiten einer bestehenden Datei ist das harmlos, weil Claude Codes Edit-Werkzeug einen vorherigen Lesezugriff auf die Datei verlangt und die Regel dann schon da ist. Ändert der Agent die Datei stattdessen über die Shell, lädt er die Regel nicht. Beim Schreiben einer neuen Datei wird der Inhalt geschrieben, bevor die Regel eintrifft.

Ein zweiter Test am 20. Oktober 2026 zeigte das in zwei Versionen: Eine Regel verlangte eine bestimmte erste Zeile für jede neue Datei unter src/. In 2.1.145 lud der Write-Aufruf die Regel gar nicht, in 2.1.301 traf sie nach dem Ergebnis des Aufrufs ein, und in beiden Fällen entstand die Datei ohne diese Zeile. Ob der Agent das danach bemerkt, hängt vom Lauf ab: In einem dritten Test mit 2.1.301 schrieb er die Datei erneut ohne die Zeile, sah die Regel mit dem Ergebnis des Aufrufs und ergänzte die Zeile mit einem zweiten Aufruf.

Darauf kann man sich nicht verlassen. Genau dann, wenn eine Konvention verhindern soll, dass etwas falsch angelegt wird, ist sie nicht da.

Der Kommentar am Kopf der Testregel, aus dem Deutschen übersetzt:

<!-- Bewusst OHNE paths:-Frontmatter. Eine gebundene Regel lädt erst mit dem Ergebnis von Read/Write/Edit auf einer passenden Datei. Für eine Datei, die noch nicht existiert, ist das zu spät. Gilt beim ANLEGEN einer Testdatei (Ort, Suffix, Mocks, Budgets). Nicht binden. --> Enter fullscreen mode Exit fullscreen mode Zwei Zeilen aus der Map:

-.claude/rules/sql.md — übergreifender SQL-Stilguide (Namensvergabe, Ausrichtung), db/** -.claude/rules/tables.md — Tabellen-DDL, Constraints, Kommentare, db/** Enter fullscreen mode Exit fullscreen mode Damit gehen zwei Stolperfallen einher. Das Frontmatter ist YAML, und Claude Code liest es zu Beginn jeder Sitzung, um zu entscheiden, welche Regeln gebunden sind. Ein Glob-Muster, das mit einem Sternchen beginnt und ohne Anführungszeichen steht, etwa - **/*.tsx, wird von YAML als Referenz auf einen Anker gelesen, nicht als Text.

Der Parser bricht ab, und Claude Code lädt die Regel danach so, als hätte sie keine Pfade:, das heißt bei jedem Start. Im normalen Betrieb meldet es das nicht. Ein Test in Claude Code 2.1.145 bestätigte es: Von drei Regeln mit Pfaden erschien genau die mit dem unbequoten Sternchen unter den beim Start geladenen Dateien, und nur claude --debug meldete den Parserfehler, ohne die Datei zu nennen.

Die Einsparung ist damit weg, und niemand merkt es. Muster im Frontmatter gehören daher immer in Anführungszeichen.

Die zweite Stolperfalle ist eine zu enge Bindung. Die Markenregel, die unter anderem Anführungszeichen und Interpunktion in benutzergerichteten Texten regelt, war zunächst an die Wörterbuchdateien und eine weitere einzelne Datei gebunden. Benutzergerichtete Texte leben jedoch auch in anderen Dateien neben diesen, und aus genau einer solchen Datei stammte der Fehler, der die Regel einmal ausgelöst hatte. Sie ist inzwischen an das gesamte Verzeichnis gebunden. Eine zu weite Bindung kostet nur etwas, wenn die Regel geladen wird. Eine zu enge lädt sie nie dort, wo sie gebraucht wird.

Der Bestandstabelle der Testregel zog in eine eigene Datei unter docs/ um, mit allen 162 Zeilen unverändert. Die Testregel selbst behielt ihre Regeln und zwei Sätze über den Bestand: wo er liegt und dass jede neue Testdatei dort eine Zeile ergänzt. Ohne diese Pflegepflicht in der Regel verwaist der Bestand. Die Regeldatei schrumpfte damit von 124.000 auf 20.300 Tokens. Das sind 103.700 eingesparte Tokens, ein gutes Viertel der Gesamtsumme.

So sieht die Trennung konkret aus. Seitdem steht in der Testregel nur noch der Verweis, hier gekürzt und aus dem Deutschen übersetzt:

## Bestand Der Bestand der Testdateien (eine Zeile pro Datei: was sie prüft, auslösender Bug, negative Kontrolle) liegt außerhalb dieser Regeldatei in docs/inventories/unit-tests.md. Lies nicht den ganzen Bestand, suche stattdessen nach dem Dateinamen. Enter fullscreen mode Exit fullscreen mode Die ausgelagerte Datei beginnt mit einem Hinweis, was sie ist und wie man sie liest. Dann folgt eine Zeile pro Testdatei, hier eine der kürzeren:

# Bestand der Unit-Test-Dateien > Aufzeichnung, keine Regel. Eine Zeile pro Testdatei: was sie prüft und warum, > oft mit auslösendem Bug und negativer Kontrolle. Die Regeln für Unit-Tests > liegen in.claude/rules/testing.md. > > Selektiv lesen, nicht im Ganzen: Der Dateiname steht am Anfang > jeder Zeile, eine Suche danach findet genau diese eine Zeile. > > Pflegepflicht: für jede neue Testdatei eine Zeile ergänzen. | Testdatei | Projekt | Tests | |----------------------------|---------|--------------------------------------------| | src/lib/topo-order.test.ts | unit | computeProcessingOrder + refineWithinLevel | | | | (levels, cycles, tie-break, isStale) | Enter fullscreen mode Exit fullscreen mode Entscheidend ist, wie die ausgelagerte Datei aufgebaut ist.

Ein bloßes Verschieben hätte das Problem nur verlagert, denn 104.000 Tokens auf einen Schlag zu lesen, sobald der Agent den Bestand braucht, wäre kaum besser. Die Datei hat daher eine Zeile pro Testdatei, und der Dateiname steht am Anfang der Zeile. Eine Suche nach einem Dateinamen findet die Zeile, ohne dass die ganze Datei gelesen wird.

Der Kopf der Datei sagt das ausdrücklich: nicht lesen, suchen.

Im Projekt DI² ist das inzwischen eine Konvention, die selbst 1.500 Tokens kostet, weil sie in einer global geladenen Regel steht. Ein neuer Bestand wird unter docs/ angelegt, nie als Abschnitt einer Regeldatei. Die Regel nennt den Ort und die Pflegepflicht in einem Satz. Und große Dateien werden nicht per @ import in CLAUDE.md gezogen, denn was es importiert, lädt bei jedem Start mit. Eine große Datei auf mehrere Imports aufzuteilen spart daher nichts.

Der dritte Hebel ändert keine Regel, sondern Anweisungen. Der Skill, der einen neuen Bug anlegt, las das Bugregister vollständig, um die nächste Nummer zu finden. Jetzt liest er höchstens die ersten 30 Zeilen, denn das Register ist absteigend sortiert und die erste Datenzeile trägt die höchste Nummer. Zwei weitere Stellen, die das Register nach einem bestimmten Eintrag durchsuchten, suchen nun mit grep statt zu lesen. Derselbe Gedanke führte zu zwei Änderungen an CLAUDE.md: Die Roadmap-Tabelle der Produktbeschreibung, 89 Prozent dieser Datei, zog in eine eigene Datei, und der Feature-Index wird nicht mehr per Import hereingezogen. Der Agent liest beides, wenn er es braucht, und die Skills, die es brauchen, sagen das ausdrücklich. Zusammen 25.500 Tokens.

Nicht jedes vollständige Lesen ist Verschwendung. Die Durchsicht aller Skills fand drei, die bleiben bleiben. Den Feature-Index lesen acht Skills vollständig, weil der Kontext dort tatsächlich gebraucht wird. Die Produktbeschreibung ist nach der Überarbeitung klein. Der Testfall-Index wird dort angehängt, wo er gelesen wird. Der Hebel greift bei Anweisungen der Form „lies die Datei, um einen einzelnen Wert zu finden“. Bei einer Datei, deren gesamter Inhalt die Entscheidung trägt, greift er nicht.

Die Überarbeitung lief in fünf Schritten, jeder ein eigener Commit und einzeln revertierbar. Zuerst kamen die rein verschiebenden, dann die Bindung an Pfade als der riskanteste Schritt, zuletzt die Absicherung. Es gab keine ausformulierten Umsetzungsanfrage. Der Agent hatte aus der Analyse eine Spezifikation vorgeschlagen und dazu Fragen gestellt, die der Betreuer durch Auswahl beantwortete. Fragen und Antworten stehen im Transkript wörtlich, hier aus dem Deutschen übersetzt:

Wie weit soll ich die Kontext-Überarbeitung jetzt treiben? -> Alles, inklusive des Scoping-Durchgangs Ein Feature für alle vier Hebel oder aufteilen? -> Ein Feature, vier Bausteine Register- und Layout-Regeln scopen? Das ist der große Posten, aber der riskante. -> Die Entscheidung an /architecture delegieren Die PRD-Roadmap und den Feature-Index von den Always-Includes der CLAUDE.md abkoppeln? -> Ja, mit einem schlanken Rest Soll eine Absicherung gegen Rückfall entstehen? Ohne sie wächst die nächste Regeldatei unbemerkt wieder zu einer Ablagentextwüste. -> Ja, als CI-Guard mit Baseline Enter fullscreen mode Exit fullscreen mode Die vier Hebel in dieser Zählung sind die drei dieses Artikels und der Guard, den dieser Artikel gesondert als Absicherung behandelt.

Die Umsetzungsanfrage danach bestand aus zwei Wörtern: „sofort umsetzen“. Während der Umsetzung kam, nachdem die Lade-Semantik geklärt war, noch eine letzte Frage: ob auch die Register- und Layout-Regeln gebunden werden sollten, nach damaliger Schätzung zusammen 115.000 Tokens, darunter die Registerregel mit gemessenen 70.700. Die Antwort war ja, und das wurde der größte Posten der Einsparung.

Die Akzeptanzkriterien, darunter die Messung mit /context in einer frischen Sitzung und der Subagent, der neu starten muss, hatte der Agent in die Spezifikation geschrieben. Der Betreuer übernahm sie mit der Spezifikation, statt sie zu formulieren, schreibt kein Node und hätte das Prüfskript nicht schreiben können. Die Überarbeitung der Regeldateien, das Skript und die Entscheidung, welche Regeln global bleiben, arbeitete der Agent aus.

Vom Betreuer kamen die Problembeschreibung, die Antworten oben und am Ende die Messung selbst: /context und der Subagent-Test in einer frischen Sitzung, von Hand gestartet, denn eine laufende Sitzung kann ihren eigenen Startzustand nicht neu messen. Entscheidend war, dass die Messung von Anfang an als Akzeptanz in der Spezifikation stand und nicht nach dem Ergebnis festgelegt wurde.

Die Messung wurde mit dem Werkzeug selbst genommen. Beide Zustände lagen als separate Git-Worktrees vor, /context war die erste und einzige Eingabe einer neuen Sitzung ohne die interaktive Oberfläche, Claude Code 2.1.145 mit dem Modell Opus 5.5, beide Läufe am 15. Oktober 2026, Minuten auseinander auf derselben Maschine. Ein Gegencheck in einer interaktiven Sitzung zählte Datei für Datei dasselbe.

Der zweite Beweis ist ein Ja oder Nein. Derselbe Subagent, der zuvor abgebrochen war, startete nach der Überarbeitung in einer frischen Sitzung mit 66.887 Tokens Kontext und beantwortete die Testfrage richtig.

Das Wachstum aus dem Abschnitt „Mit der Arbeit gewachsen“ kam von einer Pflegepflicht ohne Obergrenze. Ohne Obergrenze wäre die Einsparung ein Schnappschuss, und der nächste gut gemeinte Bestand hätte in einem halben Jahr dieselbe Zahl erneut erzeugt. Mit der Überarbeitung kam daher ein Prüfskript in die CI, ohne Abhängigkeiten, als blockierender Schritt. Es rechnet mit Bytes geteilt durch 2.0, dem gemessenen Verhältnis, und lag am Stand des 15. Oktober damit etwa zwei Prozent neben /context. Das ist eine Näherung mit dem Verhältnis eines Tages, keine Messung. Gemessen wird weiter mit /context, der Guard zieht nur die Linie, und das Budget von 80.000 Tokens lässt gegenüber den gemessenen 64.700 Raum für den Fehler der Näherung. Es prüft sieben Dinge:

Die Wache prueft Dateien, nicht Verhalten. Ob ein Skill tatsaechlich eine gebundene Regel liest, bevor er prueft, kann sie nicht sehen. Den Review-Skill ohne SQL-Regeln aus dem Abschnitt davor hat die Qualitaetssicherung gefunden, nicht die Wache.

Der erste Lauf brachte sofort einen Befund, den niemand erwartet hatte. Der Check auf verwaiste Inventory-Eintraege fand fuenf Testdateien ohne Inventory-Zeile, alle aus der Zeit vor der Ueberarbeitung. Die Pflicht „eine Zeile fuer jede neue Testdatei“ hatte das Inventory so auf 104.000 Tokens wachsen lassen und war dennoch nicht eingehalten worden. Eine Pflicht, die niemand prueft, haelt daher weder das Inventory vollstaendig noch die Regeldatei klein.

Die Zerlegung der groessten Komponente endete mit einer zweiten Wache nach demselben Muster, diesmal fuer Quelldateien. Sie prueft jede Datei unter src/ auf hoechstens 1.000 Zeilen, Tests eingerechnet, mit einer Baseline fuer den Altbestand, die nur schrumpfen darf, und einer Toleranz von zwei Prozent, damit ein Bugfix an einer alten Datei durchgeht und ein Feature nicht. Ihr erster Lauf fand 33 Dateien ueber der Grenze, erwartet waren sechs. Die Regel dazu sitzt nicht in einer Datei, die bei jedem Start geladen wird, sondern in der Regel fuer Komponenten, Seiten und Hooks, die an genau diese Pfade gebunden ist. Sie gelangt damit nur in die Sitzungen, in denen eine solche Datei wachsen kann. Fuer alle anderen Dateien meldet sich die Wache von selbst.

Regeln so zu kuerzen, bis sie nichts mehr sagen, ist es nicht. Nach der Ueberarbeitung blieben 20.300 Tokens reine Regelprosa in der Testregel, und das Abnahmekriterium hatte „hoechstens 12.000“ gefordert. Diese Zahl stammte aus der Faustregel des Vortags, mit demselben Fehler wie alle Schaetzungen. Die Zahl wurde korrigiert, nicht die Regel. Was eine Regel sagt, hat sie sich durch einen Bug verdient, und ein Tokenbudget ist kein Grund, das wieder zu vergessen.

Eine Produktkritik ist es ebenfalls nicht. Der Bindungsmechanismus existierte die ganze Zeit, er war dokumentiert, und er hat nachweislich funktioniert. Dass 3 von 30 Dateien ihn nutzten, ist eine Frage der Pflegetreue. Dass ein Agent eine Regel in seinem Kontext hat, wenn er sie braucht, ist zudem die Voraussetzung dafuer, dass er die Regel ueberhaupt befolgt.

Und am wenigsten ist es ein Check, der mit derselben falschen Annahme rechnet wie das, was er prueft. Die Wache aus dem Abschnitt „Ohne Guardrail kommt das Wachstum zurueck“, das Pruefskript in CI, wurde zunaechst nicht mit den 3,7 der ersten Schaetzung kalibriert, sondern mit einer zweiten falschen Zahl: Die Fehlermeldung des gescheiterten Subagenten nannte neben den 347.814 Tokens der gesamten Anfrage rund 227.000 Tokens fuer die Konversation, und die Dateigroesse geteilt durch diese Zahl ergab 4,0 Bytes pro Token. Nur zaehlte die Nachricht nicht dasselbe wie /context.

Sie stammte von einem anderen Modell und bezog sich auf einen Teil der Anfrage. Gemessen sind es 2,0. Qualitaetssicherung und Code-Review rechneten mit demselben Faktor, bestaetigten „33.000 von 45.000 Tokens“ und hielten das Ziel fuer erreicht.

Richtig waren 64.700 von 80.000. Das kam erst ans Licht, als /context in einer frischen Sitzung lief. Zwei Checks, die Gruen melden, beweisen nichts, wenn sie dieselbe Zahl aus derselben Quelle nehmen.

Warum ein gruenes Testergebnis auf genau diese Weise genau den Bug bestaetigen kann, den es finden sollte, zeigt Der gruene Test, der nichts beweist.

Eine Einschraenkung gehoert hierher. Die Analyse, die diesen Artikel ausgeloest hat, lief in derselben Sitzung, deren Kontextfenster sie untersuchte. Sich selbst zu messen kostet Kontext.

Wie misst man, was eine Claude-Code-Sitzung beim Start laedt?

Mit dem Befehl /context als erste Eingabe einer neuen Sitzung. Er listet jede geladene Instruktionsdatei einzeln mit ihrer Tokenzahl auf. Fuer eine wiederholbare Messung genuegt claude -p "/context" ohne die interaktive Oberflaeche, geprueft mit Claude Code 2.1.145. In Git Bash muss dafuer die Pfadkonvertierung ausgeschaltet sein ( MSYS_NO_PATHCONV=1 ), sonst erreicht /context das Modell als Dateipfad. Die frische Sitzung ist entscheidend: Eine laufende Sitzung oder ein Subagent traegt den Instruktionszustand von seinem eigenen Start mit sich.

Reicht es nicht, Zeichen zu zaehlen und durch vier zu teilen?

Fuer englische Prosa ist das ungefaehr richtig, fuer deutschsprachiges Markdown mit Tabellen, Pfaden und Code-Bezeichnern ist es das nicht. Gemessen lagen die Regeldateien bei 2,0 Bytes pro Token, je Datei zwischen 1,9 und 2,2. Eine Schaetzung mit dem Faktor 3,7 kam fast halb so gross zu niedrig herein, und eine Wache, die mit 4,0 rechnete, halbierte alle Ergebnisse, ohne dass jemand es bemerkte. Hinzu kommt, dass die Zahl vom Modell abhaengt: Der neuere Tokenizer ab Claude 4.7 zaehlt denselben Text mit rund 30 Prozent mehr Tokens als der aeltere, je nach Text etwas mehr oder weniger.

Laedt eine Regel mit paths: rechtzeitig, wenn der Agent eine neue Datei anlegt?

Nein. Die Regel kommt fruehestens mit dem Ergebnis des Toolaufrufs an, fuer eine neue Datei ist der Inhalt bis dahin schon geschrieben, und vor Version 2.1.288 lud ein Write-Aufruf sie ueberhaupt nicht. Ob der Agent die Datei dann von selbst nachzieht, haengt vom Lauf ab. Beim Bearbeiten einer bestehenden Datei gibt es keine Luecke, weil das Edit-Tool ein vorheriges Lesen verlangt. Fuer Regeln, die beim Anlegen wirken muessen, bleiben drei Wege: sie global ohne paths: lassen, sie in dem anlegenden Skill vorher explizit lesen und eine Karte in CLAUDE.md pflegen, damit der Agent weiss, dass die Regel existiert. Geprueft in Claude Code 2.1.145, 2.1.292 und 2.1.301.

Weil jede Regel aus einem Bug stammt, der ohne sie zurueckkommt. Die Testregel des DI²-Projekts behielt nach der Ueberarbeitung 20.300 Tokens reine Regelprosa, obwohl das Ziel „hoechstens 12.000“ lautete: Die Zielzahl stammte aus einer falschen Schaetzung, und korrigiert wurde die Zahl. Die Einsparungen kommen daher, wann eine Regel laedt, und daraus, dass sie nicht mehr mit Ablauftext vermischt wird, nicht aus ihrem Inhalt. Wer dennoch kuerzen will, prueft zuerst, ob ein Abschnitt eine fruehere Entscheidung belegt. Dann ist er Ablauf und kann ausziehen.

Sieht der Agent eine gebundene Regel noch, wenn er keine passende Datei anfasst?

Nur wenn er weiss, dass die Regel existiert. Eine Frage zur SQL-Benennungskonvention im Gespraech laedt die SQL-Regel nicht, weil keine Datei unter db/ gelesen wird. Dasselbe gilt fuer ein Review, das den Diff ueber git diff liest statt ueber eine Datei. Deshalb haelt CLAUDE.md eine Karte mit einer Zeile pro gebundener Regel, und Skills, die ohne Dateizugriff pruefen, lesen die relevanten Regeln explizit. Sonst waere Bindung ein unbemerkter Verlust von Konventionen.

Automatisch übersetzt. Das Original auf Englisch ist unten aufklappbar.

Original (Englisch) anzeigen

Posted on Oct 22 • Originally published at sql.marcus-belz.de

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.

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.

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" Enter fullscreen mode Exit fullscreen mode In the DI² repository, this gave the following on October 15, 2026, with the Opus 5.5 model, before any change:

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? Enter fullscreen mode Exit fullscreen mode 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.

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.

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.

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.

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.

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.

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. Enter fullscreen mode Exit fullscreen mode 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.

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.

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/**" --- Enter fullscreen mode Exit fullscreen mode 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.

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. --> Enter fullscreen mode Exit fullscreen mode 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/** Enter fullscreen mode Exit fullscreen mode 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.

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. Enter fullscreen mode Exit fullscreen mode 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) | Enter fullscreen mode Exit fullscreen mode 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.

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.

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 Enter fullscreen mode Exit fullscreen mode 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.

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.

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:

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.

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.

How do you measure what a Claude Code session loads at start?

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.

Isn't it enough to count characters and divide by four?

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.

Does a rule with 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.

Does the agent still see a bound rule when it touches no matching file?

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.

Originaltitel: The Context Window Is Full Before the First Prompt — When Project Rules in Claude Code Become a Fixed Cost of Every SessionQuelle: dev.to (dev.to). Original öffnen