DEV Community

Rulestack
Rulestack

Posted on

What survives compaction in Claude Code — and how to keep your rules alive

Long Claude Code sessions eventually hit the context window ceiling. When they do, Claude Code runs compaction: it summarizes the conversation so far and starts a fresh window seeded with that summary. Work continues — but not everything you loaded at the start makes the trip.

If you've ever watched an agent suddenly forget a rule it had been following for an hour, this is usually why. Here's what actually survives compaction, what quietly dies, and a workaround that keeps the important parts alive.

What compaction actually does

Compaction is not truncation. Claude Code doesn't just drop old messages; it writes a summary of the session — decisions made, files touched, current task state — and rebuilds the context from that summary plus a small set of always-loaded material.

That distinction matters because a summary is lossy by design. It preserves the shape of the work ("we are migrating module X, tests are failing on Y") but not the exact text of everything that was ever in the window. Anything whose value depends on its exact wording — rules, checklists, contracts — is at risk unless it lives somewhere that gets reloaded.

What survives

  • The root CLAUDE.md. It's re-read into the fresh window, so project-level instructions come back verbatim. This is the single most reliable place for a rule to live.
  • Unscoped rules — the always-on portion of your rules setup — are retained the same way.
  • The compaction summary itself, which carries forward the task narrative and recent decisions.

What dies

  • Scoped rules that were pulled in because you were working on matching files. After compaction, they're gone until something re-triggers them — and if the agent's next action doesn't match the scope, it acts without the rule it was just following.
  • Mid-session instructions you typed two hours ago. They exist only as whatever the summary kept of them.
  • Injected context from earlier hook runs or tool output. The raw text is gone; only the summary's paraphrase remains.

The scoped-rules case is the sneaky one. The whole point of scoping is to keep the always-loaded set small — but it also means your most specific, most carefully written rules are the ones with the shortest lifespan.

The workaround: SessionStart with matcher: compact

Claude Code's SessionStart hook fires not just on a brand-new session but also right after compaction, when the matcher is set to compact:

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          { "type": "command", "command": "cat .claude/reinject.md" }
        ]
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

Whatever that command prints to stdout is injected into the fresh window. The window turning over stops being an event you can't see and becomes a hook you can handle: re-state the invariants, re-load the working notes, re-assert the rule that must not be dropped.

Keep the re-injected payload small

The tempting move is to re-inject everything — paste the full rules file back in after every compaction. Resist it. Compaction just reclaimed tokens; refilling the window with the same bulk material spends them again immediately, and you're back at the ceiling sooner.

What works better is re-injecting an index, not the state itself:

  • one line per invariant that must hold ("tests must pass before any commit"),
  • pointers to where the detail lives ("full DB migration rules: .claude/rules/db.md"),
  • the current task state in two or three lines.

The agent can follow a pointer when it needs the detail. A 15-line reinject file costs almost nothing per compaction; a 400-line one costs you the headroom compaction just bought.

A practical checklist

  1. Sort your rules by lifespan. Anything that must hold for the whole session belongs in the root CLAUDE.md, not in a scoped rule.
  2. Treat scoped rules as ephemeral. They're for file-specific detail the agent can afford to re-discover, not for invariants.
  3. Add a SessionStart hook with matcher: compact that prints your reinject index.
  4. Keep the index under ~20 lines. Pointers over payloads.
  5. Test it deliberately: run /compact manually mid-session and check whether the agent still honors the rule you care about. If it doesn't, the rule was living in the part of the window that doesn't survive.

Compaction is a good trade — long sessions would be impossible without it. The failure mode is only silent if you haven't decided on purpose where each rule should live and what gets re-injected when the window turns over. Decide once, wire the hook, and the agent stops forgetting.


I maintain Rulestack, a set of rules/skills packs for Claude Code, Cursor, and Codex — including hook recipes like this one.

Daily AI-coding-agent tips on Bluesky: @ai-shop.bsky.social — or follow our "AI Coding Agents" custom feed there.

Top comments (0)