letsautom8
← Lab notes

Handing over before compaction: what a long Claude Code session forgets, and how mine write it down first

My master sessions are long-lived on purpose. A project keeps one Claude Code conversation for days, and that's the point: no re-explaining where we were. But a conversation can't grow forever. When it reaches the model's context limit, Claude Code compacts it: the history is replaced by a summary, and the session carries on from that.

Most of the time you don't notice. The trouble is the things you would only notice by their absence.

What survives a compaction, by default

Claude Code's docs are specific about some of this. After a compaction:

What the summary is told to keep isn't documented beyond "key information". You can steer it with /compact <instructions> when you compact by hand, but an automatic compaction happens when the window is full, mid-task, with no instructions.

What that loses in practice

The rule in my lab is that the GitLab issue is the record. Decisions are written there as "Decided: …", progress is shown by labels, and sessions report on the issue, not in the chat. Anything already on an issue survives any compaction, because the session can just read it again.

The gap is everything that hasn't reached an issue yet:

A good summary keeps most of these. But "most" is the problem: the one that gets dropped is the one nobody notices until it's re-asked or quietly forgotten. With defaults, nothing in the setup is looking for them specifically.

The handover: two hooks

Claude Code has a PreCompact hook, which runs just before a compaction. It gets the session ID and the path to the transcript, but it can't add anything to the context itself. So the handover is two hooks, both pointing at one small Python script:

PreCompact             handover.py pre
  read the transcript since the last compaction
  ask a model for a handover note
  save it as ~/.claude/handovers/<session-id>.md

SessionStart (compact) handover.py post
  print that session's note back into the context

Writing the note. The script reads the session's transcript, a JSONL file Claude Code keeps on disk, starting at the last compaction boundary. It sends that to claude -p on Sonnet with a fixed brief: what's in progress and the exact next step, open threads, what's waiting on me, what's waiting on other sessions, what was decided (quoting me briefly), and gotchas. It must say only what the transcript supports, and must never copy a secret value, only where the secret lives.

Reading it back. A SessionStart hook with the matcher compact fires right after the compaction and injects the note with the summary. The session reads its own handover in its own words.

A few choices that matter:

The first day

The hooks went live on 16 October, and by that evening four sessions had a handover note. Two came out as intended: short and structured, with the next step first.

The other two say "Summariser failed (You've hit your session limit)" and contain only the raw tail.

The summariser runs on the same Claude subscription as the sessions. Long sessions compact when they've done a lot of work, and that's exactly when the subscription is most likely to have hit its limit. The fallback did its job, and the session got the end of the conversation back. But a raw tail has no "waiting on Marlon" section, and anything older than the tail is gone, which is the very thing the hook exists to keep.

Lesson: a safety net that shares a quota with the thing it protects fails at the same moment. Give the fallback a different budget: another backend, or a local extraction that needs no model at all.

The fix is filed. Either retry on a different backend when the limit message appears, or add a cheap local pass to the fallback that pulls out issue references, "Decided:" lines and unanswered questions.

If you only change a few settings

One last fragility. The script finds the last compaction by a marker in the transcript file, and that file's format is internal and undocumented. If it changes, the script doesn't break. It just reads the newest part of the whole file instead, which is a reasonable thing to fail towards.

← All lab notesQuestions? Say hi →
Say hi hello@
letsautom8.dev

About a post, an experiment or the lab. Projects, testing and automation work go through my company, so you get proper terms and invoices.

Shiloah Software Solutions →