[BUG] Forking from unprimed post-compact context re-bills the full shared prefix twice (fork AND parent each pay full cache_creation)
What's Wrong?
A fork's value proposition is inheriting the parent's warm prompt cache. But when /fork is the first action after /compact + a model switch — i.e. before any API call has primed the post-compact context into the cache — the shared prefix gets billed at full cache_creation price twice:
- The fork's first API call writes the entire shared prefix as new cache (
cache_creation_input_tokens: 52798,cache_read_input_tokens: 8859— only the small system-prefix portion hits). - Two minutes later, the parent's own next turn (after the fork returns) does not reuse the cache the fork just wrote:
cache_creation_input_tokens: 53647,cache_read_input_tokens: 8859(1h ephemeral). The parent rebuilds essentially the same ~53k prefix from scratch.
The fork's cache write of the shared conversation prefix is evidently not placed (or not breakpointed) so the parent can read it back, even though both contexts share the identical post-compact history and the parent's turn ran only ~2 minutes after the fork's first call — well within both 5m and 1h cache TTLs, on the same model.
Sequence that produced it:
- Long session,
/compactruns. - Model switched (opus → other model) immediately after the compact, before any message is sent — so the post-compact context exists only client-side; no API call has primed it into any cache for the new model.
/fork <prompt>issued as the first message.- Fork runs to completion (16 API calls; its own subsequent calls cache normally —
cache_readgrows monotonically from 61657 after the first call). - Parent's next turn after the fork returns pays full
cache_creationfor the same prefix again.
Net effect: ~53k tokens of identical context billed as cache_creation twice within ~2 minutes, instead of once (or, ideally, zero extra times had the parent primed its prefix before the fork inherited it).
What Should Happen?
Either (or both) of:
- Prime before fork: when
/forkis issued on context that has never been sent to the API (post-compact, post-model-switch), prime the parent's prefix first so the fork genuinely inherits a warm cache — that inheritance is the stated point of forking. - Reusable fork write: place cache breakpoints such that the fork's first call's write of the shared conversation prefix is readable by the parent's next turn. The shared history is byte-identical up to the fork point; only the tail (fork directive vs. fork-return notification) diverges, so a breakpoint at the shared-prefix boundary would let the parent read ~53k instead of rewriting it.
Expected usage shape for the parent's post-fork turn: cache_read ≈ 53k, cache_creation ≈ small delta — not the inverse.
Error Messages/Logs
# Parent transcript (jq over assistant turns; deduped; timestamps UTC)
# Last parent turn before the event (model A = opus-tier):
2026-08-27T05:09:22 model=claude-opus-5 cache_read=61656 cache_creation=4051
# (user runs /compact, switches model, then /fork as the first message at 05:31:54)
# Fork's own transcript — first API call (model B, unprimed context):
2026-08-27T05:32:00 model=claude-fable-5 cache_read=8859 cache_creation=52798
# Fork's second call — its own write is read back fine *within* the fork:
2026-08-27T05:32:04 model=claude-fable-5 cache_read=61657 cache_creation=804
# ... fork continues caching normally through 05:33:56 (16 calls total)
# Parent's first turn after the fork returns — full rebuild of the same prefix:
2026-08-27T05:34:03 model=claude-fable-5 cache_read=8859 cache_creation=53647
# (usage detail shows cache_creation: { ephemeral_1h_input_tokens: 53647, ephemeral_5m_input_tokens: 0 })
Note: 8859 is this session's stable system-prefix hit baseline (appears identically on both the fork's and the parent's cold calls); 52798 vs 53647 differ only by the divergent tails (fork directive vs. /fork command record + task-return notification).
Steps to Reproduce
- Run a session long enough to have meaningful context, then
/compact. - Immediately switch the model (before sending any message), so the post-compact context is unprimed for the new model.
- Issue
/fork <some task>as the very first message after the compact/switch. - Let the fork complete, then send any message in the parent (or let a task notification trigger a parent turn).
- Inspect
~/.claude/projects/<project>/<session>.jsonland<session>/subagents/agent-<fork-id>.jsonl: comparemessage.usage.cache_creation_input_tokens/cache_read_input_tokenson (a) the fork's first call and (b) the parent's first post-fork turn. Both show full-sizecache_creationfor the same prefix; the parent'scache_readstays at the system-prefix baseline.
Claude Model
Other — parent ran claude-opus-5 pre-fork; fork and post-fork parent turns ran claude-fable-5 (model switched between compact and fork, which is what left the context unprimed).
Is this a regression?
I don't know
Claude Code Version
2.1.247 (Claude Code)
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
Other (tmux)
Additional Information
- Both the fork's first-call write and the parent's post-fork write are for the same post-compact conversation history; the two contexts diverge only after the fork point. With ~2 minutes between them, same model, 1h-ephemeral cache in play, the parent reading the fork's write back should be structurally possible if a cache breakpoint sits at (or before) the shared-prefix boundary.
- The fork itself is otherwise healthy: from its second call on,
cache_readgrows monotonically (61657 → 84494 over 15 calls) — the problem is confined to the fork/parent boundary, in both directions (fork inherits nothing; parent reuses nothing). - Fork transcripts live at
<project>/<session-id>/subagents/agent-<fork-task-id>.jsonl, and the same entries appear inline in the parent.jsonlasisSidechain: true, which is how the two usage sequences above were extracted.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗