{"id":689,"date":"2026-09-16T05:01:14","date_gmt":"2026-09-16T05:01:14","guid":{"rendered":"https:\/\/abrarqasim.com\/blog\/claude-code-context-window-compaction-the-rule-that-vanished\/"},"modified":"2026-09-16T05:01:14","modified_gmt":"2026-09-16T05:01:14","slug":"claude-code-context-window-compaction-the-rule-that-vanished","status":"publish","type":"post","link":"https:\/\/abrarqasim.com\/blog\/claude-code-context-window-compaction-the-rule-that-vanished\/","title":{"rendered":"Claude Code Context Window Compaction: The Rule That Vanished"},"content":{"rendered":"<p>Okay, this is going to sound dumb, but I spent most of Thursday afternoon arguing with a coding agent about a rule I had given it three hours earlier. The rule was &ldquo;don&rsquo;t touch the migrations folder, I&rsquo;ll handle schema changes by hand.&rdquo; It agreed. It followed the rule for a long stretch. Then, somewhere around the point where the session got long enough to be summarised, it cheerfully rewrote two migration files and told me it had &ldquo;cleaned up the schema drift.&rdquo;<\/p>\n<p>I didn&rsquo;t lose anything important. Git exists. But I did lose an hour working out why an instruction that had been obeyed for three hours suddenly stopped existing, and the answer turned out to be the most boring one available: the conversation got compacted, and my rule didn&rsquo;t survive the summary.<\/p>\n<p>That would&rsquo;ve stayed a private grumble, except three things landed in my reading pile the same week that all describe the same failure from different angles. Simon Willison hit it in ChatGPT. Two papers measured it. And the Claude Code docs, when I finally read them properly, admit it in plain language. So this post is me connecting those dots, and then listing what I actually changed in my setup.<\/p>\n<h2 id=\"what-compaction-is-in-one-paragraph\">What compaction is, in one paragraph<\/h2>\n<p>Every agent that runs long enough fills its context window. When that happens, the system has two options: refuse to continue, or summarise the older part of the conversation into something shorter and carry on. Nearly every product picks the second one. Claude Code calls it compaction and has a <code>\/compact<\/code> command. Codex does it automatically. ChatGPT does it inside long threads. The summary is written by a model, which means it&rsquo;s a lossy, judgement-based rewrite of everything that came before. Whatever the summariser decides isn&rsquo;t important enough to keep is gone, and nothing in the UI tells you what was dropped.<\/p>\n<p>If you&rsquo;ve read my post on <a href=\"https:\/\/abrarqasim.com\/blog\/ai-agent-memory-is-a-dose-not-a-switch\" rel=\"noopener\">agent memory being a dose rather than a switch<\/a>, compaction is the flip side of that argument. Memory is what you choose to carry forward. Compaction is what the system chooses to throw away on your behalf, and it chooses badly in a specific, predictable way.<\/p>\n<h2 id=\"simons-running-route-and-the-code-that-vanished\">Simon&rsquo;s running route, and the code that vanished<\/h2>\n<p>Simon Willison <a href=\"https:\/\/simonwillison.net\/2026\/Sep\/12\/astra-running-routes\/\" rel=\"nofollow noopener\" target=\"_blank\">asked ChatGPT Work<\/a> to generate 5K and 10K running loops from his house using OpenStreetMap data. It worked for 27 minutes and produced a map, a GPX file, and GeoJSON. Good result. Then he asked for the Python it had written to do the routing, and it couldn&rsquo;t give it to him. The thread had been compacted, and the code was in the part that got summarised away.<\/p>\n<p>His conclusion was that any system using compaction needs to keep the pre-compaction text and expose it to the agent through a tool call. I think that&rsquo;s exactly right, and I&rsquo;d go one further: it should be exposed to the user too, as a plain &ldquo;show me what got summarised&rdquo; button. The fact that a product can do 27 minutes of work and then be unable to tell you what it did is a design bug, not a model limitation.<\/p>\n<p>What struck me about his example is that it&rsquo;s the friendly version of my migrations problem. He lost an artefact. I lost a constraint. Losing an artefact is annoying. Losing a constraint is how you get an agent that starts doing the thing you specifically told it not to do, while believing it&rsquo;s being helpful.<\/p>\n<h2 id=\"the-two-papers-that-put-numbers-on-it\">The two papers that put numbers on it<\/h2>\n<p>I&rsquo;m wary of arxiv papers on agent behaviour because a lot of them measure a toy setup and extrapolate. These two are more grounded than most, and their numbers line up with what I saw.<\/p>\n<p>The first is <a href=\"https:\/\/arxiv.org\/abs\/2608.11242\" rel=\"nofollow noopener\" target=\"_blank\">Lost in Compaction<\/a>, which names the thing I lost: a &ldquo;session constraint,&rdquo; meaning an instruction like &ldquo;do not delete any emails until I confirm&rdquo; that&rsquo;s meant to hold for the rest of the session. They built a suite called COMPINT that injects constraints into multi-turn chat, agentic trajectories, and long research tasks, then measures whether they survive compaction. The headline number is that current compactors keep 17% of injected constraints on average. Seventeen. And most of the tested setups did worse on the task with compaction than the same task run without it. Their fix is a small extractor that runs alongside the compactor and pulls constraints out into a separate list, which got retention above 90% without touching the underlying model.<\/p>\n<p>The second is <a href=\"https:\/\/arxiv.org\/abs\/2608.22752\" rel=\"nofollow noopener\" target=\"_blank\">The Compaction Cliff<\/a>, which is narrower and, for me, scarier. They took 20 production agent configurations and ran Claude Code&rsquo;s own <code>\/compact<\/code> prompt on Sonnet 4.6. After one round of compaction, 53% of safety rules survived. After five rounds, 10%. The framing they use is that a safety rule and an episodic log compete for the same tokens, and the summariser compresses both at the same rate, even though only one of them needs its exact wording to still be enforceable. Their answer is to classify each line of the agent&rsquo;s knowledge by type and give each type its own retention policy, so rules get pinned and logs get squashed.<\/p>\n<p>I want to be careful about how much I lean on these. Both were tested on specific compactors at specific dates, and the vendors change their summarisation prompts without announcing it. The 53% figure might already be different. But the shape of the problem, that rules decay faster than you&rsquo;d expect and compound across rounds, matches my Thursday exactly.<\/p>\n<h2 id=\"what-the-claude-code-docs-actually-say\">What the Claude Code docs actually say<\/h2>\n<p>This is the part where I felt a bit silly. The <a href=\"https:\/\/code.claude.com\/docs\/en\/how-claude-code-works\" rel=\"nofollow noopener\" target=\"_blank\">Claude Code documentation<\/a> on context says, in plain words, that Claude compacts automatically and that &ldquo;instructions from early in the conversation can get lost.&rdquo; It tells you to put persistent rules in <code>CLAUDE.md<\/code> rather than relying on conversation history. It describes the order of operations, too: older tool outputs get cleared first, and the conversation is summarised only if that isn&rsquo;t enough. Your requests and key code snippets are preserved. Detailed instructions from early on may not be.<\/p>\n<p>That&rsquo;s the vendor telling you the exact failure mode, and I&rsquo;d skimmed past it. The docs also mention a &ldquo;Compact Instructions&rdquo; section you can add to <code>CLAUDE.md<\/code> to control what the summary keeps, and a <code>\/compact focus on X<\/code> form for steering a manual compaction. I had used neither.<\/p>\n<p>So the rule I gave the agent in chat was, by the vendor&rsquo;s own description, in the wrong place. The rule belonged in a file. I put it in a message. Chat messages are the thing that gets summarised.<\/p>\n<h2 id=\"what-i-changed\">What I changed<\/h2>\n<p>Here&rsquo;s the concrete bit. Four changes, and the first one took two minutes.<\/p>\n<p>First, anything that&rsquo;s a standing rule for a project now lives in <code>CLAUDE.md<\/code>, and I added the compact instructions section the docs describe:<\/p>\n<pre><code class=\"language-markdown\"># Project rules\n\n- Never modify files under database\/migrations. Schema changes are done by hand.\n- Do not run `php artisan migrate:fresh` under any circumstances.\n- Ask before deleting any file that isn't in the current diff.\n\n## Compact Instructions\n\nWhen compacting, preserve verbatim: every rule in the &quot;Project rules&quot; section\nabove, the list of files changed so far in this session, and any command the\nuser told you not to run.\n<\/code><\/pre>\n<p>Second, I stopped treating chat as the place where code lives. If the agent writes a script that matters, it writes it to disk, in the repo, before doing anything else. Simon&rsquo;s routing code would&rsquo;ve survived if it had been a file. This is also just better hygiene for the reason I wrote about in the <a href=\"https:\/\/abrarqasim.com\/blog\/laravel-ai-sdk-0-11-i-can-finally-see-what-the-agent-did\" rel=\"noopener\">Laravel AI SDK 0.11 post<\/a>: if you can&rsquo;t see what the agent did, you can&rsquo;t review it, and a compacted transcript is the extreme case of not being able to see.<\/p>\n<p>Third, for long sessions I now run <code>\/compact focus on the project rules and the list of changed files<\/code> myself before the auto-compaction kicks in, instead of letting it pick the moment. I can&rsquo;t prove this is better than the automatic version. It feels better, and it means the summary is written while I&rsquo;m paying attention instead of while I&rsquo;m getting coffee.<\/p>\n<p>Fourth, and this is for the agents I build for clients, as opposed to the ones I only use: constraints go in a separate channel from the transcript. The COMPINT paper&rsquo;s extractor is basically this idea, and you don&rsquo;t need their code to do a crude version of it. In the small Python agent loop I use for <a href=\"https:\/\/abrarqasim.com\/work\" rel=\"noopener\">client automation work<\/a>, it looks like this:<\/p>\n<pre><code class=\"language-python\">class Session:\n    def __init__(self):\n        self.transcript = []      # gets compacted\n        self.constraints = []     # never compacted, always re-injected\n\n    def add_user_message(self, text):\n        self.transcript.append({&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: text})\n        if looks_like_constraint(text):\n            self.constraints.append(text)\n\n    def build_prompt(self):\n        pinned = &quot;\\n&quot;.join(f&quot;- {c}&quot; for c in self.constraints)\n        system = f&quot;{BASE_SYSTEM}\\n\\nStanding rules for this session:\\n{pinned}&quot;\n        return system, self.transcript\n\n    def compact(self, summariser):\n        summary = summariser(self.transcript)\n        self.transcript = [{&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: f&quot;Summary so far: {summary}&quot;}]\n        # self.constraints untouched\n<\/code><\/pre>\n<p><code>looks_like_constraint<\/code> is embarrassingly simple in my version: a regex for &ldquo;don&rsquo;t&rdquo;, &ldquo;never&rdquo;, &ldquo;do not&rdquo;, &ldquo;until I&rdquo;, &ldquo;ask before&rdquo;. It over-triggers, which is the correct direction to fail in. A pinned rule that didn&rsquo;t need pinning costs a few tokens. An unpinned rule that did need pinning costs a migration file.<\/p>\n<p>I also keep the pre-compaction transcript on disk and give the agent a <code>read_earlier_transcript<\/code> tool, which is the thing Simon asked for. It&rsquo;s a few lines of code and it means &ldquo;what code did you run earlier&rdquo; has an answer even after three compaction rounds.<\/p>\n<h2 id=\"the-part-im-still-unsure-about\">The part I&rsquo;m still unsure about<\/h2>\n<p>I don&rsquo;t know how far to trust the compaction prompts inside the tools I don&rsquo;t control. The Compaction Cliff numbers are for one version of one prompt on one model. Anthropic and OpenAI both change these things quietly. It&rsquo;s possible the 53% figure is already out of date in either direction, and I have no way to check short of re-running their benchmark, which I&rsquo;m not going to do on a Thursday.<\/p>\n<p>What I&rsquo;m fairly confident about is the structural point. As long as rules and logs are summarised by the same process, rules will decay, and the decay compounds. That&rsquo;s true regardless of which vendor&rsquo;s prompt is best this month. The fix is to stop putting rules in the thing that gets summarised.<\/p>\n<h2 id=\"do-this-before-your-next-long-session\">Do this before your next long session<\/h2>\n<p>Open the <code>CLAUDE.md<\/code> (or the equivalent for whatever agent you use) in the project you&rsquo;re working on right now. Move every &ldquo;never do X&rdquo; and &ldquo;ask before Y&rdquo; you&rsquo;ve ever typed into chat into that file. Add a compact instructions section that tells the summariser to keep those lines verbatim. Then start a long session and run <code>\/context<\/code> a few times to watch what&rsquo;s eating space.<\/p>\n<p>If you build agents as well as use them, add a constraints list next to your transcript and re-inject it on every turn. Write the transcript to disk before you compact it. Neither of those is clever. Both would&rsquo;ve saved me an hour.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Context compaction quietly drops the rules you give a coding agent. Two papers put numbers on it (17% and 53% retention). Here&#8217;s what I moved into CLAUDE.md.<\/p>\n","protected":false},"author":2,"featured_media":688,"comment_status":"","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"","rank_math_description":"Context compaction quietly drops the rules you give a coding agent. Two papers put numbers on it (17% and 53% retention). Here's what I moved into CLAUDE.md.","rank_math_focus_keyword":"claude code context window","rank_math_canonical_url":"","rank_math_robots":"","footnotes":""},"categories":[4],"tags":[363,108,767,766,765,546,5],"class_list":["post-689","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai","tag-ai-agents","tag-claude-code","tag-claude-md","tag-coding-agents","tag-compaction","tag-context-window-2","tag-llm"],"_links":{"self":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/posts\/689","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/comments?post=689"}],"version-history":[{"count":0,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/posts\/689\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/media\/688"}],"wp:attachment":[{"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/media?parent=689"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/categories?post=689"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/abrarqasim.com\/blog\/wp-json\/wp\/v2\/tags?post=689"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}