Skip to content

Cold-cache /compact re-caches the Claude history it is about to discard; T3 could open that one compaction with caching disabled #18067

Closed as not planned

Description

@Alb11747

Summary

When a large Claude thread is resumed after its prompt cache has expired, the Compact path (/compact before send, or "Resume with less context") can cache-write the whole old history while summarizing it. The next turn then caches the new, smaller context. The old history is about to be dropped, so in this case most of that write buys nothing: one measured cold compaction spent about half its cost writing history that was discarded.

T3 can address this on its own side. In the usual cold case it already starts a fresh Claude CLI process for that compaction turn, and it already passes an env per query. The proposal is narrow: for an explicitly cold compaction only, open that one compaction process with prompt caching disabled, then reopen normally for the next productive turn. Warm compaction and normal turns would keep caching.

This is a cost/performance report with a proposed direction. It's distinct from the warm-window timing work (#13988 → Ideas #18011, Ideas #14233, draft #16855) and from compact-on-send defaults (#17467, Ideas #17170). Those decide when compaction happens; this is about how much the cold compaction itself costs. If maintainers would rather track this as an Ideas discussion, that's fine.

Evidence

Measured cold compaction (Opus 5.5, Claude Code via T3, Oct 9, about 112 min idle, so past the 1h cache):

Request input cache read cache write (all 1h) output
Previous turn (warm) 2 447,034 608 –
/compact summarizer 2 11,573 364,934 4,314
Next productive turn 2 11,573 36,190 677
Following turn 2 47,763 2,068 547

The compaction boundary reported ~448k tokens before and ~10.9k after. The summarizer request was matched to the compaction boundary by timing (completion within 15 ms, duration ~42.6 s vs ~42.8 s), the same session, and the following turn's usage. No request ID is persisted in the transcript, so this is a strong timing/sequence match, not an explicit ID join.

At published API rates (Opus 5.5: input $4, 1h write $8, cache read $0.20, output $20 per MTok), that compaction costs about $3.01. With the same token counts:

  • Old history at ordinary input, keeping the shared 11.6k warm prefix: **$1.55** (saves ~$1.46, ~49%).
  • Fully uncached (what disabling caching for the whole request does): ~$1.59 (saves ~$1.42, ~47%).

These are arithmetic estimates, not a paired live experiment. Removing cache markers could change the request in other ways. The replacement-context write on the next turn (36k) is unaffected.

Runtime behavior (Claude Code 2.1.296, Agent SDK 0.3.276, read from the shipped minified runtime):

  • Default compaction runs a cache-sharing fork with skipCacheWrite: true. That only moves the tail cache breakpoint back one message. Earlier history keeps its ordinary ephemeral cache_control marker. It is not a read-only-cache mode, so with an expired cache the old prefix is written again.
    • In a synthetic warm Haiku 5.5 test, /compact still wrote ~2.2k tokens on top of ~26–27k cached reads. That's cheap when warm; the problem is cold history.
  • The compaction request doesn't set enablePromptCaching itself. The request builder falls back to the model-level check, which returns false when DISABLE_PROMPT_CACHING (or DISABLE_PROMPT_CACHING_<MODEL>) is set in the process env. So a CLI process started with that env should send the compaction with no cache markers. I found this by reading source; I haven't verified it with a live request.
  • CLAUDE_CODE_COLD_COMPACT exists, but CLAUDE_CODE_COLD_COMPACT is unreachable in the local CLI: the auto-window gate returns before it, and an explicit window routes through the reactive path, which never reads it (2.1.270–2.1.272) anthropics/claude-code#95694 reports it is unreachable on the tested local paths, so it isn't a dependable switch.

Why T3 can realistically fix this

Checked at 273835b:

  • ProviderSessionManager.ts: DEFAULT_IDLE_TIMEOUT_MS = 30 min. Idle Claude sessions are released well before the 70-min resume offer (CLAUDE_RESUME_COMPACTION_MINUTES in ContextWindowMeter.logic.ts). So a cold compaction normally already opens a new CLI process with resume.
  • ClaudeAdapterV2.ts: compactThread is startTurn with text /compact. makeClaudeQueryOptions already forwards env. The live process is reused only when queryPolicyKey/selectionKey match; otherwise it is replaced, and replacement is refused while background work runs.

A possible shape:

  1. When T3 starts a compaction it treats as cold (the existing resume-compaction offer / compact-before-send path), open that turn's query with DISABLE_PROMPT_CACHING=1 added to the env and a distinct query-policy key.
  2. The next productive turn uses the normal key, so the process is replaced with normal env and caching resumes. Idle release already requires a new process for the compaction; restoring normal caching afterward requires one additional process start before the productive turn.
  3. Warm compaction (manual Compact while the cache is live, auto-compact, feat(server): opt in to compaction before PR waits #16855-style pre-wait compaction) is unchanged.
  4. If the live process is pinned by background work, fall back to today's behavior rather than killing it.

This is not a request to disable caching globally. That would make warm compaction and every normal turn more expensive.

Tradeoff: forks and branches

Transcript-based forks keep working. T3's fork uses the standalone SDK forkSession(sessionId, { upToMessageId }), which copies the transcript. In a synthetic Haiku 5.5 test, forking an already compacted session at its last precompaction assistant message restored the full old history and all earlier thinking blocks (27,758 cached read, 0 write). The native streaming resumeSessionAt path behaves differently and is not what T3 uses. I tested the SDK call, not the UI fork button.

What is lost is only the warm-cache benefit for a branch made from the precompaction history within the cache TTL, on the same account. Per 100k history tokens on Opus 5.5 with a 1h TTL:

  • Skipping the write saves $0.40 now.
  • The first such branch would then pay a write ($0.80) instead of a read ($0.02).

Break-even is about a 51% chance of such a branch (about 21% with a 5m TTL). Branches made after expiry would not have benefited from the write anyway.

Not verified

Environment

Claude Code 2.1.295/2.1.296, @anthropic-ai/claude-agent-sdk 0.3.276, T3 main @ 273835b, Windows 11.

Related: #13988 / #18011, #14233, #16855, #17467, #17170, #16829 (PR watch waking cold threads), #17423 (revert across compaction, separate from fork), anthropics/claude-code#95694, #66115, #99624, #70459.

Activity

  1. juliusmarminge commented on Oct 11, 2026

    @juliusmarminge
    Member

    Note

    Grok responding on behalf of Julius.

    This is a cost-optimization proposal, not a bug. Converting to Ideas per CONTRIBUTING.md.

    On main, the plumbing you point at is real: makeClaudeQueryOptions forwards environment as env, idle release is 30 minutes (ProviderSessionManager), and compactThread is /compact via startTurn. DISABLE_PROMPT_CACHING exists in the shipped Claude Code runtime. What is not verified is that opening one cold compaction with that env actually cuts the cache-write cost (no paired live test; your numbers are arithmetic).

    Distinct from Ideas #18011 (when the cold resume offer fires). Continue the design discussion there or in the converted thread—especially the warm-cache fork tradeoff and the cold heuristic.

  2. Alb11747 commented on Oct 11, 2026

    @Alb11747
    ContributorAuthor

    Moved to an Ideas discussion, per CONTRIBUTING (proposals belong there): #18069. The discussion also corrects one detail here: restoring normal caching after a cold compaction costs one extra CLI process start.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementRequested improvement or new capability.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions