Resume-from-summary guard doesn't fire when the session prefix was invalidated by a CLI version change
Environment
- Claude Code 2.1.208 → 2.1.212 (native install, macOS arm64, Darwin 25.5)
- Subscription usage (prompt-cache-dependent workflows)
Summary
When resuming a large session that has gone stale server-side, the CLI already interrupts with a "resume from summary" option — it models resume cost and offers a cheaper path. But there is a second, equally expensive invalidation source the guard misses entirely: a CLI version update that changes the system-prompt/tool-schema prefix. Resume a large, recently-active session after such an update and the CLI silently re-writes the entire context to cache — a full cache_creation hit (~1.25× input cost, counted against usage allowance) with no warning, no option, no indication anything happened.
The asymmetry is the core of the report: a thread stale by time gets a guard; a thread invalidated minutes earlier by the CLI's own auto-update gets nothing — even though the cost is identical and the user has even less reason to anticipate it, since the update happened silently in the background.
Reproduction
- Run a session with substantial context (100K+ tokens) on version N. Close it.
- CLI auto-updates to version N+1 whose system prompt or tool schemas differ byte-wise.
- Within the cache TTL,
claude --resume <sid>(or--continue). - Observed: no prompt, no notice; the transcript's next entry shows
cache_creation_input_tokens≈ the full context,cache_read_input_tokens≈ 0. - Contrast: let the same session go stale by time instead — the resume-from-summary guard fires.
Impact (observed)
Any automation or habit that resumes warm sessions across updates pays repeatedly. On this machine, several 200K–1.8M-token sessions resumed across the 2.1.208→212 churn produced a measurable ~1% weekly-allowance loss in one day, discovered only by auditing cache_creation_input_tokens in the transcript JSONLs. Nothing in the product surfaced it.
Why detection is nearly free
Every transcript entry already records the version it was written under, and the CLI knows its own version. A version-crossing resume of a large session is a one-comparison, fully-local check — no server round-trip, no cache introspection. (To avoid false positives, gate on whether the prefix actually differs between the two versions; some patch releases don't change it.)
Proposed fix
Extend the existing stale-resume guard to fire on version-crossing resumes of large sessions, offering three options:
- Resume in current version — pay the one-time full cache write (today's silent behavior, made visible: "this will re-cache ~400K tokens").
- Resume from summary — the existing cheap, lossy path.
- Resume in the session's last-run version — cheap and lossless: identical prefix, warm cache. The version-named binaries this needs are already kept on disk by the native installer (
~/.local/share/claude/versions/<ver>), so the CLI could re-exec the old binary with the same args. If in-process re-exec is undesirable, the degraded form is printing a copy-paste command:
~/.local/share/claude/versions/2.1.210 --resume <sid>
Side benefit on macOS: the old binary already holds its TCC file-access grants, so pinned resumes also avoid the per-version "\"2.1.211\" would like to access files in your Documents folder" re-prompts that headless/automated resumes currently trigger on every update.
Caveats worth designing around: offer it only when the old binary is still installed and the prefix actually differs; warn that the pinned session runs without the newest fixes; scope the pin to the one session rather than making it sticky.
Option 3 is running as a proof-of-concept on this machine today (a local watchdog that resumes sessions with the transcript-recorded version binary): zero cache churn across updates, no TCC re-prompts, sessions upgrade — and pay the write once — only when deliberately resumed unpinned.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗