Surviving context compaction with a handoff doc

· Valentin Mihai · ~6 min read

  • claude-code
  • ai-agents
  • context-window
  • documentation
TL;DR

Context compaction is lossy in a predictable direction: it keeps what happened and drops what it cost to learn. Git already preserves the first. Nothing preserves the second, so the next session pays for the same lesson twice.

cc-handoff is a versioned checkpoint doc with one filter deciding what goes in it.

github.com/mihai-valentin/cc-handoff · Claude Code plugin, MIT

what compaction actually takes

When a long agent session runs out of context, it gets summarised. The summary is competent at continuity: what the task was, which files moved, where things stand.

What does not survive is the expensive part. The approach you tried for an hour before it turned out to be wrong. The belief you held confidently and then disproved. The environment quirk that took three failed runs to pin down. The finding you deliberately decided not to act on — which, to a fresh session, is indistinguishable from a finding nobody noticed.

Notice that those are precisely the facts that cannot be recovered from anywhere else. The commits are in git. The code is on disk. The tests still run. The reasoning was only ever in the conversation, and now it is gone — so the next session re-derives it, at full price.

the filter

A checkpoint doc is only as good as its admission policy, because a doc that records everything is just a second, worse copy of the transcript. So every candidate fact faces one question:

Could the next session recover this from git log -p, the code, and the test suite in under a minute?

If yes, leave it out. Git does file inventories better than prose and is never wrong about them. If no, it belongs.

What survives that filter is a short and fairly specific list: rejected alternatives and why, beliefs that turned out false, findings deliberately declined, environment traps found by trial and error, constraints that were only ever stated in conversation — and the difference between "the tests pass" and "I ran the tests", which is a distinction a summary will quietly flatten.

the rule that stops it rotting

The interesting failure mode of a checkpoint doc isn't being incomplete, it's being confidently out of date. One rule governs that:

Any sentence describing work that REMAINS must be true at the version it ships in.

A stale "next steps" list is worse than having no doc at all. With no doc, the next session reads the code. With a stale doc, it trusts a plan and carefully rebuilds something that already exists. So each pass begins by sweeping the previous version for forward-looking claims that have since become false.

This is also why the doc increments rather than regenerates. Every write bumps v<N>, replaces what changed, and appends what was learned — so a dead end discovered in v1 is still there in v7, which is the entire value. Regenerating from scratch each time would throw away exactly the history the tool exists to keep.

knowing when to write it

A checkpoint doc written under pressure, with the context window nearly full, is a worse doc. So a hook reads the true context size from the transcript after each turn and prints one line at 75% and one at 88%:

📝 Context 76% (760k/1000k). Good moment for /handoff — the doc is better
   written now than under pressure.

Deliberately, the hook only prints. It never invokes the skill and never injects context of its own — a tool whose job is to protect your context budget has no business spending it uninvited. Deciding when to checkpoint stays a human call.

The one configuration that matters: the hook cannot detect your context window, because a transcript records the model id but not whether a large-context variant is in play. It has to be told. Leave CC_CONTEXT_LIMIT unset on a 1M-window model and every percentage is five times too high — the first nudge fires around 15% full, you learn within a day to ignore it, and then there is no warning at all when 88% actually arrives. A nudge you have been trained to dismiss is worse than no nudge, so the message labels the figure assumed 200k until you configure it.

and the other direction

Writing the doc is half of it; the doc has to come back on its own, or it won't be read. A SessionStart hook restores the first three sections the moment compaction lands, and a resume command does the full pass — read the doc, reconcile it against the repository as it is now, and report what has gone stale. That pass is strictly read-only: reconciliation tells you what drifted, and never quietly edits the record to match.