[QUESTION] Does stats-cache.json rebuild lose history for transcripts already cleaned up by cleanupPeriodDays or `project purge`?
Related to #59248.
Context
The docs (claude-directory#application-data) list ~/.claude/stats-cache.json (aggregated totals shown by /usage) under "Kept until you delete them" — explicitly exempt from both the cleanupPeriodDays retention sweep and claude project purge. This is good: it means the historical /usage totals are already decoupled from the raw session transcripts they were presumably built from.
What I found reading the client
Reading the strings in the installed claude CLI binary (v2.1.272), the stats aggregation code (functions building an object shaped like {lastComputedDate, dailyActivity, dailyModelTokens, totalSessions, totalMessages, ...}) has a fallback path: when the cache's version doesn't match, or lastComputedDate looks invalid, it logs "resetting aggregates for a full rescan" and appears to rebuild the aggregate by scanning ~/.claude/projects/**/*.jsonl from scratch.
The question
If that full-rescan path is ever triggered (cache corruption, a stats-cache schema version bump on upgrade, etc.), and the user has since had some transcripts removed — either by the default 30-day cleanupPeriodDays sweep, or by claude project purge — does the rebuilt cache still contain the historical totals for that now-gone period, or does that period silently disappear from /usage's lifetime totals?
The docs don't say either way, and neither project purge's dry-run/plan output nor /usage itself warns about this possibility.
If the answer is "it's lost," that's the same class of problem as #59248 (silent, unrecoverable, no warning) but specific to the aggregated stats rather than the transcripts themselves.
Ask
- Clarify whether the full-rescan path can actually drop already-known historical periods when their source transcripts no longer exist.
- If so, consider having the rescan preserve previously-cached daily/period entries instead of discarding them when the source transcript is gone — i.e. treat
stats-cache.jsonas the source of truth for periods it already covers, and only use transcripts to fill gaps it doesn't have yet. - Either way, documenting this explicitly in the "Kept until you delete them" section would help, since a user reading that section today would reasonably assume
/usagetotals are permanent regardless ofcleanupPeriodDaysorproject purge.
Environment
- Claude Code CLI 2.1.272 (also bundled in Claude Desktop)
- macOS
3 Comments
Follow-up from tracing a related desktop bug (#96940). Checked on Claude Desktop 2.9939.2 and CLI 2.1.281, macOS.
Only the CLI ever writes
stats-cache.json. The desktop app's stats worker reads the cache, recomputes the days afterlastComputedDatefrom transcripts on every view, and never persists the result (same finding as #87772). So the file is only as fresh as the last time the CLI Stats screen was opened.The full-rebuild path is still present in 2.1.281, and a missing file takes it too. If the cache cannot be loaded (
Failed to load stats cache: not found), has a version that is not migratable (returning empty cache), or has no usable date (resetting aggregates for a full rescan), the loader returns an empty cache. The next Stats run then logsStats cache empty, processing all historical dataand rebuilds from whatever.jsonltranscripts are still under~/.claude/projects, then saves that as the new cache.So the answer to the question above looks like yes: any day whose transcripts were already removed by
cleanupPeriodDaysorclaude project purgewould not come back after such a rebuild. One easy way to hit this is moving the file aside while troubleshooting the desktop usage widget, which is a natural thing to try for #96940, and then opening the CLI Stats screen before putting it back. Without a cache, the desktop widget itself only looks back about 182 days.Would it be possible for the loader to keep a backup of the previous cache (or refuse to overwrite it with a smaller history) before a full rebuild?
A concrete answer from my machine (CLI 2.1.284, macOS), at least for the per-model daily breakdown: yes, it is lost.
In my
~/.claude/stats-cache.json(version: 5,dailyModelTokensVersion: 5):dailyActivitygoes back to 2026-03-16 (151 days).dailyModelTokensstarts at 2026-08-11 (35 entries).cleanupPeriodDayssweep).That matches the code path that logs
Rebuilding stats dailyModelTokens through <date>whendailyModelTokensVersionis older than current: it rebuildsdailyModelTokensby scanning the transcripts that exist at that moment, so every earlier day's per-model tokens disappear, whiledailyActivityand the lifetimemodelUsagetotals are kept.I also see a
Stats cache version … not migratable … returning empty cachepath, which would presumably drop everything at once; I have not hit it.So your suggestion (2) — treat the cache as the source of truth for periods it already covers, and only use transcripts to fill gaps — would have prevented this. At minimum the "Kept until you delete them" docs should say that a schema bump can still shrink the history to the retention window.
A new, more direct way to hit this: since 2.1.284, deleting a session from the Desktop sidebar removes its transcript immediately. Because the stats cache only computes completed days, a session deleted on the same day is never counted at all. My heatmap now shows 2026-09-30 as empty after a full day of work. See #98642.