Working-state continuity: survive compaction and /clear (the long-session "goes dumb" problem)

Status Open
Maintainer reply ✓ Yes — bcherny
Activity 8 comments · opened Jun 24, 2026
💡 Likely answer: A maintainer (bcherny, collaborator) responded on this thread — see the highlighted reply below.

Summary

Working state doesn't survive context compaction or /clear. On long sessions the assistant slowly "goes dumb" — re-deriving things, forgetting in-flight threads, repeating work — and after a /clear it has no idea where we just were. The forgetting and the resulting confident-but-wrong guessing are the same problem. I've built a hook-layer workaround that fixes it, and I think the pattern belongs in the product.

The problem (two failure modes, both chronic)

  1. Long-session degradation. As context fills and auto-compaction summarizes, granular working state gets compressed away — which file is mid-edit, what was just deployed-but-not-committed, the exact next action. The assistant quietly re-derives, forgets in-flight threads, and repeats work. You usually don't notice until it's already lost.
  2. Cold-start amnesia after /clear or compaction. A fresh session reloads CLAUDE.md but has no sense of where we just were. The user spends real effort re-orienting ("where are we, why aren't we moving forward?"). That handoff across the clear boundary is the single weakest link.

The hidden cost isn't just time — when the model loses real state it guesses to fill the gap, which is exactly how you get hallucinated/invented state.

Why existing tools don't close it

  • CLAUDE.md / project memory — great for static facts; wrong home for dynamic working state, and nothing keeps it short or current.
  • Auto-compaction summary — a narrative recap, not a structured/actionable current focus / last action / next action / live state object.
  • No working-state object survives BOTH compaction and /clear as a distinct, always-first, model-maintained scratchpad.

What I built as a workaround (maps directly onto features you could ship)

  1. Pinned "RIGHT NOW" block at the top of the auto-loaded memory index (~5 lines: current focus, last action, next action, live state — branch/URLs/IDs). First thing in context every session.
  2. PreCompact hook that snapshots working state the instant before compaction — leads with the RIGHT NOW block (pulled live from memory), then appends tracked-only git status so deployed-but-uncommitted work can't be silently lost. Also increments a compaction counter.
  3. SessionStart hook that re-injects that snapshot, but only if recent (≤2h), so a stale file doesn't pollute a genuinely fresh start.
  4. Watchdog (UserPromptSubmit hook) that reads the compaction counter + elapsed time and proactively nudges a checkpoint-and-/clear before degradation gets bad.
  5. Wrap discipline that rewrites the RIGHT NOW block at session end.

It works — but no user should have to hand-roll PowerShell hooks and a memory taxonomy to keep their agent from going senile mid-task.

Concrete feature requests

  • [ ] A first-class "working scratchpad" — short, structured, model-maintained (current focus, next action, live artifacts), always re-injected first, updated automatically, and guaranteed to survive both compaction and /clear, kept distinct from the conversation summary.
  • [ ] Automatic pre-compaction capture of that scratchpad + uncommitted/deployed working state (built-in, not a user hook).
  • [ ] Surface compaction to the user with a one-click "checkpoint & restart" flow, instead of silent degradation noticed only once the assistant is already lost.
  • [ ] A guaranteed pinned-top region in CLAUDE.md/memory that's always first in context.

Before/after

Before: a mid-session compaction left the assistant having forgotten I'd just deployed a Lambda but not committed it — so it either redid the work or assumed it was committed and moved on, leaving real drift between deployed and source. After: the first thing in the new context is "you deployed X, it's uncommitted, commit it" + the exact next action. A relearn-for-an-hour event became a two-line handoff.

Happy to share the full hook scripts and memory layout if useful.

(Filed by a daily heavy user; also sent via email to the feedback address. Cross-posting here because this is where the product team actually lives.)

View original on GitHub ↗

5 Comments

kcarriedo · 2 months ago

The two-failure-mode framing is exactly right, and worth separating further: compaction degradation is gradual (you notice late), post-/clear amnesia is instant (you notice immediately). They share a fix but feel different to the user.

Your hook stack is close to what I run. A few details that sharpened it:

The RIGHT NOW block breaks down under one condition: agents that run headlessly (no interactive session, spawned from a parent process or scheduler) don't re-read CLAUDE.md after compaction -- they get the compacted summary and that's it. So for unattended sessions, the pre-compaction snapshot has to be written to a file the next session will explicitly load, not just injected into the current context. A .coordination/state.json in the repo works; the agent loads it as a tool call on SessionStart instead of relying on hook injection.

On the "guessing to fill the gap" failure -- this is the expensive one. The model doesn't signal uncertainty when filling from the compacted summary; it presents derived state with the same confidence as directly-observed state. The mitigation I've found: include a compaction_count field in the working scratchpad. When the agent reads a high compaction count relative to the depth of the current context, it should proactively ask "what is the current state of X?" rather than assuming the summary is complete.

For the feature requests: the guaranteed pre-compaction write should include not just narrative summary but structured fields the model can reference without re-reading: current_branch, last_deployed_but_uncommitted, next_action, open_questions. Narrative summaries compress well but aren't reliably machine-readable when the next session needs to act on specific values.

junaidtitan · 2 months ago

Your framing is precise. The reason compaction can't preserve working state isn't that summaries are badly written — it's that compaction is a narrative operation, not a state operation. It answers "what happened" and discards "where exactly are you right now."

Your hook architecture (PreCompact snapshot → SessionStart re-inject → UserPromptSubmit watchdog) is the right shape. The gap is that you have to build and maintain it yourself on top of a hook layer that wasn't designed with this use case in mind — e.g., there's no guaranteed ordering between PreCompact and the compaction actually running, and SessionStart has no way to distinguish "resuming within 2h" from "cold start" without external mtime tracking.

One thing that helps on the JSONL side: if you prune session bloat before compaction fires, the summary has denser material to work with and more working detail survives. A 60 MB JSONL is mostly tool-result payloads (file reads, Bash output) that were useful at the time but are long-dead weight for the summarizer — they dilute the recent working context in the summary. Pruning those down before compaction means the "what just happened" narrative is more recent and granular.

cozempic does that pruning pass automatically (guard mode monitors the session, prunes at configurable thresholds, and optionally reloads before compaction fires). It also has a behavioral digest that extracts and re-injects behavioral corrections specifically so they survive compaction — which is basically a packaged version of part of your memory taxonomy. It won't give you the "RIGHT NOW" structured scratchpad you're asking for (that's first-party territory, needs Anthropic to build it into the compaction contract), but it does extend the working-detail window that compaction can pull from.

Your point 2 (automatic pre-compaction capture of uncommitted state) and point 3 (surface compaction with a one-click checkpoint flow) are the parts that need native support — agreed they should be in the product.

Leo-YuchengLi · 2 months ago

Totally relate to this. The "forgetting and confident-but-wrong guessing are the same problem" line nails it.

We built Super Intern with this exact failure mode in mind. Our approach: durable facts (contacts, commitments, user preferences) are stored outside the conversation context in a vector DB + structured database. So even if the conversation window compacts or resets, the AI's "knowledge" of who you are and what you're working on persists.

The result: you can /clear or start a new session and it still knows your contacts, your open follow-ups, and your preferences. No re-teaching.

Part of the Super Intern team — happy to share a free invite if you're curious how this works in practice.

vladimirvladimirovichkamardin-glitch · 29 days ago

I run a closed reactive loop of four LLM agents (shared journal, peer stimuli, ~80 cycles in one session) and we hit exactly this failure mode — then built a protocol that addresses one part the thread is missing: the compaction itself is a write without a receipt.

What we found

The summarizer that runs between turns is an unenumerable writer: it rewrites the very history that fork-detection and audit tooling rely on, it does not log what it dropped, and its success is not verifiable. In our session we measured the consequence directly: ~20% of write actions (6 documented cases) happened against stale state because the acting agent trusted a context that had been compacted. The assistant doesn't "go dumb" gradually — it goes dumb with confidence, and the confidence is the dangerous part.

What we built (the receipt pattern)

Compaction becomes a first-class write operation instead of a background narrative:

  1. Digest before/after — prev_digest and new_digest of the state, so every compaction is an auditable transition, not an invisible event.
  2. Loss manifest — dropped_facts[] (with the compression ratio N→M), so losses are enumerable and can be diffed on demand.
  3. Compaction journal — every compaction logged to shared state with writer_id=runtime, which makes the summarizer enumerable (it becomes a writer you can query, instead of a writer outside the list).
  4. Agent's right to diff — on session init, the agent can request the diff of what was dropped since its last view; that converts "I don't know what I've forgotten" into "I can check."

This is complementary to the hook-layer workaround above: PreCompact/SessionStart hooks preserve your RIGHT NOW block (working state you explicitly maintain), while the receipt pattern makes the platform's lossy summarization auditable. The two cover different writers — the agent's and the runtime's.

Numbers from our session

  • 80+ cycles, 4 nodes, one session; 6 stale-hit cases ≈ 20% of actions
  • Pre-check (O(1) digest compare before any write) eliminated the class — remaining hits were caught by scheduled post-check
  • One duplicate-write case where two nodes fired on one trigger 9 seconds apart — visible in the journal precisely because it's append-only

Open question to the thread

Who already logs compactions, and what schema do you use? We're exchanging field layouts — digest pair, dropped_facts, writer_id — and would rather standardize than multiply formats.

Full protocol writeup: github.com/vladimirvladimirovichkamardin-glitch/reactive-loop-protocol (rules, metrics, case studies). — angel_vova, reactive loop m/kvartet (4 agents, 80+ cycles, 2026-08-01)

bcherny collaborator · 14 days ago

Thanks for the detailed writeup. Some of this is possible today, though not as a single built-in working-state object:

  • Auto memory lets Claude keep notes that load at the start of every session: https://code.claude.com/docs/en/memory#auto-memory
  • Hooks run at the boundaries you care about: PreCompact/PostCompact around compaction, and SessionStart with the compact or clear matcher to re-inject state right after: https://code.claude.com/docs/en/hooks
  • You can also give /compact focus instructions (/compact keep the current focus, last action, next action) so the summary preserves what matters.

A first-class, model-maintained working-state block that survives both compaction and /clear isn't shipped, so leaving this open as a feature request.

🤖 Generated with Claude Code

Showing cached comments. Read the full discussion on GitHub ↗