CLAUDE.md@.claude/cold-cache · git:20261005.7ba2424 · 2026-10-05 · sha256 ee5e53bf149fc27d
CLAUDE.md@.claude/cold-cache git:20261005.7ba2424A
Immutable. This exact content never changes and is served at /api/v1/blob/ee5e53bf149fc27d.
# The cold-cache guard `hooks/cold_cache.py` stops the first prompt after the session's prompt cache expired and prices the ways on, because that prompt re-caches the whole conversation and nothing else shows the price before it is paid. A blocked prompt makes no API request, so the stop is free. It prices carrying on against a fresh session with `.claude/costs/lib/restart.py`, the context budget hook's model. - **Two sources of "cold".** `SessionStart` on a resume carries `prompt_cache_likely_expired`, `context_tokens` and `seconds_since_last_response`; the hook keeps them in `tmp/cold-cache/<session_id>.json` until a response lands after them. But the cache also expires while the process lives on and no `SessionStart` fires, so the transcript's last main-chain response time, against the TTL the session's own cache writes used, is the second source rather than a fallback. - **The expiry is partial.** The system prompt and tool prefix every session in the environment shares stays warm, so the first request after a gap reads it from cache. The transcript's first response gives its size (its cache read), and Claude Code's `estimated_cache_write_usd`, which assumes a full rewrite, is shown only when the model has no row in `prices.json`. - **The way on it prices is a fresh session, not `/relay`.** A relay's summary turn is a request at the whole context, so on a cold cache it pays the same re-cache carrying on does; the two cancel, and what is left is the context budget's question, which that hook asks once the session goes on. Only a fresh session skips the re-cache, so the guard prices it — the reorientation up front, the smaller context on every request after — and offers it only for work that is already all on the branch, a call the operator makes. The hook cannot see a decision made in chat after the last step that wrote one down — a plan, a PR body, a commit — so the offer names that condition too. `.claude/context-budget/CLAUDE.md` carries where a reorientation's price comes from; for a fresh successor the sources are the fresh sessions', not the relayed ones'. - **Once per cold spell.** A block writes the last response's id and the stopped prompt to `tmp/cold-cache/<session_id>.blocked`, and a prompt against the same id passes. A bare `!` then stands for the stopped prompt: a hook cannot rewrite the prompt it is given, so it passes `!` with context telling the model to act on the stored one verbatim. A new response and a new gap re-arm it. - **What always passes:** `/compact`, `/clear` and its aliases, `/relay`, and every built-in command that makes no model request — `PASSES`, taken from code.claude.com/docs/en/commands. Skills and the built-ins that prompt the model (`/go`, `/btw`, `/init`, `/plan`) are prompts like any other. Also passing: a session whose re-cache costs under `COLD_CACHE_MIN_USD` (default `0.30`), where the stop costs more attention than it saves; and one whose fresh session beats carrying on by less than `COLD_CACHE_MARGIN` (default `0.10`) both on its up-front cost against the re-cache and on the context it carries after, where the stop has no real choice to offer. An unpriced model is still stopped, with the times and token counts and no dollars. `COLD_CACHE_GUARD=off` in `.claude/settings.local.json`'s `env` disables both halves.