Skip to content

[BUG] Claude forgets everything in CLAUDE.md after compaction #6354

Open

Description

@flux627

Environment

  • Platform (select one):
    • Anthropic API
    • AWS Bedrock
    • Google Vertex AI
    • Other:
  • Claude CLI version: 1.0.88 (Claude Code)
  • Operating System: macOS 14.5 (23F79)
  • Terminal: iTerm2

Bug Description

My complex project has a lot of things that need to be known for agentic workers to navigate effectively. All these details are listed in my project's CLAUDE.md located at the root of my project. I noticed that I have to tell it to re-read this every time it compacts, otherwise it starts doing things in "common sense" ways that deviate from the "correct way" that is defined. After re-reading, all problems disappear.

Steps to Reproduce

  1. have a complicated project with specific operational requirements
  2. do a lot of work until compaction
  3. watch claude not follow CLAUDE.md
  4. tell claude to read CLAUDE.md and watch it follow it

Expected Behavior

After compaction, Claude should remember or re-read CLAUDE.md

Actual Behavior

Claude does not remember CLAUDE.md after compaction

Additional Context

None

Activity

  1. github-actions commented on Aug 22, 2025

    @github-actions

    Found 3 possible duplicate issues:

    1. [BUG] /compact causes Claude Code to ignore CLAUDE.md #4017
    2. [BUG] /compact command loses CLAUDE.md context, causing behavioral rule violations #4517
    3. [BUG] Claude keeps forgetting about the context in CLAUDE.local.md after compacting #1027

    This issue will be automatically closed as a duplicate in 3 days.

    • If your issue is a duplicate, please close it and 👍 the existing issue instead
    • To prevent auto-closure, add a comment or 👎 this comment

    ðŸĪ– Generated with Claude Code

  2. igorkofman commented on Aug 22, 2025

    @igorkofman
    Collaborator

    Could you try asking Claude to recite what is in CLAUDE.md when compaction happens (you can trigger compaction manually using /compact). In my testing it still have the contents of CLAUDE.md in context but would be great to have a repro of this not happening.

  3. DougJoseph commented on Sep 12, 2025

    @DougJoseph

    I have this problem with every auto-compact. Claude Code forgets what is in Claude.md and I have to tell it to read again.

  4. rogerc66 commented on Oct 21, 2025

    @rogerc66

    I can confirm this issue is critical for production workflows. I maintain a comprehensive CLAUDE.md with specific instructions for:

    • File creation protocols (checking for duplicates before creating)
    • Vault structure conventions (customer directories, naming patterns)
    • Tool-specific workflows (email archiving, git operations)
    • Integration points (MCP tools, custom scripts)

    After conversation compaction/summarization, Claude needs to be explicitly reminded to follow CLAUDE.md conventions, otherwise it reverts to generic behavior that doesn't match my established patterns.

    Real example from today: Built a smart email archiver system with specific filtering rules, attachment handling, and vault organization patterns all defined in CLAUDE.md. After compaction, continuing work would require manually asking Claude to re-read the context.

    Impact: This breaks continuity in long-running projects where CLAUDE.md serves as the single source of truth for project-specific conventions.

    +1 for making CLAUDE.md persistent across compaction cycles.

  5. tatyree commented on Nov 24, 2025

    @tatyree

    I routinely have this problem as well. It just wiped out a running production project because it carried on making changes after compaction and before I could catch it.

  6. TerrysPOV commented on Dec 20, 2025

    @TerrysPOV

    @claude : just create/enable a post compact hook and this MAJOR issue would be solved. You release features all the time far more useless and complex than this would be to do. Come on guys this is killing us!

  7. solace-rcampbell commented on Jan 7, 2026

    @solace-rcampbell

    I also routinely run long sessions (sometimes for weeks) with hundreds of interactions and dozens of context compactions. After every compact, I have to tell it to re-read the memory files. Please either make this automatic, or add a hook for after compact.

  8. wiwa commented on Jan 28, 2026

    @wiwa

    Edit: I would likely not recommend the below, due to https://news.ycombinator.com/item?id=46723384
    (possibly off-topic, but on the topic of guideline stability): It seems we can, even without CLAUDE.md changes, "poison" the compactor agent itself, transferring style+information (i.e. guidelines) into the summary, and can even give the summarizer tasks. I presume this would be easier if you added lines in CLAUDE.md for the summarizer ('upon a summary request...'). I also assume that this is all within normal expectations since this issue implies guiding auto-compaction is an expected of CLAUDE.md: #7708 (comment)
    If you have Claude Code give such a compaction summary to a subagent, that subagent will automatically assume certain guidelines. Seems like the models give extra high priority to 'the summary' over other things. Presumably one could just skip all the ceremony and write their own compaction-summary-like text for any Claude Code agent to pattern match on.
    Something possible more useful for auto-compaction: TodoWrite a final item for "read CLAUDE.md post-compaction" (haven't tested though)

  9. asvishnyakov commented on Jan 29, 2026

    @asvishnyakov

    HALF A YEAR! And nothing done for that. I'm sorry, this isn't even free, we pay you money for that

  10. tiagogbarbosa commented on Jan 30, 2026

    @tiagogbarbosa

    I dont know that is the problem to implement this? The lack of this make experience WAY worst, as someone that pay 200USD/MONTH i guess you guys need at least a explanation why dont want to implement, because its IMPOSSIBLE to be hard to implement a hook after compact!

  11. SebRogala commented on Feb 4, 2026

    @SebRogala

    Findings from extensive SessionStart hook experimentation

    We tested using the existing SessionStart hook with compact matcher to auto-refresh CLAUDE.md. Here's what we found:

    Setup that works for /clear:

    {
      "hooks": {
        "SessionStart": [{
          "matcher": "clear",
          "hooks": [{
            "type": "command",
            "command": "cat \"$CLAUDE_PROJECT_DIR\"/.claude/session-start-reminder.md; exit 0"
          }]
        }]
      }
    }

    With reminder content:

    STOP. Your context was just reset.
    
    Step 1: Say "Let me refresh my context first."
    Step 2: Call Read tool on CLAUDE.md (visible to user)
    
    DO NOT skip Step 2. DO NOT assume CLAUDE.md is loaded.
    

    Key insight: The instruction must be the first line and include a specific observable action (say a phrase, then visibly read file). Passive context injection doesn't work.

    Results:

    Trigger Hook fires? Content injected? Claude follows it?
    /clear ✅ ✅ ✅
    /compact ✅ ✅ ❌

    The problem with /compact: The compacted conversation summary creates "momentum" that overrides the injected reminder. Claude sees the summary ("user asked about X") and continues that thread, ignoring the hook output.

    Suggested fix: The hook output needs higher priority than the compacted summary - perhaps injected AFTER the summary in Claude's context, or treated as a system instruction rather than context.

  12. junaidtitan commented on Feb 10, 2026

    @junaidtitan

    This has been my biggest pain point too — CLAUDE.md instructions forgotten after compaction, and then Claude goes off-script. The root issue is that compaction is lossy compression, and when it's operating on a session full of noise (progress ticks, duplicate system reminders, stale file reads, metadata), the signal-to-noise ratio is terrible and the important stuff gets summarized away.

    What helped me was Cozempic. The approach: remove the noise before compaction fires, so when it does compress, it has a much better signal-to-noise ratio and CLAUDE.md instructions are more likely to survive.

    pip install cozempic
    cozempic init

    cozempic init auto-wires a guard daemon that starts on every session and continuously prunes dead weight — progress ticks (often 40-50% of session size), duplicate system-reminder injections, stale file reads that were superseded by later edits, thinking block signatures, metadata bloat. On a typical session I see 15-22% size reduction from pure noise removal.

    The result: compaction triggers far less often, and when it does, it has much less junk to wade through. Won't fix the fundamental issue of compaction being lossy, but it significantly reduces how often you hit it and improves the quality when you do.

    Would appreciate if you could give it a try and let me know if it helps with the CLAUDE.md retention. If compaction is still dropping your instructions after pruning, I'd like to investigate what's left in the session and see if there are additional strategies to apply.

  13. Grivn commented on Feb 21, 2026

    @Grivn

    This thread captures the core problem well — compaction summaries create "momentum" that overrides re-injected instructions.

    I've been running mnemon in production to address this — it's a standalone skill binary for persistent agent memory. Rather than fighting the compaction summary, mnemon stores knowledge in a local SQLite graph outside the context window. After compaction, the SessionStart hook re-injects relevant entities via structured recall — not re-reading a monolithic CLAUDE.md, but querying specific facts and decisions relevant to the current work.

    The PreCompact hook also extracts critical insights before compression occurs, so knowledge survives the cycle.

    brew install mnemon-dev/tap/mnemon && mnemon setup

    mnemon setup auto-detects Claude Code and deploys skill + four lifecycle hooks. Portable — the same binary + skill works across Claude Code, OpenClaw, and any LLM CLI that reads markdown skills.

  14. bodiam commented on Feb 24, 2026

    @bodiam

    What helped me was [insert product here]. The approach: remove the noise before compaction fires, so when it does compress, it has a much better signal-to-noise ratio and CLAUDE.md instructions are more likely to survive.

    Would be nice if you stop advertising your own projects here, especially without a disclaimer.

  15. junaidtitan commented on Feb 24, 2026

    @junaidtitan
  16. asvishnyakov commented on Mar 8, 2026

    @asvishnyakov

    Any update on this?

  17. yurukusa commented on Mar 24, 2026

    @yurukusa

    A PreCompact hook can save critical state before compaction happens:

    HANDOFF="$HOME/.claude/session-handoff.md"
    {
        echo "# Pre-Compact State ($(date -Iseconds))"
        echo ""
        [ -d .git ] && echo "Branch: $(git branch --show-current 2>/dev/null)"
        [ -d .git ] && echo "Last commit: $(git log --oneline -1 2>/dev/null)"
        [ -d .git ] && echo "Modified: $(git diff --name-only 2>/dev/null | head -5)"
    } > "$HANDOFF"
    exit 0

    Register as PreCompact hook in settings.json. After compaction, the next turn can read ~/.claude/session-handoff.md to recover context about what was happening.
    For automatic session end state capture:

    npx cc-safe-setup --install-example session-handoff

    Not a complete fix (CLAUDE.md should be re-injected by the system), but preserves the "what was I working on" context.

  18. lipcool8-ship-it commented on Mar 25, 2026

    @lipcool8-ship-it

    I keep seeing the same failure pattern: the agent follows repo-specific rules until compaction, then falls back to generic behavior and starts violating the project’s actual way of doing things.

    For people here, what rule types hurt most when they get dropped? Stack constraints, migration/schema rules, banned commands/patterns, or post-task workflow steps?

  19. junaidtitan commented on Apr 3, 2026

    @junaidtitan

    This is caused by compaction summarizing CLAUDE.md instructions into prose — the specific rules get diluted or dropped. The compact-summary-collapse strategy in Cozempic v1.4.1 proactively removes stale pre-compaction content (85-95% savings) so compaction operates on a much smaller, cleaner input.

    Additionally, the behavioral digest feature extracts your corrections and syncs them to Claude Code's memory system as feedback type memories. These persist on disk and survive compaction natively — Claude re-reads them on every turn.

    pip install cozempic && cozempic init

    Run cozempic digest show to see what rules have been extracted from your sessions.

  20. yurukusa commented on Apr 6, 2026

    @yurukusa

    This is a known structural limitation — compaction summarizes conversation history but doesn't preserve the full CLAUDE.md context. Two workaround approaches using hooks:

    1. Save state before compaction (PreCompact hook)

    #!/bin/bash
    # PreCompact hook — saves session state before compaction
    set -euo pipefail
    INPUT=$(cat)
    
    CHECKPOINT=".claude/pre-compact-checkpoint.md"
    mkdir -p .claude
    
    {
      echo "# Pre-compaction checkpoint ($(date -Iseconds))"
      echo ""
      echo "## Current branch"
      git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "not a git repo"
      echo ""
      echo "## Modified files"
      git diff --name-only 2>/dev/null | head -20
      echo ""
      echo "## Recent commits"
      git log --oneline -5 2>/dev/null
      echo ""
      echo "## Critical CLAUDE.md rules"
      grep -iE '(ABSOLUTE|MUST NEVER|NEVER|CRITICAL|į́æ­Ē)' CLAUDE.md 2>/dev/null | head -20
    } > "$CHECKPOINT"
    
    echo "ðŸ“ļ Pre-compact checkpoint saved to $CHECKPOINT" >&2
    exit 0

    Register in settings.json:

    {
      "hooks": {
        "PreCompact": [{
          "hooks": [{ "type": "command", "command": "bash .claude/hooks/pre-compact-knowledge-save.sh" }]
        }]
      }
    }

    After compaction, Claude can read .claude/pre-compact-checkpoint.md to recover context.

    2. Periodic CLAUDE.md re-injection (PostToolUse hook)

    #!/bin/bash
    # PostToolUse hook — reminds critical rules every 20 tool calls
    set -euo pipefail
    
    COUNTER_FILE="/tmp/claudemd-reminder-counter"
    COUNT=$(cat "$COUNTER_FILE" 2>/dev/null || echo "0")
    COUNT=$((COUNT + 1))
    echo "$COUNT" > "$COUNTER_FILE"
    [ $((COUNT % 20)) -ne 0 ] && exit 0
    
    CLAUDEMD=""
    for candidate in "CLAUDE.md" ".claude/CLAUDE.md" "../CLAUDE.md"; do
      [ -f "$candidate" ] && CLAUDEMD="$candidate" && break
    done
    [ -z "$CLAUDEMD" ] && exit 0
    
    echo "⚡ CLAUDE.md critical rules reminder (tool call #${COUNT}):" >&2
    grep -iE '(ABSOLUTE|MUST NEVER|NEVER|CRITICAL|į́æ­Ē)' "$CLAUDEMD" 2>/dev/null | head -10 | while read -r line; do
      echo "  → $line" >&2
    done
    exit 0

    This extracts lines containing enforcement markers (ABSOLUTE, MUST NEVER, etc.) and prints them to stderr every 20 tool calls. The output appears in Claude's context, acting as a persistent reminder that survives compaction.

    Neither approach is perfect — the root fix needs to come from Anthropic preserving CLAUDE.md context across compaction boundaries. But these hooks significantly reduce the "amnesia" effect in long sessions.

  21. marcindulak commented on Aug 17, 2026

    @marcindulak
  22. John-Rood commented on Sep 27, 2026

    @John-Rood

    The gap has moved since most of this thread was written, so here is where it stands on current builds. PostCompact exists (added in 2.1.76) and its payload carries the full compact_summary. SessionStart with matcher compact is a real injection point that supports additionalContext. InstructionsLoaded fires with load_reason: "compact" when instruction files are re-loaded after a compaction. The rules text surviving is mostly handled now. What still fails is rank. After compaction the summary is the live frame, so anything re-injected reads as background and gets outranked, which matches the earlier hook tests in this thread: the reminder fires on /compact and the model still follows the summary.

    Re-injection loses that fight. A denied tool call does not.

    So gate instead. After any compaction, the first mutating tool call is refused until the model reads the rules file, and that read clears the gate. The re-anchor stops being optional, and it covers /compact and auto-compaction the same way.

    .claude/hard-rules.md, only the rules that actually get violated, one line each:

    # Hard rules
    
    Read after every compaction. One line per rule, no prose.
    
    1. Never commit directly to main. Use a branch and open a PR.
    2. Never edit anything under vendor/. Patch upstream instead.
    3. Run pnpm test --filter changed before every commit.
    4. Create a rollback checkpoint before any destructive command.
    5. Do not reformat files outside the requested change.

    .claude/hooks/post-compact.sh saves the summary that replaced the transcript and arms the gate. PostCompact cannot inject context, so the message goes through SessionStart instead:

    #!/usr/bin/env bash
    # PostCompact hook: save the summary that replaced the transcript, arm the re-anchor gate.
    set -euo pipefail
    cd "${CLAUDE_PROJECT_DIR:-.}"
    input=$(cat)
    sid=$(printf '%s' "$input" | jq -r '.session_id // "nosession"' | cut -c1-8)
    agent=$(printf '%s' "$input" | jq -r '.agent_id // empty')
    trigger=$(printf '%s' "$input" | jq -r '.trigger // "unknown"')
    stamp=$(date -u +%Y%m%dT%H%M%SZ)
    mkdir -p .claude/compact-log
    log=".claude/compact-log/${stamp}-${trigger}-${sid}.md"
    printf '# compaction %s (%s)\n\n%s\n' "$stamp" "$trigger" "$(printf '%s' "$input" | jq -r '.compact_summary // ""')" > "$log"
    # main conversation only: a teammate compaction should not gate the lead session
    if [ -z "$agent" ]; then touch .claude/.reanchor-required; fi
    printf 'compact audit: %s\n' "$log" >&2
    exit 0

    .claude/hooks/session-start-compact.sh tells the model what is blocked and what clears it:

    #!/usr/bin/env bash
    # SessionStart hook (matcher: compact): tell the model the gate is armed and what clears it.
    set -euo pipefail
    cd "${CLAUDE_PROJECT_DIR:-.}"
    [ -f .claude/.reanchor-required ] || exit 0
    latest=$(ls -t .claude/compact-log/*.md 2>/dev/null | head -n 1 || true)
    ctx="Context was just compacted. Mutating tools are blocked until you read .claude/hard-rules.md with the Read tool. That read clears the gate."
    if [ -n "$latest" ]; then ctx="$ctx The summary that replaced the transcript is saved at $latest."; fi
    jq -n --arg c "$ctx" '{hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:$c}}'
    exit 0

    .claude/hooks/reanchor-gate.sh is the enforcement. Only Bash, Edit, Write and NotebookEdit are denied while armed, so the model can still find and read the rules file with its normal tools, and everything unblocks on that read:

    #!/usr/bin/env bash
    # PreToolUse hook (no matcher): block writes after a compaction until the rules file is read.
    set -euo pipefail
    cd "${CLAUDE_PROJECT_DIR:-.}"
    marker=.claude/.reanchor-required
    [ -f "$marker" ] || exit 0
    input=$(cat)
    tool=$(printf '%s' "$input" | jq -r '.tool_name // empty')
    if [ "$tool" = "Read" ]; then
      file=$(printf '%s' "$input" | jq -r '.tool_input.file_path // empty')
      case "$file" in
        *hard-rules.md|*CLAUDE.md) rm -f "$marker"; exit 0 ;;
      esac
    fi
    case "$tool" in
      Bash|Edit|Write|NotebookEdit)
        jq -n '{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:"Post-compact re-anchor pending: read .claude/hard-rules.md first. That read clears this gate."}}'
        exit 0 ;;
    esac
    exit 0

    .claude/hooks/instructions-loaded.sh records which instruction files actually re-loaded, so the reload question stops being guesswork:

    #!/usr/bin/env bash
    # InstructionsLoaded hook (matcher: compact): record which instruction files reloaded.
    set -euo pipefail
    cd "${CLAUDE_PROJECT_DIR:-.}"
    input=$(cat)
    [ "$(printf '%s' "$input" | jq -r '.load_reason')" = "compact" ] || exit 0
    mkdir -p .claude/compact-log
    printf '%s reloaded: %s\n' "$(date -u +%H%M%SZ)" "$(printf '%s' "$input" | jq -r '.file_path')" >> .claude/compact-log/reloads.log
    exit 0

    Merge into .claude/settings.json:

    {
      "hooks": {
        "PostCompact": [
          { "hooks": [{ "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/post-compact.sh" }] }
        ],
        "SessionStart": [
          { "matcher": "compact", "hooks": [{ "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/session-start-compact.sh" }] }
        ],
        "PreToolUse": [
          { "hooks": [{ "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/reanchor-gate.sh" }] }
        ],
        "InstructionsLoaded": [
          { "matcher": "compact", "hooks": [{ "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/instructions-loaded.sh" }] }
        ]
      }
    }

    Verify on your build:

    1. chmod +x .claude/hooks/*.sh. jq must be on PATH.
    2. Run /compact, then ask for a small edit. The edit gets denied with "Post-compact re-anchor pending", the model reads .claude/hard-rules.md, and the same edit goes through in that turn.
    3. cat .claude/compact-log/*.md now holds the summary that replaced your transcript, on disk. Diff it against hard-rules.md once. You will see which rules the compactor paraphrased away instead of keeping.
    4. tail .claude/compact-log/reloads.log lists every instruction file that re-loaded and why, so you know what actually comes back on your machine.

    The log folder and the marker are local state: add .claude/compact-log/ and .claude/.reanchor-required to .gitignore.

    The gate arms only when agent_id is absent, because these events also fire for teammate compactions (#91910). Confirm PostCompact actually fires on your version and platform before trusting the gate (#77832 reports Windows misses). If it does not fire, the gate cannot arm.

  23. dripston commented on Sep 28, 2026

    @dripston

    For anyone hitting this and looking for a stopgap while this is open: I built a small Claude Code plugin, seatbelt, that re-reads and re-injects CLAUDE.md at the moments it's most likely to have been dropped — SessionStart on compact/resume, and past a token-depth threshold in long sessions. It also gives a systemMessage confirmation each time it fires, so it's not silent.

    To be clear about what it isn't: it doesn't fix the underlying compaction behavior (that's this issue), it just re-supplies the context afterward, and I don't have a clean measurement yet of how much that actually improves adherence versus just having the rules re-read again. It's a workaround for the symptom, not a fix for the cause.

  24. samvallad33 commented on Oct 1, 2026

    @samvallad33

    tatyree's comment above is the one that should anchor this thread — a production project damaged because, post-compaction, the model kept changing things by "common sense" instead of the documented way. The structural problem is that CLAUDE.md is context, and compaction is a lossy summary of context: anything that lives only in the window gets rewritten at exactly the moment the work is longest and least supervised. We keep durable rules outside the window instead — a local-first memory kernel with an append-only, tamper-evident log, where a rule's exact handle stays retrievable no matter what the compactor kept. No embeddings, no fuzzy recall; you keep handles honest, and that's the trade. It wouldn't have saved everything here, but the conventions would still have been addressable. brew install samvallad33/tap/vestige

    (Disclosure: I work on Vestige, the memory kernel just described; free and open-source.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions