[BUG] Desktop "Models" token chart mixes two token definitions: cached days include cache read/write, live-scanned days are input+output only (≈1000× cliff at `lastComputedDate`)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet (related but distinct: #87772 stale cache, #91775 per-row double counting, #85509 total-tokens window)
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code
What's Wrong?
The desktop app's Code-tab start screen → Models chart (All / 30d / 7d) stitches together two data sources that use different definitions of "tokens":
| Days | Source | What each daily bar sums |
|---|---|---|
| ≤ lastComputedDate | ~/.claude/stats-cache.json dailyModelTokens (written only by CLI /stats) | input + output + cache_read + cache_creation |
| > lastComputedDate | Desktop's own live transcript scan | input + output only |
Cache reads are ~95–99% of token volume for agentic sessions, so the chart shows a cliff at the cache boundary. On my machine lastComputedDate was 2026-09-09: every bar up to Sep 9 is 0.3–1.9 billion, every bar after it is single-digit millions, although actual activity was similar on both sides. It looks like days with ~2B tokens followed by weeks of near-zero usage.
The legend (X in · Y out) adds to the confusion: it shows only input/output, yet the bars above it are ~99% cache tokens on the cached days.
Where it happens (minified; symbol names vary per build)
CLI 2.1.267: the stats scanner writing dailyModelTokens sums all four fields:
let $t=Ee(St.input_tokens)??0, ct=Ee(St.output_tokens)??0,
ot=Ee(St.cache_read_input_tokens)??0, qt=Ee(St.cache_creation_input_tokens)??0;
...
let Wn=$t+ct+ot+qt; if(Wn>0){ ao[Be]=(ao[Be]||0)+Wn; B.set(Xe,ao) }
Desktop 2.9939.4 (app.asar): the live scan for days after lastComputedDate sums only two:
let i=t.inputTokens+t.outputTokens;
if(i>0){ let t=l.get(n)??Object.create(null); t[e]=(t[e]??0)+i; l.set(n,t) }
The desktop then merges them: for(let e of r?.dailyModelTokens??[]) e.date<i&&l.set(e.date,{...e.tokensByModel}) (cached days) plus the live-scanned days above.
Measured (same machine, same transcripts)
Recomputing from ~/.claude/projects/**/*.jsonl the way the CLI does reproduces the cache exactly on several days (e.g. 2026-08-25: 326M vs 326M; 2026-09-08: 1,396M vs 1,396M). The same days under the desktop's definition (input+output) are 2–6M. After the cache boundary the desktop's per-row input+output is 0–11M/day, while the CLI definition gives 0.6–2.7B/day for the same days.
A secondary issue is day bucketing. The CLI uses UTC (toISOString().split("T")[0]), while the desktop uses local date (getFullYear()/getMonth()/getDate()). The two halves of the chart can therefore disagree near midnight.
What Should Happen?
One definition for every bar, and a legend that matches it. Either:
- desktop live scan sums all four fields (matching the cache and the CLI Stats screen), with the legend also showing cache read/write, or
- both paths chart input+output (or "uncached") and show cache tokens separately.
Ideally the cache also carries its definition (dailyModelTokensVersion already exists), and the desktop rejects or recomputes days whose definition doesn't match.
Steps to Reproduce
- Use the desktop app mostly (so CLI
/statshasn't been run recently andlastComputedDateis days/weeks old). - Open the Code tab start screen → Models → All.
- Days ≤
lastComputedDateare 100–1000× taller than later days with comparable activity. - Confirm:
jq '.lastComputedDate' ~/.claude/stats-cache.jsonmarks the cliff. Per the code above, moving the file aside forces the desktop's 182-day live scan, so every bar uses the input+output definition. Running CLI/statsrewrites the cache and brings back billion-scale bars up to the newlastComputedDate.
Related (not duplicates)
- #87772: only the CLI writes the cache, so
lastComputedDategoes stale for desktop-first users. That is why this boundary drifts weeks into the past. - #91775: usage summed per transcript row instead of per
message.id. The desktop live scanner has the same problem (nomessage.id/requestIddedup). On my transcripts, 129,149 of 233,562 usage rows are duplicates, inflating output ~3.3× and cache read ~2×.
Environment
- Claude desktop app 2.9939.4 (macOS 15.7.3)
- Claude Code CLI 2.1.267
stats-cache.jsonversion 5,dailyModelTokensVersion5
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗