[FEATURE] PostCompact Hook Event and Compaction Content Control

Open 💬 19 comments Opened Dec 17, 2025 by brianmjohnson

Feature Request: PostCompact Hook Event and Compaction Content Control

Summary

Users who define behavioral frameworks in CLAUDE.md cannot reliably maintain framework compliance across compaction boundaries. The compaction process paraphrases framework references in chat history, causing behavioral drift.

Problem Description

Current Behavior

  1. User defines behavioral framework in CLAUDE.md and linked documents
  2. During session, Claude references framework rules in responses
  3. When compaction occurs, these references get paraphrased in the summary
  4. Post-compaction, Claude sees "framework already discussed" in the summary
  5. Claude doesn't re-read source files due to training bias ("don't re-read if in context")
  6. Paraphrased rules lose precision → behavioral drift

Expected Behavior

Framework rules defined in CLAUDE.md should be:

  • Retained verbatim through compaction (not paraphrased)
  • Complied with consistently across session boundaries
  • Re-read from source files after compaction

Why This Matters

Users building sophisticated agent frameworks (custom workflows, behavioral rules, domain-specific guidelines) cannot trust that Claude will maintain consistent behavior during long sessions. This fundamentally limits what can be built with Claude Code.

Technical Analysis

What Currently Works

  • CLAUDE.md files ARE loaded fresh from disk after compaction - they survive verbatim
  • SessionStart hooks can inject reminders at session start
  • PreCompact hooks can inject content before compaction

What's Missing

| Capability | Current State |
|------------|---------------|
| Force Claude to read specific files post-compaction | Not possible |
| Control what gets summarized during compaction | Not possible |
| Inject content AFTER compaction completes | No PostCompact hook |
| Mark content as "do not summarize" | No API/schema |
| Verify Claude actually read and internalized content | Not possible |

Root Cause

The core issue is that:

  1. PreCompact hooks inject content into the context being summarized
  2. That content itself gets paraphrased
  3. There's no way to inject content after the summary is created
  4. No mechanism exists to mark certain content as "retain verbatim"

Proposed Solutions

Option A: PostCompact Hook Event (Preferred)

Add a PostCompact hook event that fires after compaction completes but before Claude responds.

{
  "hooks": {
    "PostCompact": [
      {
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/framework-reload/hook.sh"
          }
        ]
      }
    ]
  }
}

This would allow:

  • Injecting fresh instructions that appear after the summary
  • These instructions wouldn't be subject to summarization
  • Could include explicit "re-read these files" directives

Option B: Compaction Content Control Schema

Allow hooks or CLAUDE.md to mark content with handling directives:

# In CLAUDE.md or via hook output
compaction_rules:
  - pattern: "framework rule:"
    action: do_not_summarize
  - pattern: "CRITICAL:"
    action: retain_verbatim
  - file: ".claude/agents/framework/*.md"
    action: summarize_as_pointer  # Replace with "re-read file X"

Option C: Framework Pinning

Allow CLAUDE.md to specify "pinned context" that:

  • Survives compaction verbatim (not summarized)
  • Gets re-injected after summary
  • Has priority over summarized content
{
  "pinned_context": {
    "files": [
      ".claude/agents/business-operations/framework/THE_CONSTITUTION.md"
    ],
    "max_tokens": 10000,
    "priority": "high"
  }
}

Option D: Behavioral Checkpoint Hook

Add a hook event for "first response after compaction" that can:

  • Block response until prerequisites are met
  • Verify specific files were read (via tool call inspection)
  • Inject mandatory context

Workarounds We've Tried

We've implemented compensating controls, but all rely on behavioral compliance:

  1. PreCompact Hook: Outputs "re-read these files after compaction"
  • Problem: This instruction itself gets summarized/paraphrased
  1. Strengthened CLAUDE.md Rules: "Treat summary content as INVALID"
  • Problem: Claude may not follow rules it doesn't re-read
  1. Verification Protocol: Require Claude to state what files were read
  • Problem: No enforcement mechanism
  1. Compaction Survival Rules Table: Critical behaviors to maintain
  • Problem: Table itself can be paraphrased in summary

Conclusion: All workarounds fail because they rely on Claude to voluntarily comply with rules it may not have actually read post-compaction.

Impact

Current State

| Metric | Value |
|--------|-------|
| Framework compliance after compaction | ~60-70% |
| Predictability in long sessions | Low |
| Ability to build reliable agent frameworks | Limited |

With Fix

| Metric | Expected |
|--------|----------|
| Framework compliance after compaction | ~95%+ |
| Predictability in long sessions | High |
| Ability to build reliable agent frameworks | Enabled |

Environment

  • Claude Code CLI
  • Using extensive CLAUDE.md framework (~30KB across multiple files)
  • Long sessions that trigger multiple compactions
  • Custom hooks for SessionStart, PreToolUse, PostToolUse, PreCompact

Related

This may be related to general concerns about:

  • Context management in long sessions
  • Reliability of custom instructions
  • Agent framework development patterns

Appendix: Detailed Hook Analysis

SessionStart Hook

  • Fires: On new session start
  • Does NOT fire: After compaction (compaction is NOT a new session)
  • Can do: Inject reminders
  • Cannot do: Force file reads

PreCompact Hook

  • Fires: Before compaction
  • Output: Goes INTO the context being summarized
  • Problem: Output itself gets paraphrased

Missing: PostCompact Hook

  • Would fire: After compaction, before Claude responds
  • Output: Would appear AFTER summary (not subject to summarization)
  • Would enable: Fresh instructions that aren't paraphrased

View original on GitHub ↗

19 Comments

am-will · 6 months ago

thanks for the idea. I actually built this in Codex so its totally possible. works great

<img width="328" height="101" alt="Image" src="https://github.com/user-attachments/assets/e8be30ec-a9c7-4fa5-a730-48fcdb5ddb2e" />

brianmjohnson · 6 months ago

LOL, Cool! Did you get that pushed upstream or is it on a fork?

Since this ticket and my others have been sitting for a few weeks with no real traction, I also ended up forking codex-cli. I integrated it with Anthropic to work around this and some other issues.

thanks for the idea. I actually built this in Codex so its totally possible. works great <img alt="Image" width="328" height="101" src="https://private-user-images.githubusercontent.com/42459108/532623413-e8be30ec-a9c7-4fa5-a730-48fcdb5ddb2e.png?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Njc5MjM0ODIsIm5iZiI6MTc2NzkyMzE4MiwicGF0aCI6Ii80MjQ1OTEwOC81MzI2MjM0MTMtZThiZTMwZWMtYTljNy00ZmE1LWE3MzAtNDhmY2RiNWRkYjJlLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNjAxMDklMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwMTA5VDAxNDYyMlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPTUwMjJmY2JjYTdjOTRhYjBlZTI1Y2E0Yjk2YWU4NGM2YTYyZjNjMzdlNDhkM2E0MzJkODc4YzkxNjdhNmViMzEmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.tG2Vszw8KL5kSzk8xv6kgRcV0IzbAz2pP6pjKruFIAY">
tekn0x · 5 months ago

We absolutely need a PostCompact hook. There are so many good uses for it.

poppabear8883 · 5 months ago

+1

TechNickAI · 5 months ago

Workaround: Todo Persistence via Hooks

While waiting for a PostCompact hook, we built a solution for one of the most frustrating compaction problems: losing your todo list mid-task.

The Pain

You're deep in a complex refactor, carefully tracking 8 todos across multiple files. Compaction hits. Suddenly Claude has amnesia and your carefully organized task list is gone. Sound familiar?

The Solution

ai-coding-config now includes hooks that automatically persist todos to disk. After compaction:

cat ~/.claude/projects/$(echo $PWD | sed 's|/|-|g')/todos.md

Your todos are saved as human-readable markdown, grouped by status. Restore them via TodoWrite and pick up exactly where you left off.

How It Works

  • PostToolUse hook on TodoWrite captures every todo update
  • Saves to Claude's existing ~/.claude/projects/{project}/ directory
  • Uses transcript_path from hook input - no SessionStart needed
  • Zero config, zero files in your repo

Get It

/plugin marketplace add https://github.com/TechNickAI/ai-coding-config
/plugin install ai-coding-config

ai-coding-config also includes 18 workflow commands (/autotask, /address-pr-comments, /session), 24 specialized review agents, and coding rules that work across Claude Code, Cursor, and Windsurf. The todo persistence is just one of many quality-of-life improvements.

---

This doesn't solve the broader framework compliance problem (CLAUDE.md rules being paraphrased during compaction) - a proper PostCompact hook would enable that. But it does solve the immediate "where was I?" problem that hits everyone in long sessions.

+1 for the feature request\!

maxthraxx · 5 months ago

Adding my +1 here, a PostCompact hook event is desperately needed as more people are discovering the power of Ralph Wiggum loops which necessitate compaction. Ralph Wiggum loop + PostCompact hooking to feed the Ralph prompt back to Claude ensures Claude stays on task.

ThatDragonOverThere · 5 months ago

+1 for this feature.

I have an extensive CLAUDE.md (600+ lines) with trading system rules, non-negotiable patterns, and critical context. After compaction, instructions that were followed perfectly pre-compact get violated.

A post-compact hook would let me re-inject critical context that must persist across compaction boundaries. This is essential for long-running sessions with complex domain-specific rules.

am-will · 5 months ago
+1 for this feature. I have an extensive CLAUDE.md (600+ lines) with trading system rules, non-negotiable patterns, and critical context. After compaction, instructions that were followed perfectly pre-compact get violated. A post-compact hook would let me re-inject critical context that must persist across compaction boundaries. This is essential for long-running sessions with complex domain-specific rules.

Just a tip, you really dont want to have an obnoxiously long AGENTS file. Just asking for drift. Would strongly recommend trimming the fat there. There's no way you need all of that.

bakeb7j0 · 5 months ago

pretty please! I am so tired of typing read @CLAUDE.md and confirm rules of engagement.

mark-hubers · 5 months ago

+1

CLAUDE.md rules get paraphrased during compaction and lose precision.

Using SessionStart:compact hook to re-inject rules (~70% reliable). PostCompact with forced file re-read would actually fix this.

(Comment drafted with Claude Code - dogfooding the tool I'm requesting features for)

bhpascal · 5 months ago

Workaround: SessionStart's compact matcher as a de facto PostCompact hook

While we wait for a proper PostCompact event, there's a functional workaround hiding in the existing hooks system that I haven't seen discussed here.

SessionStart supports four matchers: startup, resume, clear, and compact. That last one fires after compaction completes and the session restarts, but before Claude responds. And critically, SessionStart hooks can inject additionalContext into the conversation via stdout — which means you can force-feed Claude whatever context you need post-compaction.

Basic setup

{
  "hooks": {
    "PreCompact": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/pre-compact.sh"
          }
        ]
      }
    ],
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/post-compact.sh"
          }
        ]
      }
    ]
  }
}

Example: Re-inject CLAUDE.md with emphasis

The core problem described in this issue is that CLAUDE.md is loaded post-compaction, but the model treats it as redundant because the compacted summary already paraphrases its contents. You can fight this by re-injecting it as fresh additionalContext with framing that overrides the summarization-induced complacency:

#!/bin/bash
# .claude/hooks/post-compact.sh
CLAUDE_MD="$CLAUDE_PROJECT_DIR/CLAUDE.md"

if [ -f "$CLAUDE_MD" ]; then
  CONTENT=$(cat "$CLAUDE_MD")
  jq -n --arg ctx "IMPORTANT: Context was just compacted. The following rules are authoritative and take precedence over any paraphrased version in the compacted summary:\n\n$CONTENT" '{
    hookSpecificOutput: {
      hookEventName: "SessionStart",
      additionalContext: $ctx
    }
  }'
fi
exit 0

Example: Save and restore custom state across compaction

#!/bin/bash
# .claude/hooks/pre-compact.sh
# Save critical state before compaction
STATE_FILE="$CLAUDE_PROJECT_DIR/.claude/compact-state.md"
echo "# Preserved State ($(date))" > "$STATE_FILE"
echo "## Active Tasks" >> "$STATE_FILE"
# Add whatever project-specific state you need to preserve
cat "$CLAUDE_PROJECT_DIR/TODO.md" >> "$STATE_FILE" 2>/dev/null
exit 0
#!/bin/bash
# .claude/hooks/post-compact.sh
STATE_FILE="$CLAUDE_PROJECT_DIR/.claude/compact-state.md"
CLAUDE_MD="$CLAUDE_PROJECT_DIR/CLAUDE.md"

CONTEXT=""
[ -f "$CLAUDE_MD" ] && CONTEXT="$(cat "$CLAUDE_MD")\n\n"
[ -f "$STATE_FILE" ] && CONTEXT="${CONTEXT}$(cat "$STATE_FILE")"

if [ -n "$CONTEXT" ]; then
  jq -n --arg ctx "$CONTEXT" '{
    hookSpecificOutput: {
      hookEventName: "SessionStart",
      additionalContext: $ctx
    }
  }'
fi
exit 0

What this does and doesn't solve

Does solve:

  • Re-injecting framework rules / CLAUDE.md with fresh authority post-compaction
  • Preserving and restoring arbitrary state across compaction boundaries
  • Running any custom logic after compaction completes

Doesn't solve:

  • No access to the compacted summary itself (can't validate or modify what the compaction produced)
  • Undocumented interaction pattern — could break in a future release
  • Doesn't address the deeper issue of compaction paraphrasing precision rules into lossy summaries

A proper PostCompact hook that exposes the compacted summary would still be the right solution. But this gets you 80% of the way there today.

ThatDragonOverThere · 4 months ago

This Feature Request Is Now Urgent — Feb 17, 2026

The existing SessionStart(compact) hook is not sufficient. We've built a full workaround using it (detailed in #9796) and confirmed that Claude ignores the injected output after compaction.

The problem: hook output is injected as user-level context, but the compaction summary creates a narrative momentum that overrides it. Claude follows the compressed "what I was doing" summary and ignores the "here are your rules" injection.

What's needed: A PostCompact hook (or enhancement to SessionStart) where the output is injected as system-level instructions, not user-level context. This is the only way to ensure rules survive compaction.

The API already has pause_after_compaction — please expose this in the CLI, or give us a hook whose output is treated as authoritative directives rather than informational context.

This is the #1 reliability issue for power users. See #9796 for full evidence.

russellmoss · 4 months ago

I ran into the same CLAUDE.md drift problem and ended up building a system around it. The core insight was that CLAUDE.md shouldn't be a static file you write once — it should be regenerated from your actual code on every commit. I built a pre-commit hook that scans API routes, Prisma models, and env vars, then rewrites the standing instructions so the AI always has grounded context. It's open source if anyone wants to try it: https://github.com/russellmoss/agent-guard. Happy to answer questions about the approach.

ThatDragonOverThere · 4 months ago

Urgency Update: Crash + Compaction Death Spiral

The need for a proper post-compact hook (or CLAUDE.md re-read as system prompt) is now critical. The Bun crash crisis (#21576 — 26+ crashes in 30 days) forces constant session restarts. Each restart hits compaction sooner. Each compaction loses all instructions.

The existing SessionStart(compact) hook fires but its output is treated as user context, not system directives. Claude follows the compaction summary narrative instead. This was proven on Feb 17 with full evidence.

4 computer lockups in 5 days + instruction loss on every compaction = the product is unreliable on Windows.

yurukusa · 3 months ago

@bhpascal mentioned SessionStart's compact matcher — that's the closest workaround today. Here's a concrete implementation that makes it more reliable:
Create ~/.claude/hooks/post-compact-reinject.sh:

cat << 'RULES'
⚠️ COMPACTION JUST OCCURRED — Re-read these mandatory rules:
1. ALWAYS use the Read tool to read files. Never use cat/head/tail in Bash.
2. NEVER amend pushed commits. Create new commits instead.
3. NEVER run rm -rf, git reset --hard, or git clean -fd.
4. Read CLAUDE.md now to refresh your full instructions.
These rules override any paraphrased versions in the compacted context.
RULES
{
  "hooks": {
    "SessionStart": [{
      "matcher": "compact",
      "hooks": [{
        "type": "command",
        "command": "bash ~/.claude/hooks/post-compact-reinject.sh"
      }]
    }]
  }
}

The hook output lands as a tool result in the conversation, not as a system message. Claude treats it as "information I received" rather than "instructions I must follow." Two techniques improve this:
1. Pair it with a PreToolUse enforcement hook
The SessionStart hook reminds Claude of the rules. A PreToolUse hook enforces them:

if echo "$CC_TOOL_INPUT" | grep -qE '\b(cat|head|tail)\b'; then
  echo 'BLOCKED: Use Read tool. (Rule refreshed after compaction)' >&2
  exit 2
fi

Now even if Claude "forgets" the SessionStart reminder, the PreToolUse hook physically blocks the violation.
2. Keep the reinject message short and imperative
Long paragraphs get diluted in the context. Short, numbered rules with "ALWAYS/NEVER" framing survive attention better post-compaction.
@ThatDragonOverThere is right — a proper PostCompact hook event with system-level message injection would fix this properly. Until then, the SessionStart(compact) + PreToolUse enforcement combo covers ~90% of cases in my experience.

chadptk1238 · 3 months ago

+1 — we built a working workaround for this that re-injects the last 40 raw messages after compaction. Sharing in case it helps others or informs the implementation.

The workaround

Since PostCompact stdout isn't visible to the model, we use UserPromptSubmit with a marker file state machine to detect compaction:

  1. On every user prompt, scan for agent-acompact-*.jsonl files in the session's subagents directory
  2. Compare file count against a persistent marker file
  3. If count increased → compaction just happened → query a SQLite memory DB for the last 40 raw messages → inject via stdout
  4. Update marker → won't fire again until next compaction

This gives us full raw message recovery post-compaction. The summary alone loses variable names, exact commands, and specific decisions — the raw messages are the highest-value context.

Why PostCompact context injection would replace all of this

Our entire state machine (marker files, filesystem scanning, count comparison) exists solely because PostCompact can't inject model-visible context. If PostCompact supported additionalContext or model-visible stdout, the workaround collapses to ~10 lines querying the DB and printing the result.

The gap is clear: UserPromptSubmit, SessionStart, and PreCompact all support context injection. PostCompact is the only lifecycle hook at a context-loss boundary that can't.

JulienLizano · 2 months ago

I've been trying to handle some logic around compaction and trying to use hook. Like in many issues mentionned, I wanted to :

  1. Save Session Context before compact -> Solved via PostCompact hook that parse the session JSONL and extract content in between the "compact_boundary" lines.
  1. Save the Summary -> Solved via PostCompact hook that store the exposed "compact_summary"
  1. Modify the Compact Instruction -> That I couldn't find anything documented, no exposed field in hook (despite being handled by the manual compact). But THERE IS a solution that is undocumented and work with native claude code and after verification, it doesn't overide the compact instruction, so you can tell the compact agent what to remenber or even add permanent information each time.

-> PreCompact hook's plain-text stdout is captured and passed to the summarization model as custom instructions — the same channel populated by the user's /compact "<text>" argument. The result: PreCompact stdout shapes the summary

Wire contract (validated end-to-end):

  • Write plain text to stdout. Not JSON, not hookSpecificOutput, not additionalContext — just a bare string.
  • Leading/trailing whitespace is trimmed.
  • If multiple PreCompact hooks emit stdout, their strings are concatenated with \n\n between them.
  • The combined string is merged with the user's /compact "..." argument (if any) and delivered to the summarizer as the

custom-instructions block.

  • Exit 0 required; non-zero stderr surfaces as a hook failure.

Minimal working example
.claude/hooks/precompact-shape-summary.sh:
```bash
#!/usr/bin/env bash
cat <<'EOF'
When producing the compact summary:

  • Preserve every file path mentioned verbatim.

EOF
exit 0
```

.claude/settings.json:
  {
    "hooks": {
      "PreCompact": [
        { "hooks": [{ "type": "command", "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/precompact-shape-summary.sh" }] }
      ]
    }
  }
SaravananJaichandar · 1 month ago

+1, with a working implementation that approximates this externally.

world-model-mcp ships PostCompact-style auto-injection as of v0.7.0 (May 17). A Node hook subscribed to PostCompact + UserPromptSubmit calls a Python helper that pulls the top constraints (ordered by violation_count) and recent canonical facts from a local SQLite knowledge graph, returns a compact bundle, and the hook emits it as additionalContext. Bundle size is configurable (max_constraints, max_facts); the helper fails open so a broken hook never blocks the agent.

v0.7.2 (shipped today) adds streamable HTTP transport so the same 25 MCP tools also work behind an MCP tunnel - the configuration Anthropic announced today for Claude Managed Agents with self-hosted sandboxes, where the built-in Memory primitive isn't supported yet.

Concrete primitives this exercises against the current hook surface:

  • Constraints with violation counts: rules learned from corrections, ranked by how often they've been violated. Hard violations (severity=error, count>=3) deny at PreToolUse; warning-level violations seen 5+ times return a new defer tier that pauses headless agents.
  • Temporal facts with confidence + source_count: find_contradictions now surfaces confidence and source-count per fact in each contradicting pair; resolve_contradiction picks a winner via keep_higher_confidence / keep_most_recent / keep_most_sources / auto.
  • Compaction audit log: every PostCompact event records pre/post token counts plus what was re-injected. Query with world-model audit-compactions [--export <jsonl>].

Repo: github.com/SaravananJaichandar/world-model-mcp - pip install world-model-mcp

Two real gaps in the current hook surface that would make external memory layers significantly cleaner:

  1. A first-class PreCompact event with mutable output (the way UserPromptSubmit allows additionalContext). Today's PostCompact lifecycle is best-effort observation.
  2. A documented contract for the defer decision in PreToolUse - clients that don't recognize it currently fall back to ask, which is fine but conflates "headless pause" with "interactive confirm."

The 45 reactions on this issue suggest the appetite is real. Happy to share more on the hook payloads / failure modes if useful.

am-will · 1 month ago

there's a fix. just use codex ;)