Claude Code Context Window Management
What the Claude Code context window actually is
The Claude Code context window is the working memory of a single session. Every system prompt, CLAUDE.md file, tool result, file Claude opens, and message you send sits in that window. When the model generates a response, the entire window goes in and the response comes back. There is no separate "memory" Claude consults outside of what fits in this window.
Two numbers define the working limit. Opus 4.7 ships with a 1 million token context window in Claude Code. Sonnet 4.5 ships with a 200,000 token window. Both are theoretical caps. The practical limit where quality degrades sits well below them, because the model has to attend to everything in the window when generating each response, and attention quality drops as the window fills with stale context.
A million tokens sounds like room to never worry. In practice, a long debugging session in a large codebase fills 60 percent of an Opus context window in under two hours. A thousand-line file is around 5,000 tokens. A bash output dump is often 2,000 to 10,000. A failed test run with stack traces can be 8,000. The window fills faster than developers expect, and the failure mode is silent: Claude does not warn you that it has forgotten the bug you were chasing two files ago, it just stops referencing it.
For the broader token math and per-category sizes, the Claude Code context window guide covers the primer. This post is about what to do once the window starts to fill, and how to make Claude clean up after itself in a way that respects what you actually care about.
The compaction problem
Claude Code has automatic compaction. When the context approaches the model limit, the harness pauses the session, runs a summarisation pass over the conversation, replaces the long history with a compressed summary, and resumes. The summary is meant to preserve the important context. In practice, "important" is whatever the summariser prioritises, which is often not what you would have prioritised.
The three things automatic compaction routinely loses:
Recently identified bugs that have not been fixed yet. Claude found a race condition at auth.ts:240 an hour ago. You said "finish the form validation first, come back to that". The form work generated 30,000 tokens of file reads. Auto-compaction summarises it as "discussed auth bugs" without preserving the line or the cause. You come back to fix it and Claude has to rediscover the same bug.
Decisions made during the session that override defaults. "Use Drizzle, not Prisma." "Skip strict mode on legacy/payments.ts." Auto-compaction drops these because they sound like discussion turns rather than load-bearing decisions. After compaction Claude reverts to the default and the work loses internal consistency.
Test results and verification state. "We confirmed the API returns 200 with the new schema." "The race condition only reproduces under pod 3." Compaction summarises these as "discussed test results" and the next time the question comes up you have to re-verify from scratch.
The compaction problem is not that compaction happens. It is that automatic compaction has no idea what your current task cares about, so it averages across the whole session and drops the specifics you needed it to preserve.
The /compact command with an argument
The /compact command in Claude Code is the manual control. Run it at a clean boundary in your work and it triggers the same compaction process, but on your schedule rather than at the random moment when the window fills.
Bare /compact runs the default compaction, which has the same blind-summariser problem as auto-compact, just at a moment you chose. The version that actually matters takes an optional argument: a free-text instruction that tells the compaction summariser what to preserve.
/compact preserve the auth race condition we identified at auth.ts:240, the
decision to keep strict mode off for legacy/payments.ts, and the failing test
case in __tests__/checkout.spec.ts that reproduces the duplicate charge bug
Everything after /compact becomes a steering instruction for the summarisation pass. The summariser still compresses the bulk of the conversation, but it treats the listed items as load-bearing and writes them into the post-compact summary verbatim or near-verbatim. The result is a compacted session that retains the specific threads you were carrying, with the tokens of the long history reclaimed.
Run /compact with an argument at every clean boundary in the work: just shipped a feature, just wrote a passing test, just closed an investigation phase. The argument has to be specific. "Preserve the bugs" is too vague for the summariser to act on. "Preserve the race condition at auth.ts:240, the schema mismatch in user.ts, and the failing test in user.spec.ts" is specific enough.
Three kinds of items belong in the argument: open bugs (file path + line + one-line cause), decisions that override defaults ("use approach X, not Y"), and empirically verified facts ("test X passes after change Y", "reproduces on Node 18 only"). Avoid dumping every detail. Six to ten specific items work. Twenty becomes noise that defeats the purpose.
Compact Instructions in CLAUDE.md
The /compact argument is great for the current session, but it relies on you remembering to type the right things at the right moment. The version that scales is a "Compact Instructions" section in your CLAUDE.md file that makes every auto-compaction permanently project-aware.
Claude Code reads CLAUDE.md at session start and respects it as a persistent instruction set. A "Compact Instructions" section in that file is consulted by the compaction summariser when it runs, whether triggered manually or automatically. This is the upgrade that turns compaction from a lossy compression into a project-aware checkpoint.
A working example for a typical full-stack project:
## Compact Instructions
When compacting this conversation, always preserve:
1. Any open bug or issue identified during the session that has not been
fixed, including file path and line number if known.
2. Any decision made to override project defaults: which migration tool,
which validation approach, which auth flow, which database client.
3. The current branch name and the last successful build / test status.
4. Any failing tests, including the test file path and the failure reason.
5. Environment variables that were added or changed during the session.
6. Schema changes made to the database during the session.
7. Any third-party API quirks discovered (rate limits, schema gotchas, edge
cases) that are not yet documented elsewhere in the codebase.
Do not compact away these items even if the conversation around them is long.
Summarise the discussion, but keep the specific identifiers (file paths, line
numbers, env var names, branch names) verbatim.
This section sits in CLAUDE.md at the project root. Every session in this project reads it. Every compaction respects it. The auto-compact failure mode of dropping the auth race condition disappears because the instructions explicitly tell the summariser that open bugs are load-bearing.
The "summarise the discussion, but keep the specific identifiers verbatim" instruction is the most important one. Without it, the summariser will compress "we found a race condition at auth.ts:240 caused by missing await on the session fetch" into "discussed auth bugs", losing the file path that lets the next turn reload the file and fix it. With it, the file path and the cause survive the compaction.
Claude Code with CLAUDE.md covers the broader CLAUDE.md pattern and what other sections belong in it. The compact instructions sit alongside coding conventions, stack rules, and project-specific gotchas. They are not a replacement for those, they are a complement: the rules tell Claude how to write code, the compact instructions tell Claude what to remember about the work-in-progress.
A useful add-on clause sets a soft floor on session length: if a session has fewer than 20 tool calls or ten file reads, decline to compact and ask the user to confirm. Compaction is not free, it always loses some signal, and running it on a session that has not filled up is pure cost.
/context: the diagnostic that tells you when
Knowing when to compact is the other half of the problem. /context is the diagnostic that gives you the per-category breakdown of where your tokens have gone in the current session.
Running /context returns something like:
Context: 187,420 / 200,000 tokens (93.7%)
By category:
System prompt: 4,200
CLAUDE.md (loaded): 12,800
Conversation: 88,400
Tool results: 71,300
Pending response: 10,720
Tool results is usually the biggest source of bloat. Every Read, Bash, and Grep call returns content that goes into context. A single grep across a large repo can drop 30,000 tokens in one call. The fix is targeted reads: specific file paths instead of broad globs, narrow line ranges instead of full files, head or wc -l for size checks before dumping a whole result.
CLAUDE.md (loaded) adds up in nested projects. Keep each CLAUDE.md under 200 lines and move stack-specific rules into subdirectory CLAUDE.mds only where they are actually needed.
Conversation is the messages plus responses. When this category dominates, the session has been long and chatty, which is the case for /compact with an argument.
The threshold to act: above 60 percent, plan a clean boundary for compaction. Above 80 percent, compact at the next safe stopping point. Above 90 percent, compact now before the harness auto-compacts at a random moment.
Quality tiers and the hook system
If you have a tracking hook set up (the track-session-quality.sh pattern that ships with Claudify), the system also tags each turn with a quality tier based on tool count, file re-reads, and heavy-operation weighting. The tiers are advisory, not prescriptive, but they give you a sense of where the session sits beyond the raw token count.
| Tier | What you notice | What to do |
|---|---|---|
| Pristine | Sharp reasoning, perfect recall, fast turns | Keep going |
| Optimal | Strong work, occasional missed detail | Keep going |
| Watch | Re-reading own outputs, slight repetition | Plan a compact at next clean boundary |
| Degraded | Forgetting earlier instructions, sloppy edits | Compact now |
| Critical | Imminent overflow, severe quality drop | Compact before next tool use |
The tier is computed from observed behaviour, not raw context size, so it catches sessions that have only used 40 percent of the window but have been re-reading the same files repeatedly. Re-read frequency is a stronger predictor of imminent quality drop than raw token count. The hook writes a counter to disk on each tool use, applies a multiplier for re-reads and known heavy ops, and reports the current tier through the Stop hook. Full hook code and canonical thresholds sit in the Claude Code hooks reference.
When NOT to /compact
Compaction is destructive. Even with steering instructions, summarisation loses signal the model could have used. The cost is low at a clean boundary, high in the middle of a task that depends on the full conversation. Three moments to avoid it:
Mid-debug. Claude is chasing a bug, has six files loaded, ran two diagnostic commands, is forming a hypothesis. The conversation history is the working set the model is reasoning over. Compacting compresses the working set into a summary and the half-formed hypothesis evaporates.
Mid-refactor. Claude is moving code across files, tracking references. Auto-compaction at the wrong moment drops the cross-file context and leaves the refactor half-applied. Finish the current step (every file builds), then compact.
Just received bad news. A test failed, a deploy was rejected, a production bug was reported. The natural impulse is to investigate and compact when context fills. The better pattern: don't compact until the immediate fire is out. Compacting the diagnostic context mid-investigation throws away the work you just did to assemble it.
The opposite anti-pattern: keeping a session running so long that quality has visibly dropped in the hope that compacting will lose more than it preserves. By the time a session is in Degraded tier, compaction is usually a net improvement. A clean summary plus current work beats a stale conversation full of dead context the model is half-ignoring.
Context poisoning after compaction
The other failure mode after compaction is "context poisoning". The summariser writes a summary that contains a mistaken claim, the post-compact session reads the summary as authoritative, and the model proceeds on the bad fact for the rest of the session.
A real example: a session debugging a Stripe webhook chased several wrong hypotheses about the signature header before landing on the actual cause (a body parser middleware consuming the raw body before signature verification). When the session compacted, the summariser pulled the "signature header is wrong" hypothesis into the summary because it had been discussed more, and the correct cause appeared only as one resolved line. The post-compact session believed the summary and re-implemented the wrong fix.
The defence is in Compact Instructions: tell the summariser to preserve the final resolved state, not the intermediate hypotheses.
## Compact Instructions (additional)
When summarising a debugging arc, preserve only the final resolved cause and
fix. Do not summarise intermediate hypotheses that were ruled out, except as
a brief note that they were considered and rejected.
If a bug investigation is still open at compaction time, mark it as open and
preserve the current best hypothesis verbatim, not the chain of earlier
hypotheses that did not pan out.
The combination of /compact with arguments and a sharp Compact Instructions section in CLAUDE.md makes context poisoning rare enough to be a non-issue. The defaults make it routine.
Subagents as an alternative to compaction
Subagents are the other lever. A subagent runs in its own context window, returns a result to the parent session, and discards its own working history when it terminates. The parent pays the cost of the final result, not the cost of the investigation. For broad searches, codebase analysis, and any task that would burn tens of thousands of tokens in the parent session, a subagent is the cleaner pattern. Claude Code custom subagents covers setup and which tasks benefit. Every task you delegate to a subagent is a task you do not have to compact later.
Get Claudify. The ready-made CLAUDE.md templates ship with a Compact Instructions section pre-configured for full-stack projects.
The practical workflow
The day-to-day workflow is short:
- Put a Compact Instructions section in your CLAUDE.md. The example block earlier is a starting point, tuned to the patterns of your project: which file paths matter, which decisions come up, which third-party APIs you wrestle with.
- Run
/contextwhenever a session feels slow or repetitive. Above 60 percent, plan a compact. Above 80, compact at the next clean boundary. Above 90, compact now. - Use
/compactwith an argument that lists the load-bearing items in the current work. Bare/compactis the wrong default once Compact Instructions are in place: the argument adds session-specific steering on top of the project-wide rules. - Don't compact mid-debug, mid-refactor, or mid-fire. Wait for a clean boundary.
- Watch the tier indicator if hooks are set up.
Watchmeans plan a compact,Degradedmeans compact now,Criticalmeans stop before the next tool use. - Delegate investigation and search tasks to subagents instead of running them in the parent session.
- If a post-compact session starts confidently asserting something that was actually a ruled-out hypothesis, add a Compact Instructions clause that preserves resolved state, not intermediate hypotheses.
The Claude Code context window is large, but every session has a finite budget of clear thought. The combination of Compact Instructions in CLAUDE.md, manual /compact at clean boundaries with explicit arguments, and /context as the diagnostic to know when, keeps long sessions sharp without losing the threads that matter. The cost is about ten lines of CLAUDE.md and a habit of checking /context before things start to feel slow.
Claude Code memory systems covers the next layer: what to write to disk so state survives the session ending entirely. The pattern compounds. Persistent memory on disk, project-aware compaction in-session, targeted subagents for delegation, /context to keep an eye on the budget.
Get Claudify. The full CLAUDE.md template, the hook scripts that produce the tier system, and a /safe-clear command that pairs cleanly with /compact are all included.
More like this
Ready to upgrade your Claude Code setup?
Get Claudify