If you use Claude Code for long sessions, you have lost work to compaction. Maybe not today. But it has happened, and when it does, you lose hours of context with no warning and no way to get it back.
Claude Code auto-compacts when the context window reaches roughly 95% of its 200K token limit. The agent summarizes everything into a compressed form, and the original conversation is gone. File paths, error messages, debugging chains, architectural decisions, multi-step reasoning. All of it gets compressed into a lossy summary that drops the details you actually need.
I have been running Claude Code daily for months across 9 projects. 1,300+ sessions, 69,000+ messages. Compaction has hit me dozens of times. After losing a 3-hour debugging session to auto-compaction mid-conversation, I built hooks that solve the problem permanently.
What Claude Code Compaction Actually Does
When your context window fills up, Claude Code runs automatic compaction. Here is what happens:
- The agent detects that the context is at roughly 95% capacity
- It summarizes the entire conversation into a shorter form
- The original messages are discarded
- The session continues with only the summary
The problem is that summaries are lossy. They capture the general topic but drop specifics:
- Error messages with stack traces get reduced to "there was an error"
- File paths and line numbers disappear
- Multi-step debugging chains lose the intermediate steps
- Architectural decisions lose their rationale
- Code snippets that were discussed get summarized away
After compaction, Claude does not remember the exact error you spent 20 minutes diagnosing. It does not remember which files you modified. It does not remember the three approaches you tried and rejected. It has a vague summary that says you "worked on debugging" and nothing more.
This is the single biggest productivity killer in Claude Code for long sessions.
Why Manual Workarounds Fail
The most common advice is to manually save your context before compaction hits. Run /compact yourself at a convenient time. Copy important notes into CLAUDE.md. Save key decisions to a file.
This does not work in practice for three reasons:
You forget. You are deep in a debugging session, focused on the problem. You are not watching the context percentage. Compaction happens automatically when you least expect it.
You cannot predict when it will fire. Auto-compaction triggers based on token count, which depends on message length, tool output size, and file contents. There is no reliable way to know when you are about to hit 95%.
Manual saving is incomplete. Even when you do remember to save, you decide what to keep and what to skip. You are making deletion decisions with the least information about what will matter later. The thing you skip is often the thing you need after compaction.
The Fix: PreCompact and PostCompact Hooks
Claude Code has a hook system that lets you run scripts at specific points in a session. Two of these hooks fire around compaction:
- PreCompact fires right before compaction starts
- PostCompact fires right after compaction finishes
These are the fix. Here is what they do in my system:
PreCompact: Capture Everything Before It Disappears
When the PreCompact hook fires, a Python script reads the current session transcript and writes every message to a local SQLite database. Not a summary. Not selected highlights. Every word of every message, including tool calls, file contents, and error output.
This happens automatically. No manual trigger. No decision about what to keep. The hook fires, the data is captured, and the session continues into compaction. Even if the summary is terrible, the full conversation is safe in the database.
# Simplified version of what the PreCompact hook does
def pre_compact(session_id, transcript_path):
# Read the full JSONL transcript
messages = read_jsonl(transcript_path)
# Write every message to SQLite
for msg in messages:
db.execute("""
INSERT OR IGNORE INTO transcripts
(session_id, role, content, timestamp)
VALUES (?, ?, ?, ?)
""", (session_id, msg['role'], msg['content'], msg['timestamp']))
db.commit()
PostCompact: Re-inject What Matters
After compaction finishes, the context window is nearly empty. Claude has a lossy summary and nothing else. The PostCompact hook fixes this by searching the database for relevant context and injecting it back.
It does not dump the entire pre-compaction conversation back in. That would just fill the context window again. Instead, it injects structured context:
- The current project summary (what this project is, what is in progress, what is blocked)
- The last session's notes (what was accomplished, what is next)
- Recent decisions that are still active
- The most relevant search results from the full conversation history
This gives Claude enough context to continue working without re-explaining everything, while leaving room in the context window for new work.
# Simplified version of what the PostCompact hook does
def post_compact(project, db_path):
context = []
# Get project summary
summary = db.execute(
"SELECT summary FROM project_registry WHERE prefix = ?",
(project,)
).fetchone()
if summary:
context.append(f"## Project Summary\n{summary[0]}")
# Get last session notes
notes = db.execute(
"SELECT notes FROM sys_sessions WHERE project = ? ORDER BY end_time DESC LIMIT 1",
(project,)
).fetchone()
if notes:
context.append(f"## Last Session\n{notes[0]}")
return {"additionalContext": "\n\n".join(context)}
What This Looks Like in Practice
Without hooks:
[3 hours of debugging WebSocket race condition]
[Auto-compaction fires at 95% context]
[Claude's summary: "We discussed WebSocket issues and debugging"]
You: "What was the root cause we found?"
Claude: "I don't have the specific details from our earlier
discussion. Could you remind me what we found?"
With hooks:
[3 hours of debugging WebSocket race condition]
[PreCompact fires: full conversation saved to SQLite]
[Auto-compaction fires at 95% context]
[PostCompact fires: project summary + recent context re-injected]
You: "What was the root cause we found?"
Claude: [searches database, finds the exact exchange]
"The root cause was a race condition in the connection
pool cleanup. When two clients disconnected within
50ms, the cleanup handler fired twice on the same
socket."
The difference is that nothing is lost. Compaction still happens. The context window still gets cleared. But the full conversation exists in the database, searchable on demand.
The Full Hook Lifecycle
PreCompact and PostCompact are two of six hooks that work together to give Claude Code persistent memory across sessions and compaction events:
session-start loads last session's notes, project context,
unfinished items
user-prompt-submit searches the database on every message, injects
relevant matches from past conversations
stop captures new messages to the database after
every Claude response
session-end triggers backup and sync
pre-compact saves the FULL conversation before compaction
post-compact re-injects relevant context after compaction
The pre-compact and post-compact hooks handle within-session continuity. The session-start and stop hooks handle between-session continuity. Together, they give Claude Code a persistent memory layer that survives both compaction and session restarts.
The Simple Version (No Database Required)
You do not need a full database setup to protect yourself from compaction. Here is a minimal approach using just CLAUDE.md:
Add this to your CLAUDE.md:
## Compaction Protocol
Before any compaction event, save the following to a file
called SESSION_STATE.md in the project root:
1. Current task and progress
2. Key decisions made and their rationale
3. Files modified and why
4. Errors encountered and their solutions
5. Next steps
After compaction, read SESSION_STATE.md and use it to
restore context.
This is not automated. Claude has to follow the instruction, and it will not always do so before auto-compaction. But it costs nothing to set up and catches some of the cases.
For reliable protection, you need hooks. Hooks fire automatically. They do not depend on Claude remembering to follow an instruction.
What the Hooks Do Not Fix
The hooks solve the data loss problem. They do not solve these:
Compaction still happens. The hooks do not prevent compaction. They capture the data before it fires and restore context after. The context window still gets cleared.
The summary is still lossy. Claude's internal summary after compaction is still a compressed version. The hooks compensate by injecting real data alongside the summary, but the summary itself is unchanged.
Token limits are real. The PostCompact hook cannot inject the entire pre-compaction conversation back. It injects the most relevant context and leaves room for new work. For full retrieval of specific details, Claude searches the database on demand.
Real Numbers
This system has been running across 1,300+ sessions:
- Zero messages lost to compaction since deploying the hooks
- Full conversation history searchable with keyword, semantic, and fuzzy search
- 69,000+ messages in the database across 9 projects
- Average post-compaction context restoration takes under 2 seconds
- Database size: roughly 1GB for 1,300 sessions (SQLite, fully local)
Try It
The CLAUDE.md approach takes 2 minutes and costs nothing. Start there.
For the full automated system with hooks, database, search, and MCP tools:
curl -fsSL https://raw.githubusercontent.com/mikeadolan/claude-brain/main/install.sh | bash
Free, open source, works on macOS, Linux, and Windows.
GitHub: github.com/mikeadolan/claude-brain
Video: claude-brain in 85 seconds
Article 1: How I Built Persistent Memory for Claude Code
Article 2: Why I Use Claude Code for Everything
Article 3: The Session Protocol That Fixed Claude Code's Amnesia Problem
Top comments (0)