request_components.saved_usd — and every figure derived from it — is computed by pricing our own message-text token count at the provider's rate, rather than the tokens the provider actually billed. On a measured live turn it under-reports by 12.4%.
This is the same root cause as two defects fixed in #234, at a third site. It predates that PR and affects every saved_usd consumer, so it gets its own issue.
The measured pair
From the acceptance run on #234 (full data) — a real Claude Code session on claude-haiku-4-5, where a cold turn arrived after the transcript had been summarized:
|
|
| The full prefix that turn would have re-created |
149,363 tokens |
| The compacted prefix it did re-create |
32,123 tokens |
| Tokens the compaction removed, as the provider billed them |
117,240 |
| At haiku's 5-minute cache-write rate (1.25 × $1/MTok) |
$0.14655 |
What saved_usd reported for the same removal |
$0.12834875 |
| Implied token count behind that figure |
102,679 |
| Under-report |
12.42% |
The savings pipeline counted 102,679 tokens where the provider billed 117,240 for the same removed content — a ratio of 0.876.
Why the gap exists
Event.baselineDeltaUSD prices SavedUnique and Saved, which come from schema.MessagesTokens: this repo's own tokenizer, over message text only. The provider bills the whole serialized prompt. dash/overview.go's EstimatorDivergence already measures the general form of this — a median 3.38x across all traffic — but that figure is dominated by small transcripts where the fixed overhead (system prompt, tool declarations, JSON envelope) is most of the request. On a large transcript, which is where compaction happens, the divergence narrows to roughly 1.1-1.5x, and 12.4% is what it comes to on this turn.
Why it is worth filing rather than leaving
The direction is the interesting part. It under-reports, so:
It is the third site of one root cause. #234 fixed the same class of error twice:
Trigger.Fires compared frac × window against MessagesTokens — a gate that could never fire, because a window is stated in billed tokens.
- The compaction-episode span was sized in one quantity and evaluated against another, twice over.
- This:
saved_usd prices our token count at the provider's rate.
The first two were unreachable-gate and empty-measurement bugs. This one is a quiet 12% understatement, which is why it survived both.
Scope
Everything that reads saved_usd or baseline_cost_usd: the Components tab's per-component economics, NetSavedUSD/TotalSavedUSD, the savings waterfall, the compaction-episode panel's credit buckets, and /stats. So this is not a small change, and a fix wants:
- a decision on what to price — the provider's billed delta is available per request (
fresh_input + cache_read + cache_write before and after is not, but the written and read columns are), so this may be answerable from stored data rather than by improving the tokenizer;
- the before/after figures published together, because the headline will move up;
- a test pinning the relationship on a known pair, so the next tokenizer change does not move it silently.
Not on #234's branch
#234's own two instances of this root cause are fixed there. This third one predates it and affects consumers that PR does not touch, so per the repo rule it gets its own issue and branch off main.
Found by the reviewing session on #234, which re-derived the headline from the raw token counts rather than reading the reported figure — which is how the 12.4% gap surfaced at all.
request_components.saved_usd— and every figure derived from it — is computed by pricing our own message-text token count at the provider's rate, rather than the tokens the provider actually billed. On a measured live turn it under-reports by 12.4%.This is the same root cause as two defects fixed in #234, at a third site. It predates that PR and affects every
saved_usdconsumer, so it gets its own issue.The measured pair
From the acceptance run on #234 (full data) — a real Claude Code session on
claude-haiku-4-5, where a cold turn arrived after the transcript had been summarized:saved_usdreported for the same removalThe savings pipeline counted 102,679 tokens where the provider billed 117,240 for the same removed content — a ratio of 0.876.
Why the gap exists
Event.baselineDeltaUSDpricesSavedUniqueandSaved, which come fromschema.MessagesTokens: this repo's own tokenizer, over message text only. The provider bills the whole serialized prompt.dash/overview.go'sEstimatorDivergencealready measures the general form of this — a median 3.38x across all traffic — but that figure is dominated by small transcripts where the fixed overhead (system prompt, tool declarations, JSON envelope) is most of the request. On a large transcript, which is where compaction happens, the divergence narrows to roughly 1.1-1.5x, and 12.4% is what it comes to on this turn.Why it is worth filing rather than leaving
The direction is the interesting part. It under-reports, so:
It is the third site of one root cause. #234 fixed the same class of error twice:
Trigger.Firescomparedfrac × windowagainstMessagesTokens— a gate that could never fire, because a window is stated in billed tokens.saved_usdprices our token count at the provider's rate.The first two were unreachable-gate and empty-measurement bugs. This one is a quiet 12% understatement, which is why it survived both.
Scope
Everything that reads
saved_usdorbaseline_cost_usd: the Components tab's per-component economics,NetSavedUSD/TotalSavedUSD, the savings waterfall, the compaction-episode panel's credit buckets, and/stats. So this is not a small change, and a fix wants:fresh_input + cache_read + cache_writebefore and after is not, but the written and read columns are), so this may be answerable from stored data rather than by improving the tokenizer;Not on #234's branch
#234's own two instances of this root cause are fixed there. This third one predates it and affects consumers that PR does not touch, so per the repo rule it gets its own issue and branch off
main.Found by the reviewing session on #234, which re-derived the headline from the raw token counts rather than reading the reported figure — which is how the 12.4% gap surfaced at all.