A live room re-sends the same large prefix every turn — the megaprompt plus each host's profile, then the whole running transcript. Prompt caching pays for that prefix once and reads it cheap afterward. But the cache expires, and the only question that matters for a human-in-the-loop room is: does it survive the gap between turns? This is the study, the design we shipped, and the measured saving. (Companion to the Prompt caching explainer — that's the mechanic; this is the economics.)
Recorded 2026-06-04 · case: the “Early Adopter” room (megaprompt-humanloop, claude-sonnet-4-6, 10 turns)
Reads are identical across both TTLs. The cache's clock resets on every hit. So the lever is just the write multiplier — and whether the next turn lands before the clock runs out.
| Token class | × base input | Sonnet 4.6 $/Mtok | note |
|---|---|---|---|
| Base input (fresh) | 1.0× | $3.00 | uncached |
| Cache write · 5-min TTL | 1.25× | $3.75 | cheaper to write |
| Cache write · 1-hour TTL | 2.0× | $6.00 | 1.6× the 5-min write |
| Cache read (hit) | 0.1× | $0.30 | same for both TTLs |
| Output | — | $15.00 | unaffected |
A read is 12.5× cheaper than re-writing the same tokens under 5-min. So the whole game is converting expired re-writes into cheap reads. The cost of 1-hour: a write premium of 0.75 × prefix, paid only on a genuinely cold start.
A human reads six panel replies, thinks, and types a long paragraph. Those gaps mostly land between 5 minutes and 1 hour — the band where a 5-min cache is already dead but a 1-hour cache is still warm. In the Early Adopter room, 6 of 9 gaps fall in that band.
From the room's event-log.json. Under the live 5-min scheme, writes mean a full prefix re-write (cache_read 0); reads are warm hits. The 1-hour column is the same conversation re-priced: the flip tag marks a turn that was a full re-write under 5-min but becomes a cheap read under 1-hour.
| Turn | Gap before | 5-min cache | 1-hour cache |
|---|---|---|---|
| 1 · kickoff | — | write 26,686 | write 26,686 cold start |
| 2 | 3h 12m | write 56,569 | write 56,569 >1h — cold either way |
| 3 | 47m | write 58,643 | read 56,569 +w 2,074 flip |
| 4 | 4m 51s | read 58,643 | read 58,643 |
| 5 | 1m 33s | read 58,804 | read 58,804 |
| 6 | 6m 26s | write 59,550 | read 59,081 +w 469 flip |
| 7 | 8m 52s | write 60,193 | read 59,550 +w 643 flip |
| 8 | 11m 05s | write 60,996 | read 60,193 +w 803 flip |
| 9 | 11m 34s | write 61,986 | read 60,996 +w 990 flip |
| 10 | 8m 54s | write 62,729 | read 61,986 +w 743 flip |
| totals | write 447,790 · read 117,447 | write 89,415 · read 475,822 |
Only turns 4–5 (sub-5-min gaps) hit the cache under 5-min — every other turn re-wrote the whole ~60k prefix. The room is unusually TTL-sensitive because it re-sends the entire conversation as one cached prefix, so an expiry is a total re-write, not a partial miss.
Both bars are drawn on the same dollar scale (5-min total = full width), so the 1-hour bar is literally shorter by the amount saved.
| Component | 5-min | 1-hour | what moved |
|---|---|---|---|
| Cache writes | $1.6792 | $0.5365 | 447,790 → 89,415 tok |
| Cache reads | $0.0352 | $0.1427 | re-writes became reads |
| Input + output | $0.0694 | $0.0694 | unchanged |
| Total | $1.7838 | $0.7486 | −$1.04 · −58% |
The 5-min run's recorded cost was $1.783816 — the reconstruction matches to the cent, which is what validates the 1-hour projection. (The $0.75 is a projection until the room takes its first live turn under the new setting and the log records actual ephemeral_1h_input_tokens.)
This isn't universal — it's a property of the gap distribution. Match the TTL to how the room is actually used.
| Inter-turn cadence | Use | Why |
|---|---|---|
| reliably < 5 min | 5-min | free refresh on every hit; the 2× write premium buys nothing |
| mostly 5 min – 1 hour | 1-hour | the dead band — 5-min keeps expiring, 1-hour stays warm |
| reliably > 1 hour | either | both cold; 1-hour just overpays the write premium |
Both cache breakpoints in lib/run_room.py now run through one helper at a single CACHE_TTL constant. 1-hour TTL is generally available on Sonnet 4.6 — no beta header.
The persona prefix and the growing dialogue must both survive the pause — the dialogue is the bulk of the prefix by mid-conversation. Holding only the prefix would still re-write the transcript every slow turn.
A 5-min tail only helps once there's genuinely volatile per-turn content (RAG, fresh context). There is none today — marking the dialogue 5-min would re-expire it. The helper keeps the ordering valid for a future tail.
The legacy AI-AI batch coordinator (the archived run_dialogue.py) ran at 5-min by design: it drove turns machine-paced, back-to-back, so gaps stayed under 5 minutes and the cheaper write multiplier was correct there. Opposite regime.
The community asks this too — Anthropic's docs name this exact "user may not respond in 5 minutes" scenario, and a public Claude Code issue tracked ~17% cost waste when the default regressed 1h→5m. Anthropic exposes no auto-adaptive knob, so adaptivity is the app's job. Three options considered:
prefix × 0.1× (~$0.018 here), needs a real request, and races an unpredictable human. More expensive than just holding 1-hour and paying one read when the turn lands.0.75 × prefix write premium per cold start — refunded by a single rescued turn. Adaptivity earns cents; just default to 1-hour.[1h stable prefix][1h dialogue][5m volatile tail] — so per-turn RAG/context wouldn't drag the 2× premium onto volatile bytes. Wired the ordering for it; not needed yet.