DEV Community

J. Gravelle
J. Gravelle

Posted on

What survives /compact in Claude Code...

...and how to lose less of it

Claude Code doesn't forget things at random when it compacts. There's actually a table in the docs spelling out exactly what survives.

Everything else is left to a model trying to squeeze a few hundred thousand tokens of your afternoon into a summary, including that edge case you spent 40 minutes figuring out.

Two things are worth doing about it: put the important stuff somewhere Claude will find it again, and make compaction happen less often.

That 40-minute example isn't something I made up. An r/ClaudeCode post this weekend put it as "/compact 'fixes' that by summarizing, and then Claude forgets the edge case you spent 40 minutes on" (https://www.reddit.com/r/ClaudeCode/comments/1x9aivo/). The next morning a founder on X was asking about an orchestrator that goes "kind of useless" after a few compactions.

What comes back after compaction

Here's what the "What survives compaction" table in Claude Code's docs says (code.claude.com/docs/en/context-window, read 2026-10-19):

  • The project-root CLAUDE.md and unscoped rules: re-injected from disk.
  • Auto memory: re-injected from disk.
  • The plan Claude wrote in plan mode: re-injected from disk.
  • Files Claude read or edited: up to five come back, most recently modified first. A file over 5,000 tokens comes back as a path with no content.
  • Skill bodies: re-injected, capped at 5,000 tokens per skill and 25,000 total.
  • SessionStart hooks with a compact matcher: run again, and their output is added.
  • Everything else, including the conversation and context earlier hooks added: summarized.

The stuff on disk comes back. The stuff you discussed in the conversation gets summarized. And where did you spend those 40 minutes working out that edge case? In the conversation, naturally.

Put decisions on disk while you work

I'd suggest a short notes file that Claude updates whenever you settle something important. The edge case you found, the approach you rejected and why, the test that reproduces the bug. That sort of thing.

Then have Claude read it back after every compaction. There's a hook for exactly that. Put this in .claude/settings.json at your project root:

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          { "type": "command", "command": "cat .claude/session-notes.md" }
        ]
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

One caution: keep that file short. Everything the hook prints goes right back into context after compaction and rides along on every turn after. Turn your notes into a 3,000-line autobiography and you've invented another way to waste tokens.

One other detail from the same docs page: nested CLAUDE.md files and rules with paths: frontmatter only reload when needed. Otherwise they get summarized away with the rest. If a rule absolutely needs to survive, the root CLAUDE.md is where it belongs.

Tell the summarizer what matters

You can also tell /compact what you want preserved:

/compact Keep the retry edge case in payments/charge.py and the test that reproduces it
Enter fullscreen mode Exit fullscreen mode

Or put standing instructions in CLAUDE.md under a # Compact instructions heading, so you don't have to remember to type the same thing every time. Costs nothing to set up.

Compact on your schedule, not at the cliff

/autocompact 300k tells Claude Code how full the context window can get before it compacts on its own. You can set it anywhere from 100K to 1M.

When you compact also affects what you pay. Per the docs, /compact reads the entire conversation it's summarizing. While the prompt cache is warm, that's relatively cheap. Take a break longer than the cache lifetime (an hour on a subscription, five minutes by default on an API key) and that same history gets processed as uncached input.

So if you're about to go to lunch, compact before you leave. Not when you get back.

And if you're done with the conversation and don't need any continuity, /clear costs nothing.

Compact less often

Compaction happens because the context window fills up, and in a coding session a lot of what fills it is source code.

Claude needs one function. It opens the file containing that function. The entire file gets dragged into context and sits there until the next compaction, even if the function was a tiny fraction of it.

Here's an actual measurement on FastAPI 0.115.0, fastapi/routing.py, counted with tiktoken's cl100k_base:

whole file:          32,397 tokens
get_request_handler:  1,137 tokens
Enter fullscreen mode Exit fullscreen mode

To check it against your own code, here's the script (pip install tiktoken first):

import ast, sys, tiktoken
path, name = sys.argv[1], sys.argv[2]
src = open(path, encoding="utf-8").read()
enc = tiktoken.get_encoding("cl100k_base")
for node in ast.walk(ast.parse(src)):
    if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)) and node.name == name:
        print(f"whole file: {len(enc.encode(src)):,} tokens")
        print(f"{name}: {len(enc.encode(ast.get_source_segment(src, node))):,} tokens")
        break
Enter fullscreen mode Exit fullscreen mode

python fn_vs_file.py fastapi/routing.py get_request_handler

That's about 28 times as much source as the agent came looking for.

And that 5,000-token line from the compaction table? This file is well over it. So after compaction Claude gets the path back, but not the source. If it needs that function again, it may wind up reading the whole dang file again.

Try running /context during one of your coding sessions and see what's eating the window. If it's file reads, you've got options. Ask Claude to read line ranges instead of whole files. Anthropic's own cost docs recommend code intelligence plugins for typed languages. A symbol index is another approach.

Full disclosure: I've got a dog in this hunt. jCodeMunch is my MCP server that parses your repository with tree-sitter and indexes individual symbols. When the agent needs a function, class or method, it asks for it by name and gets just that symbol, pulled by byte offset, without the file around it.

On our published benchmark that came to 96.6% fewer tokens per lookup (29.2x) than grep plus reading the top three files whole, across 15 task-runs on express, fastapi and gin, measured with cl100k_base. The method and the harness are public (github.com/jgravelle/jcodemunch-mcp/tree/main/benchmarks). If you think I've missed something, I'd like to hear about it.

It helps more the bigger the repo. On a small one the built-in search is already cheap.

uv tool install jcodemunch-mcp
jcodemunch-mcp init
Enter fullscreen mode Exit fullscreen mode

Back to compaction. Fewer whole files dragged into context means the window fills more slowly. That means fewer compactions, and fewer chances for the summarizer to drop something you spent half your afternoon figuring out.

Which beats explaining the same bug to Claude three times...


FAQ

Does /compact cost tokens?
Yep. It sends the whole conversation to be summarized. With a warm cache most of that is inexpensive cache reads. After a long enough break you're paying to process the history uncached. /clear costs nothing.

Does CLAUDE.md survive compaction?
The project-root one does. Claude reads it again from disk. Nested CLAUDE.md files and path-scoped rules don't come back until Claude touches something that triggers them.

Doesn't a 1M context window fix this?
It moves the cliff farther away. The docs say compaction works the same way with the larger window, and everything in that window still rides along on every turn at the cached rate. A bigger window is useful. Filling it with source files you no longer need isn't a great use of it.

Do I need an MCP server for any of this?
Nope. The notes file, the SessionStart hook, compact instructions and /autocompact are all built into Claude Code. A symbol index handles a different part of the problem: keeping source you don't need out of context before you ever have to compact.

Top comments (0)