Stats dashboard counts multi-day sessions only on their start date (Active days / streaks / peak hour undercounted)
Summary
The desktop client's Stats dashboard (Overview tab 鈥?Active days, Current/Longest streak, Peak hour, and the activity heatmap) buckets each entire session under the date/hour of its first message only. Any session that spans multiple calendar days (a long-running or resumed/forked conversation) is counted as activity on its start day alone; every subsequent day silently disappears from these metrics.
Environment
- Claude desktop (Windows, MSIX):
Claude_1.11187.1.0_x64 - Bundled Claude Code CLI:
2.1.161 - OS: Windows 11, local timezone UTC+8
Symptoms (real example)
On a machine with 7 sessions / 5,570 messages whose transcript activity actually spans 8 consecutive local days (2026-05-30 鈫?2026-06-06):
| Metric | Shown | Correct |
|---|---|---|
| Active days | 2 | 8 |
| Longest streak | 1d | 8d |
| Current streak | 0d | 8d |
| Peak hour | hour sessions were started | should reflect when messages actually occurred |
| Heatmap | only 2 cells lit | 8 cells |
Cross-check: Total tokens, Messages, Sessions, and Favorite model are correct, because those are accumulated per-message. Only the day/hour-bucketed metrics are wrong.
Root cause
In the stats aggregation routine, each transcript file's day/hour bucket is derived from the first message's timestamp, and the whole session is attributed to that single bucket:
// per transcript file:
const v = new Date(messages[0].timestamp); // only the FIRST message
const day = formatLocalDate(v); // one bucket for the ENTIRE session
dayEntry.sessionCount++;
dayEntry.messageCount += messages.length; // all messages -> start day
hourCounts[v.getHours()]++; // peak hour uses the START hour
// later:
activeDays = new Set(dailyActivity.map(d => d.date)).size; // = number of distinct *start* days
streaks = computeStreaks(thatSameSet);
Because a resumed/forked session copies the original history, multiple multi-day branches share the same first-message timestamp, so several of them collapse onto a single start day, compounding the undercount.
Expected behavior
Day/hour bucketing should be derived from each message's own timestamp, not the session's first message 鈥?i.e. iterate the messages and increment the bucket for formatLocalDate(message.timestamp) (and message.timestamp.getHours() for peak hour). Then activeDays, the streaks, the heatmap, and peak hour will reflect actual per-day / per-hour activity.
Note: stats-cache.json would need to be invalidated on upgrade, since the cached dailyActivity is already mis-bucketed and the incremental updater would otherwise keep the stale per-day rows.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗