Anthropic prompt cache still head-only on v18.2.5 despite ab88e931 (follow-up to #12318)
Follow-up to #12318: still reproducing on v18.2.5 (fix ab88e931 included)
ab88e9313fad ("skip undecoratable tail messages when placing Anthropic cache breakpoints",
PR #12332, merged 2026-09-17T12:44Z) is present in v18.2.5 (published 2026-09-17T23:07Z).
The frozen head-only cache signature survives it on my machine.
Environment
omp/18.2.5, macOS 25.6.0 (Darwin 25.6.0), arm64, Apple M4 Max- Anthropic OAuth (Max),
anthropic/claude-opus-5:highasdefaultrole,defaultThinkingLevel: high providers.cacheRetention: auto, noPI_CACHE_RETENTIONin env- 4 MCP servers configured (mongodb-atlas, tia, unity-mcp, tableplus);
memory.backend: hindsight;autolearn.enabled: true; projectAGENTS.mdcontext file present - Binary installed 2026-09-18T00:32:33Z; the session below was launched 10s later (2026-09-18T00:32:43Z), so it is unambiguously the 18.2.5 build
Per-request trace, v18.2.5, single session, consecutive requests
From ~/.omp/agent/sessions/<project>/2026-09-18T00-32-43-641Z_*.jsonl
(message.usage, provider anthropic, model claude-opus-5):
t input cacheRead cacheWrite output
00:34:18 44954 27040 0 1057
00:34:32 56054 27040 0 802
00:34:38 65575 27040 0 331
00:34:45 69855 27040 0 237
00:34:50 70349 27040 0 414
00:35:02 71747 27040 0 892
00:35:19 73686 27040 0 1177
00:35:32 75105 27040 0 955
00:35:56 85709 27040 0 1574
00:36:11 88555 27040 0 994
00:36:28 101415 27040 0 1090
00:36:54 102807 27040 0 1765
00:37:07 107968 27040 0 851
00:37:33 110777 27040 0 1599
00:37:54 116589 27040 0 1274
00:38:11 118751 27040 0 563
00:38:44 120605 27040 0 2177
00:38:54 123878 27040 0 627
00:39:04 124615 27040 0 357
00:39:08 127329 27040 0 194
00:39:21 133230 27040 0 895
00:39:34 134581 27040 0 725
00:39:47 137586 27040 0 1015cache_read pinned at exactly 27,040 (the tools+system head anchor), cache_write 0 on
every request including the first, input monotonically tracking the transcript.
Session total at 26 requests: 2,707,688 uncached input tokens for 28,039 output tokens
(97 input : 1 output).
Note the difference from the original report: there is no baseline write at all here. The head anchor was already warm from a previous session sharing the same prefix, so this session never wrote a breakpoint — it only ever read the frozen 27,040 and paid full price for the tail.
Same machine, versioned boundary
The break is at process launch time, not at workload change. Aggregated from session JSONL,
provider anthropic only, uncached input per request:
day (UTC) reqs input/req cacheWrite/req
2026-09-11 479 2 15264
2026-09-12 1171 2 22478
2026-09-13 781 2 31325
2026-09-14 1344 2 20459
2026-09-15 937 2 17496
2026-09-16 <15h 200 2 ~13000
2026-09-16 >=15h 455 51k-325k ~0
2026-09-17 158 67k-231k ~0
2026-09-18 116 129994 0First broken request is 2026-09-16T15:24:35Z, in the session whose omp process started at
that exact second. Processes launched before that timestamp kept the healthy signature
(input == 2) for as long as they stayed alive; every process launched after it is broken,
across 18.2.x up to and including 18.2.5.
Cost shape
Worst case is resuming a long session: the whole tail is re-billed every turn, so spend grows quadratically in turns. In one 54-minute window (2026-09-17T23:49Z - 2026-09-18T00:43Z, 191 requests, Anthropic only): 21,599,592 uncached input tokens for 87,780 output tokens (246:1). Weighting input 1x / cache read 0.1x / cache write 1.25x, that is ~23.1M equivalents against ~4.4M for the same traffic with a rolling anchor: 5.2x excess. Two Max accounts went to 100% of the 7-day meter.
Controlled A/B against the official endpoint: v18.2.0 vs v18.2.5
Both arms ran within the same minute, same cwd (so identical system/rules/AGENTS.md/skills/MCP
head), same anthropic/claude-opus-5 at --thinking low, same OAuth credentials, same
https://api.anthropic.com, same prompt (four sequential echo bash calls, --tools=bash,
--auto-approve, isolated --session-dir). Only the binary differed.
arm A: omp-18.2.0 arm B: omp-18.2.5
# input cacheRead cacheWrite # input cacheRead cacheWrite
1 4 0 57109 1 32308 0 26992
2 2 57109 99 2 32405 26992 0
3 2 57208 97 3 32502 26992 0
4 2 57305 97 4 32599 26992 0
5 2 57402 97 5 32696 26992 0
total uncached input: 12 total uncached input: 16251013,542x more uncached input from the client version alone. Two details name the mechanism:
- The baseline write differs: 18.2.0 writes 57,109 (head + messages), 18.2.5 writes 26,992 (head only — the same ~27k prefix frozen in the field trace above). 18.2.5 never anchors the message tail on the first request either, so this is not "the anchor stopped advancing", it is "the anchor was never placed on messages at all".
cacheReadadvances in 18.2.0 (57,109 -> 57,402, +97/+99 per turn, matching each new tool-result turn) and is pinned in 18.2.5, withinputrising by the same ~97 per turn instead.
This rules out provider-side and accounting-side explanations: identical endpoint, identical account, identical payload shape, opposite behaviour.
Reproduction
Two binaries, one command each, in any project directory with a non-trivial context head:
omp-18.2.0 -p --auto-approve --no-title --tools=bash \
--model anthropic/claude-opus-5 --thinking low --session-dir=/tmp/ab/v0 \
"Run exactly four bash commands, one per turn, in separate sequential calls: echo T1, then echo T2, then echo T3, then echo T4. Do not read or modify any file. Answer only DONE."
omp-18.2.5 -p --auto-approve --no-title --tools=bash \
--model anthropic/claude-opus-5 --thinking low --session-dir=/tmp/ab/v5 \
"<same prompt>"Then read message.usage from each session JSONL. Healthy is input == 2 with cacheWrite > 0
per request and a climbing cacheRead; broken is input tracking the transcript with
cacheWrite == 0 and cacheRead pinned.
After rolling the installed binary back to v18.2.0, the same check on the installed path gives 10 uncached input tokens across 4 requests:
# input cacheRead cacheWrite
1 4 26988 30072
2 2 57060 99
3 2 57159 97
4 2 57256 97Request 1 is informative: it reads the 26,988 head left warm by v18.2.5 and then writes 30,072 on top of it, i.e. v18.2.0 anchors the messages that v18.2.5 never anchored.
One operational note for anyone bisecting this: the effective version is bound at process
exec. Long-lived omp processes started before the upgrade keep the healthy behaviour for new
sessions, and starting a new session inside an already-running broken process does not help.
That is why the boundary in the table above lands on a process start timestamp rather than on a
release timestamp.
Remaining leads
Consistent with lead 1 from the original issue (countHeadBreakpoints exhausting the budget
before the tail): this configuration has an unusually large stable head (SYSTEM + RULES +
project AGENTS.md + hindsight mental-model block + 4 MCP servers' tool inventory). If the
head consumes all 4 breakpoints, applyPromptCaching returns at messageBudget <= 0 and the
tail is never anchored, which reproduces exactly the trace above — including the absence of a
baseline write, since the head anchors were already warm.
Happy to run an instrumented build; the machine reproduces this on every session.
Source: can1357/oh-my-pi