Later, I created a plugin that can automatically include the following content and transparently enhance the compact feature.
https://x.com/u1/status/2073742408555343968
How Claude Code's Compact Actually Works
As a premise, Claude Code's compact runs through two paths:
- Manual compact (/compact): Triggered explicitly by the user. Can be fired at any timing.
- Automatic compact: Runs automatically when the session context usage reaches near the limit (roughly 90-95% in practice). It doesn't run at the exact moment the user intends, but rather at turn boundaries where you suddenly realize it has been compressed.
In both paths, the content is the same: the previous conversation history is sent to the LLM to create a natural language summary, and the summary + recent turns + system prompt are reconstructed as a new context. The summary becomes a narrative-based description of "what was done," and as a result, the decision structure—such as "why that choice was made," "which plans were rejected," and "what phase we are currently in"—is weakened. Furthermore, once compression runs, it is an irreversible operation; you cannot return to the raw logs from before compression.
From a hook perspective, compression can be caught by the PostCompact event. However, the PostCompact hook cannot return additionalContext, so there is no path to directly inject instructions into the agent immediately after compression. To insert instructions, one must go through the subsequent UserPromptSubmit event. This constraint dictates the hook design in this article.
What's Wrong with Standard Compact
Claude Code's /compact summarizes conversation history, placing the result as the initial context for a new session. While the summary itself is reasonable, it is a "work log of the past" rather than an "instruction for what to do next." Consequently, the post-compression agent becomes prone to the following misconceptions:
- Losing the boundary between "work instructions" and "work logs." If the summary says "Plan A was considered," the agent might start implementing Plan A instead of the actually adopted Plan B.
- Confusing the verification phase with the implementation phase. The premise of "verify on the actual machine before deploying" drops out of the summary, and the agent might jump into destructive deployment operations in the next turn.
- Re-proposing incorrect approaches that were already dismissed. The summary might record that an approach was "tried" but not the "reason why it was rejected."
- Losing "session state" such as plan mode, worker delegation, or task trees. The agent might continue work while out of plan mode or forget the existence of workers running in tmux and start doing the work itself.
What Kind of Accidents Actually Occurred
Looking back at my sessions over the last week, I observed at least four patterns immediately after a compact. All seem rooted in the failure to separate "work instructions" from "work logs":
- Deployment before verification: Despite agreeing to "verify behavior on the machine before placing files," the agent only saw the completion image in the summary after compression, skipped verification, and overwrote the destination.
- Re-executing rejected shortcuts: Because the summary recorded a failed shortcut approach as a "tried procedure," the agent re-executed it and stepped into the same failure again.
- Forgetting design principles: Principles established during the session, like "edit the source repository instead of the destination," dropped out of the summary, leading the agent to make direct edits that work short-term but are not reproducible.
- Misunderstanding task goals: The specific purpose of a test fixed before compression was lost, leading the agent to derail into peripheral investigations and start work on a completely different line.
These occurred multiple times within the same session, with different types of misconceptions happening after each compact. I see these as structural failures of the standard compact specification rather than isolated accidents.
What I Built
(1) compact-prep skill
A skill implemented as a slash command that the user explicitly hits before typing /compact. It extracts the "decision structure" and "session state" that likely won't fit in a summary and saves them to a state file at a fixed path (${TMPDIR}/claude-compact-state/<session_id>.md) in a specific format.
Items saved include those that are hard to capture in summaries:
- Active Plan (plan file path and current phase)
- TaskList Summary (in-progress tasks and supplements)
- Session Decisions (adopted plans / rejected plans / reasons for rejection)
- Constraints and Blockers
- Worker Topology (tmux-bridge panes / roles / assignments)
- Editing Files (notes on unsaved or unverified changes)
- Recovery Notes (a letter to the post-compression self)
The key design point is "mechanical enforcement." It uses a hard gate to prevent file creation if a session_id isn't found, a forcing function with fixed headings to detect omissions, and restricted allowed-tools to prevent state corruption.
(2) Two-stage hook: PostCompact + UserPromptSubmit
Even if (1) writes a state file, it's meaningless if the post-compression agent doesn't read it. Since PostCompact cannot return additionalContext, I used a two-stage configuration:
- The PostCompact hook writes a marker file with the session_id (no instructions, just recording that compression happened).
- When the next UserPromptSubmit hook detects the marker, it injects instructions via additionalContext: "Read the plan file / Read the state file / Check the TaskList / Treat the summary's next steps as hypotheses / Confirm re-entry if plan mode was deactivated," then deletes the marker.
Since Claude Code lacks a shared state mechanism between hooks, a marker on the filesystem is the only communication path. This keeps each hook thin with a single responsibility. In normal turns, the overhead is just one test -f check.
(3) 60% Notification (To avoid automatic compact)
The previous tools assume the user manually runs /compact-prep → /compact. However, automatic compact runs without warning, bypassing this flow. If automatic compact hits first, the state file isn't saved, and the raw logs are crushed into a summary, losing recovery material.
The solution is to create a state where the user can manually run the prep before automatic compact triggers. This is the purpose of the 60% notification.
Why 60%?
- Sufficient lead time: Automatic compact triggers around 90-95%. 60% provides a 30% margin, ensuring the user sees the warning before the next turn triggers an auto-compact.
- Room to reach a stopping point: At 60%, there is still 30% capacity left to finish a sub-task before saving the state.
- Mechanical trigger: It acts as a forcing function when you are too focused to notice context consumption.
- 1M Context Assumption: With a 200K context, 60% is too small. I use the 1M context (Opus 4.7), where 60% leaves about 600K tokens—plenty of room to finish work and run the prep.
Implementation involves a statusline hook writing a warn marker at the threshold, and a UserPromptSubmit hook detecting it to suggest running /compact-prep.
Results
Sessions are now stable even after about 10 compactions without logical breakdown!
- Work loss due to compression is nearly zero.
- Re-proposals of rejected ideas have disappeared.
- Plan mode is maintained.
- No more forgetting the topology when running multiple workers in tmux.
Reference: Implementation Code
Below are the actual skill and hook codes. They work by copying them to the standard Claude Code directory (~/.claude/).
A: compact-prep skill body
Place in ~/.claude/skills/compact-prep/SKILL.md.
1---2name: compact-prep3description: |4 Before executing /compact in Claude Code, save the current session state to a temporary state file.5 MANDATORY TRIGGERS: /compact-prep, compact-prep, compression preparation.6 DO NOT TRIGGER: recovery after compact, normal progress reports, plan creation.7strict_procedure: true8argument-hint: "[recovery notes]"9allowed-tools: Read Write Bash(~/.claude/scripts/get-session-id.sh *) Bash(mkdir *) Bash(date *) Bash(pwd)10---1112# compact-prep1314Saves session state that is hard to keep in compression summaries to15`${TMPDIR}/claude-compact-state/${SESSION_ID}.md` before `/compact`.1617## Strict procedure profile1819- Strictness: strict-procedure.20- Hard gates: Stop if session_id cannot be retrieved.21- Forcing function: Fixed path and mandatory heading check after saving.22- Completion receipt: Report file path, saved items, and guidance to run `/compact`.2324## Procedure25261. Get session_id via `~/.claude/scripts/get-session-id.sh`.272. Set path to `${TMPDIR:-/tmp}/claude-compact-state/${SESSION_ID}.md`.283. Check TaskList, active plan, tmux-bridge, and editing files.294. Save headings in order: # Compact Prep State, ## Active Plan, ## Current Phase, ## TaskList Summary, ## Session Decisions, ## Constraints and Blockers, ## Worker Topology, ## Editing Files, ## Recovery Notes.305. Verify all headings exist.316. Tell user: "Preparation complete. Please execute `/compact`."
B: Recovery hooks (2 stages)
B-1: PostCompact hook
~/.claude/hooks/compaction-recovery.sh
B-2: UserPromptSubmit hook (Injection)
~/.claude/hooks/userpromptsubmit-compaction-recovery.sh
C: 60% Notification
C-1: statusline hook threshold branch
Added to ~/.claude/hooks/statusline.sh.
C-2: UserPromptSubmit hook (Reminder)
~/.claude/hooks/userpromptsubmit-compact-prep-reminder.sh
D: settings.json registration
Add the hooks to ~/.claude/settings.json.





