- Published on
Surviving the Dumb Zone: A Checkpoint System for Claude Code's /compact
- Authors

- Name
- Duncan Leung
- @leungd
In my previous post on the dumb zone, I covered how LLM reasoning quality degrades with absolute token count — not percentage fill — and how a statusline can tell you which zone you're in. The natural follow-up question: what do you do when you hit the dumb zone?
The default answer is /compact. Claude Code compresses your conversation into a summary, freeing the context window. You drop back toward the smart zone and keep working.
The problem: /compact is lossy in systematic, predictable ways. And the things it loses are the things that hurt worst when they're gone.
What /compact Actually Does
When you run /compact, Claude Code replaces your full conversation history with a compressed summary. The summary captures the general shape of what happened — the task, the approach, recent progress. The original messages, tool outputs, and reasoning chains are discarded.
This works well for the parts of a conversation that are narrative: "we decided to use approach X, implemented it in file Y, and it passes tests." Summaries handle narrative fine.
It fails for the parts that are structural:
Exact numeric details. IDs, line numbers, error codes, commit SHAs, threshold values. A dialogue-summarization study found models retained numerical keywords only 7.8–28.3% of the time1.
Attempted-and-abandoned approaches. Summaries privilege the final story. The three approaches you tried and reverted — with the specific reasons each failed — get compressed to nothing. Post-compaction, the model retries the approach you already proved broken.
Governance and behavioral constraints. A 2026 paper found context compaction raises governance constraint violations from 0% to 30% pooled across models, up to 59% for some2. A documented Claude Code bug showed
/compactdropping CLAUDE.md safety rules, causing an unauthorizednpx cdk deploy.External state mutations. This is the dangerous one. If your session pushed a branch, created a PR, called an API, or started a deployment, that mutation happened outside the context window. Compaction can lose the record that it happened. Post-compaction, the model may repeat a non-idempotent operation because it no longer knows the first one succeeded.
The Failure Mode That Matters Most
The external state problem deserves its own section because it has the highest blast radius.
Consider a session that:
- Implements a feature across three files
- Runs tests (they pass)
- Commits and pushes to a branch
- Creates a PR with
gh pr create
At this point, the session has consumed enough context to cross into the dumb zone. You run /compact. The summary captures "implemented feature X, tests pass, PR created." But does it capture the PR number? The branch name? The exact commit SHA?
Sometimes it does. Sometimes it doesn't. And "sometimes" is not a property you want in a system that controls whether a second PR gets created on top of the first.
The same problem applies at every external boundary: S3 uploads, database migrations, CI triggers, Slack messages, Jira transitions. Any operation where "did this already happen?" is not answerable from the local filesystem alone.
The Solution: Write Ground Truth Before Compaction
The fix is to not trust the summary. Write the facts to a file before compaction, then read them back afterward.
I built this as two Claude Code skills — /precompact and /postcompact — that bracket every /compact invocation.
Before /compact: Write the checkpoint
/precompact writes a structured checkpoint to .ai/compact-checkpoint.md. The file captures everything that compaction reliably loses:
# Compact Checkpoint
SESSION_ID: e63063fa-18e0-4a81-abac-4bb39eb04eae
MODEL: claude-opus-4-6[1m]
ROLE: implementation
CWD: /Users/me/projects/fleet-cli
TIMESTAMP: 2026-09-22T14:30:00Z
COMPACTION_COUNT: 1
---
## Goal
Build the fleet watch command — a read-only TUI status board
showing agent states from the fleet state file.
## Active Tasks
| # | Task | Status | Notes |
|---|------|--------|-------|
| 1 | Implement watch command scaffold | DONE | cmd/fleet/watch.go |
| 2 | Add TUI rendering with bubbletea | IN_PROGRESS | Basic table working |
| 3 | Connect to state file watcher | PENDING | |
## External State
| System | Identifier | Applied? | Idempotent? | Verify command |
|--------|-----------|----------|-------------|----------------|
| git branch | fleet-watch-spike | yes | yes | git branch --list fleet-watch-spike |
| git push | origin/fleet-watch-spike | yes | yes | git ls-remote origin fleet-watch-spike |
## Key Decisions
- **Q:** Use bubbletea or lipgloss for TUI?
**D:** bubbletea — it handles input/update/view lifecycle.
**Why:** lipgloss is styling-only, no event loop.
## Attempted and Abandoned
- **Approach:** Polled state file with fsnotify
**Result:** Race condition on partial writes — 3/10 reads got truncated JSON
**Do not retry:** fsnotify on the state file directly
## State File Pointers
- `.ai/plan/fleet-watch.md` — implementation plan
## Resume Instruction
Run `go test ./internal/watch/...` and fix any failures,
then implement the state file watcher using atomic read
(read into buffer, unmarshal, retry on error).
Every section exists because a specific failure mode demands it:
- External State prevents replaying non-idempotent mutations. The verify command lets the post-compaction session check before acting.
- Key Decisions prevents re-litigating settled questions. A degraded model loves to reopen decisions because it can't remember the reasoning that closed them.
- Attempted and Abandoned prevents retrying broken approaches. This is the single most common post-compaction failure I've seen: the model spends 5 minutes rediscovering that an approach doesn't work.
- Resume Instruction is a concrete next action, not "continue working." The post-compaction session reads this line and executes it.
After /compact: Cross-check and reconcile
/postcompact reads the checkpoint and uses it as ground truth. It does three things:
1. Cross-check the compacted summary. Compare what the summary retained against what the checkpoint recorded. The checkpoint wins every conflict. Pay special attention to:
- DONE tasks that the summary dropped (without them, the model redoes finished work)
- External mutations the summary forgot (the most dangerous gap)
- Decisions and their rationale (without them, the model re-opens settled questions)
2. Read state file pointers. The checkpoint lists files that carry operational context (.ai/plan/, .ai/status/, etc.). Read the headers of each. These files are on disk — they didn't get compacted. They're fresh context at disk-read cost, not summary-of-a-summary cost.
3. Reconcile external state against live systems. This is the step that prevents the worst failures. Before executing any resume instruction, verify that the checkpoint's External State section matches reality:
# Git: was the branch actually pushed?
git fetch --quiet origin
git branch -r --list 'origin/fleet-watch-spike'
# CI: did the last run pass?
gh run list --branch fleet-watch-spike --limit 3 --json status,conclusion
# PRs: does it exist?
gh pr list --state all --head fleet-watch-spike --json number,state
Each row in the External State table gets one of three outcomes:
- CONFIRMED: live state matches the checkpoint. Proceed.
- CORRECTED: live state differs. Update understanding to match reality. Log the correction.
- UNVERIFIABLE: the verify command failed or doesn't exist. Treat as UNKNOWN. If the operation is non-idempotent, stop and ask the user rather than risking a replay.
This reconciliation step catches a failure mode that no amount of checkpoint fidelity can prevent: things that happened during compaction. An agent might have pushed a commit while you were compacting. A CI run might have completed. The checkpoint was written before compaction; the world kept moving.
Why Not Just Hand Off to a Fresh Session?
This is the obvious alternative: instead of compacting, write a brief and spawn a fresh agent. I have a handoff skill that does exactly this — it writes a structured brief with an external state ledger, standing decisions, and remaining work, then launches a successor session. The question is when each approach wins.
Compaction wins when:
- The task is continuous within a single phase (you're still implementing, still debugging, still reviewing)
- State is externally verifiable (git, tests, CI)
- You don't need historical evidence from earlier in the conversation
- The human is present and can run
/compactwithout disrupting their flow
Handoff wins when:
- You're crossing a semantic boundary (research to implementation, implementation to review)
- Non-idempotent external mutations have occurred that are hard to verify
- The model shows fidelity symptoms: repetition, forgotten constraints, circular reasoning
- The task type is changing entirely
The key insight from the research: the checkpoint system breaks the cumulative-drift chain. The standard argument against repeated compaction is that each round is a lossy summary of the previous lossy summary, and quality degrades monotonically. That argument assumes the summary is the only state carrier. When the checkpoint is written from full pre-compaction context and re-read from disk, each compaction cycle gets a fresh ground-truth injection. The 3rd compaction's checkpoint is no less accurate than the 1st's.
This means compaction with a checkpoint is safe to repeat within a coherent work phase. The handoff skill's original rule — "prefer handoff over compaction" — was written before the checkpoint system existed. With the checkpoint, the preference is weaker: compact within phases, hand off between phases.
The Research That Informed This
Three papers shaped the specific design choices:
Cross-Context Review (2025) tested whether a fresh session reviews code better than the same session. Fresh-session review achieved F1 of 28.6% versus 24.6% for same-session (p=0.008). Reviewing twice in the same session didn't beat reviewing once — the benefit comes from context separation, not repetition. This is why review sessions should always be separate from implementation sessions, regardless of compaction strategy.
Governance Decay (2026) measured how compaction affects constraint following. The finding — 0% to 30% constraint violation — is why the postcompact skill re-injects CLAUDE.md and behavioral rules as a distinct block rather than trusting the summary to preserve them.
Slipstream (2026) identified a fundamental problem with in-session validation of compaction quality: once a summary replaces original context, the agent's behavior is conditioned on the summary, so it can't independently detect its own compaction errors. The agent thinks everything is fine because it can't see what it lost. This is why the checkpoint must be written before compaction by the session that has full context, and the cross-check must compare against an external source of truth, not the agent's own recollection.
The Skills
These are Claude Code custom slash commands. Each lives in ~/.claude/skills/<name>/SKILL.md. When you type /<name>, Claude Code reads the skill file and follows its instructions.
/precompact — Write the Checkpoint
The precompact skill writes ground truth to disk before the context window gets compressed. It captures everything that compaction reliably loses: the session's goal, active tasks with their status, external state mutations with verify commands, settled decisions with rationale, and a concrete resume instruction.
The skill is session-qualified — the checkpoint file includes $CLAUDE_CODE_SESSION_ID in its path, so multiple concurrent sessions in the same repo don't clobber each other's checkpoints.
Full /precompact skill
---
name: precompact
description: Save session goal, tasks, external state, and decisions to the
session-qualified checkpoint .ai/compact-checkpoint-<session-id>.md before
the user types /compact. Outputs a post-compact directive telling the session
to run /postcompact after compaction.
---
# Precompact
Write a checkpoint file to `.ai/compact-checkpoint-<session-id>.md` before the
user types `/compact`. `<session-id>` is `$CLAUDE_CODE_SESSION_ID`; it stays the
same across `/compact`, so `/postcompact` resolves the same path.
## When to invoke
- Before every `/compact`, regardless of session role.
- When context is approaching the compact threshold (~120K for orchestrators,
~160K for others).
- When the user says "compact", "save state", or "checkpoint".
## Step 1 — Detect role and existing state
Check what kind of session this is and what state files already exist:
ls .ai/status/orchestrator-*.md 2>/dev/null
ls .ai/status/*.md 2>/dev/null
ls ".ai/compact-checkpoint-$CLAUDE_CODE_SESSION_ID.md" 2>/dev/null
## Step 2 — Gather session identity
Record these values at the top of the checkpoint:
SESSION_ID: <$CLAUDE_CODE_SESSION_ID>
MODEL: <exact model ID from system prompt>
ROLE: orchestrator | implementation | research | review | general
CWD: <working directory>
TIMESTAMP: <ISO 8601>
## Step 3 — Write the checkpoint
Write to `.ai/compact-checkpoint-$CLAUDE_CODE_SESSION_ID.md`.
### Checkpoint format
# Compact Checkpoint
SESSION_ID: <uuid>
MODEL: <model-id>
ROLE: <role>
CWD: <path>
TIMESTAMP: <ISO 8601>
COMPACTION_COUNT: <N>
---
## Goal
<1–3 sentences: the end-state this session is driving toward.
What does "done" look like? What did the user ask for?>
## Active Tasks
| # | Task | Status | Notes |
|---|------|--------|-------|
| 1 | <task> | IN_PROGRESS / DONE / BLOCKED | <key detail> |
## External State
Mutations this session made outside the local filesystem.
If none, write "None."
| System | Identifier | Applied? | Idempotent? | Verify command |
|--------|-----------|----------|-------------|----------------|
| <system> | <id> | yes / no / UNKNOWN | yes / no | <command> |
Every non-idempotent row needs a verify command.
UNKNOWN is a valid value — never guess yes or no.
## Key Decisions
Non-obvious choices already made. Each entry:
- **Q:** <question that was decided>
**D:** <decision>
**Why:** <rationale>
Do not re-litigate these after compaction.
## Attempted and Abandoned
Approaches tried and reverted this session:
- **Approach:** <what was tried>
**Result:** <what went wrong, with evidence>
**Do not retry:** <specific instruction>
## State File Pointers
Files the post-compact session must read to recover full context.
Point, do not inline.
## Resume Instruction
<The single next action to take after compaction.
"Poll agent impl-ai370 for PR status" — not "continue working.">
## Step 4 — Verify the checkpoint
After writing, re-read it and confirm:
1. Goal section is filled (not blank, not placeholder).
2. Active Tasks table has at least one row.
3. External State section is present (even if "None.").
4. Resume Instruction is a concrete action.
Read nothing else after this point. Compaction keeps a short tail of the
conversation and re-attaches the full contents of every file that tail read,
so each extra Read lands in the post-compact context in full.
## Step 5 — Output the post-compact directive
⚠️ POST-COMPACT DIRECTIVE: After compaction, run /postcompact before doing
anything else.
Type /compact now.
/postcompact — Recover and Reconcile
The postcompact skill reads the checkpoint as ground truth and cross-checks it against the compacted summary. It then reads each state file pointer, reconciles external state against live systems, and executes the resume instruction. The reconciliation step is the critical one — it catches things that happened during compaction.
Full /postcompact skill
---
name: postcompact
description: Recover session state after /compact by reading the
session-qualified checkpoint .ai/compact-checkpoint-<session-id>.md.
Cross-checks the compacted summary, reads state file pointers, reconciles
external state against live systems, and executes the resume instruction.
---
# Postcompact
Recover session state from `.ai/compact-checkpoint-<session-id>.md` after
`/compact` runs. The checkpoint was written by `/precompact` with full
pre-compaction context. It is ground truth — it wins every conflict with the
compacted summary.
## Step 1 — Read the checkpoint
cat ".ai/compact-checkpoint-$CLAUDE_CODE_SESSION_ID.md"
Confirm that the file's SESSION_ID line matches $CLAUDE_CODE_SESSION_ID.
Never read another session's checkpoint.
## Step 2 — Cross-check against the compacted summary
Compare what the compacted summary retained against the checkpoint:
- **Goal:** Does the summary mention the goal? If not, adopt from checkpoint.
- **Active Tasks:** Same tasks with same statuses? Trust the checkpoint.
Pay special attention to DONE tasks — the summary may drop them.
- **External State:** Does the summary know about external mutations?
This is the most dangerous gap.
- **Key Decisions:** Does the summary preserve the decisions and rationale?
- **Attempted and Abandoned:** Does the summary know what failed?
The checkpoint wins every conflict.
## Step 3 — Read state file pointers
Read each file listed in the checkpoint's State File Pointers section:
head -30 .ai/status/orchestrator-<ticket>.md
head -30 .ai/status/<agent>.md
Use head -30 — not full reads. Check for mismatches: an agent may have
completed during compaction.
## Step 4 — Reconcile external state against live systems
Before executing any resume instruction, verify each External State row:
# Git: was the branch actually pushed?
git fetch --quiet origin
git branch -r --list 'origin/<branch>'
# CI: did the last run pass?
gh run list --branch <branch> --limit 3 --json status,conclusion
# PRs: does it exist?
gh pr list --state all --head <branch> --json number,state
Each row gets one of three outcomes:
- CONFIRMED: live state matches the checkpoint. Proceed.
- CORRECTED: live state differs. Update understanding. Log the correction.
- UNVERIFIABLE: verify command failed. Treat as UNKNOWN. If non-idempotent,
stop and ask the user.
## Step 5 — Execute the resume instruction
Read the Resume Instruction from the checkpoint and execute it.
## Step 6 — Report recovery status
Post-compact recovery complete.
- Goal: <one-line summary>
- Tasks: <N> active, <N> done
- External state: <N> mutations (or "none")
- Reconciliation: <N> confirmed, <N> corrected, <N> unverifiable
- Compaction count: <N>
- Resuming: <the resume instruction>
/compact-flow — Remote Compaction
When an orchestrator needs to compact another session (a long-running implementation agent approaching context exhaustion), it can't type /compact in the other terminal. The /compact-flow skill sends the full /precompact → /compact → /postcompact sequence to a remote herdr pane via terminal commands, waiting for each step to complete before sending the next.
Full /compact-flow skill
---
name: compact-flow
description: Run the full /precompact → /compact → /postcompact sequence on a
remote herdr pane. Accepts a herdr pane ID (e.g. w1N:p1P). Use when you need
to compact another session but cannot invoke /compact from a skill directly.
---
# Compact Flow
Run /precompact → /compact → /postcompact on a remote herdr pane by sending
commands from this session.
## Argument
The argument is a herdr pane ID — the workspace:pane format (e.g. w1N:p1P).
## Step 1 — Send /precompact
herdr pane send-text <PANE_ID> "/precompact"
herdr pane send-keys <PANE_ID> Enter
Wait for completion:
herdr pane wait-output <PANE_ID> \
--regex "Checkpoint verified|Type /compact now" \
--source visible --timeout 180000
## Step 2 — Send /compact
Send the full /compact command exactly as precompact output it:
herdr pane send-text <PANE_ID> "/compact Preserve the goal, open tasks,
standing constraints, decisions and rationale, failed approaches not to retry,
completion markers, unresolved external state, and the instruction to run
/postcompact first. Omit detailed closed-work narratives and copied file
contents."
herdr pane send-keys <PANE_ID> Enter
Wait for compaction:
herdr pane wait-output <PANE_ID> \
--regex "Compacted|Skills restored" \
--source visible --timeout 300000
## Step 3 — Send /postcompact
herdr pane send-text <PANE_ID> "/postcompact"
herdr pane send-keys <PANE_ID> Enter
herdr pane wait-output <PANE_ID> \
--regex "Recap|Post-compact|recovered|Resume" \
--source visible --timeout 300000
## Step 4 — Verify and report
herdr pane read <PANE_ID> --source visible --lines 30
Confirm the context indicator changed, the session produced a Recap,
and the prompt is idle.
## Error handling
If any step fails or times out, stop the flow. Do not send the next command.
If /precompact fails, /compact must not run — it would lose state without
a checkpoint.
Practical Usage
The workflow in practice:
1. Work normally in the smart zone
2. Statusline shows WARN or DUMB → finish current subtask
3. Run /precompact → writes checkpoint to .ai/compact-checkpoint.md
4. Run /compact → Claude Code compresses the conversation
5. /postcompact runs automatically:
- Reads checkpoint (ground truth)
- Cross-checks against compacted summary
- Reads state file pointers
- Reconciles external state against live systems
- Executes the resume instruction
6. Back in the smart zone with verified state
The checkpoint file is typically 50–100 lines. It points to other files rather than inlining their contents — the post-compaction session reads those files directly, getting fresh data at disk-read cost.
The total overhead is about 30 seconds per compaction cycle. The alternative — a post-compaction session that retries a broken approach, re-creates an existing PR, or re-pushes a branch — costs minutes to hours.
What This Doesn't Fix
The checkpoint system addresses state preservation across compaction. It does not fix reasoning degradation within a session. If the model is in the dumb zone and producing poor reasoning, compaction resets the context length but the summary quality depends on the degraded model that wrote it. The checkpoint provides external ground truth that the model can verify against, but it can't make a degraded model write a better summary.
For reasoning degradation specifically — the model showing circular logic, ignoring instructions, or producing incoherent plans — a fresh session via handoff is the right call. No amount of checkpointing saves a session that has lost the ability to reason about its own output.
The checkpoint system is a safety net, not a performance enhancer. It makes compaction safe where it was previously risky. The question of when to compact versus hand off remains a judgment call — but now it's a judgment call with a safety net underneath it.
Evolution: From Custom Skills to Native Compaction
Updated September 2026.
These three skills served as the compaction safety net for several months. In September 2026, they were retired in favor of OpenRig's native compaction system, which absorbed every concern and added capabilities the custom skills could not reach.
What changed
OpenRig ships two compaction skills and a watchdog policy that together replace /precompact, /postcompact, and /compact-flow:
claude-compaction-restore — A PreCompact hook writes a restore packet before compaction. The post-compact session reads the packet or falls back to JSONL transcript-based restore. It includes a restore map with an ASCII file/folder tree and a read-depth audit (FULL / PARTIAL / NOT_READ) that proves the restored session read back everything it claimed to. Seven guardrails and five failure modes cover the edge cases the custom skills discovered the hard way.
session-compaction-and-restore — A conceptual skill covering five session-continuation distinctions (resume, fork, rebuild, artifact-backed, fresh) and three mechanisms (Claude /compact, OpenRig-managed restore, Codex resume). It defines a 16-field v0 packet standard that captures everything the custom checkpoint format captured, plus fields the custom format lacked: occupant lineage, trust-bridge injection state, and context-usage telemetry.
context-usage-threshold watchdog policy — Instead of relying on the operator to notice the statusline and invoke /precompact manually, the daemon monitors context usage and fires a watchdog event when it crosses a threshold. The compaction flow starts automatically.
Why retire the custom skills
The overlap was total. A line-by-line comparison showed:
| Concern | Custom skill | OpenRig native |
|---|---|---|
| Checkpoint before compact | /precompact writes .ai/compact-checkpoint-<id>.md | PreCompact hook writes restore packet |
| Cross-check after compact | /postcompact reads checkpoint, reconciles external state | Restore skill reads packet, runs read-depth audit |
| Remote compaction | /compact-flow sends terminal commands via herdr | rig compact <session> (guided prep → compact → restore → audit) |
| Proactive trigger | None — operator must notice statusline | context-usage-threshold watchdog fires automatically |
| Packet format | Ad-hoc markdown with ~10 fields | 16-field v0 standard with schema |
| Verification | External state reconciliation (CONFIRMED / CORRECTED / UNVERIFIABLE) | Read-depth audit (FULL / PARTIAL / NOT_READ) plus trust-bridge restore |
The custom skills were the prototype. OpenRig's native system is the production version — it covers the same failure modes with a formal packet standard, automated triggering, and deeper verification.
What carried forward
The core insight survived intact: compaction is lossy, and the post-compaction session needs external ground truth to recover from. The custom skills proved this worked. OpenRig formalized it.
The specific contributions from these skills that shaped OpenRig's design:
- Session-qualified checkpoints — The
$CLAUDE_CODE_SESSION_IDpath qualifier that prevents concurrent sessions from clobbering each other's state became a first-class concept in the packet standard. - External state reconciliation — The CONFIRMED / CORRECTED / UNVERIFIABLE outcome model for verifying mutations against live systems influenced the read-depth audit design.
- "The checkpoint wins every conflict" — This rule became a guardrail in
claude-compaction-restore: the restore packet is ground truth, and the compacted summary is a fallback. - The "read nothing else" constraint —
/precompactdeliberately avoided reading extra files after writing the checkpoint, because those reads would land in the post-compact context. This observation shaped the restore skill's approach to managing what enters the restored context.
The skills themselves are no longer maintained. If you are running a multi-agent topology with OpenRig, use rig compact and the native watchdog. If you are running standalone Claude Code sessions without OpenRig, the skill code above still works — copy the SKILL.md files into ~/.claude/skills/ and use them as described.
Footnotes
CogCanvas: Verbatim-Grounded Artifact Extraction for Long LLM Conversations (2025). The study measured keyword retention across summarization tasks, with numeric/symbolic tokens consistently scoring lowest. ↩
Governance Decay: How Context Compaction Silently Erases Safety Constraints in Long-Horizon LLM Agents (2026). ↩