The status bar (bottom-left) and analytics dashboard (main area) show different numbers for sessions, messages, and projects. There are three separate causes.
- Status bar:
stats.session_count— trigger-maintained counter onINSERT/DELETEon thesessionstable. Counts all sessions including empty ones (message_count = 0). - Dashboard:
SELECT ... FROM sessions WHERE message_count > 0 AND <date/project/agent filters>. Excludes empty sessions and defaults to a 1-year date range (analytics.svelte.tssetsfrom = daysAgo(365)). - Sidebar: Like dashboard but with sidebar-specific filters and no default date range.
Dashboard <= Status bar because the dashboard excludes empty sessions and applies a 1y date filter.
- Status bar:
stats.message_count— trigger-maintained counter onINSERT/DELETEon themessagestable. Counts actual rows stored in the DB. - Dashboard:
SUM(sessions.message_count)over filtered sessions. Thesessions.message_countcolumn is set by the parser tolen(messages)— the total count of parsed messages before filtering.
The key: toDBMessages() in sync/engine.go calls pairAndFilter() which removes user messages that carry only tool_result blocks (no displayable text). So fewer rows are actually inserted into the messages table than the parser reports. The session's message_count column stores the parser's pre-filter count, but the stats trigger only counts the post-filter rows.
This is why Dashboard (8.9K) > Status bar (4,305) — the dashboard sums the unfiltered parser counts, while the status bar counts actual stored rows.
- Status bar:
COUNT(DISTINCT project) FROM sessions— all projects with any session (including empty ones). - Dashboard:
COUNT(DISTINCT project)from the filtered session set (after applyingmessage_count > 0, date range, etc.).
Dashboard <= Status bar because the dashboard applies filters.
| Metric | Status bar | Dashboard | Why they differ |
|---|---|---|---|
| Sessions | All sessions in DB | Non-empty, within 1y, matching filters | Date filter + message_count > 0 |
| Messages | Actual rows in messages table |
Sum of sessions.message_count (parser count, pre-filter) |
pairAndFilter removes tool-result-only messages before inserting rows, but sessions.message_count stores the unfiltered count |
| Projects | All distinct projects | Distinct projects from filtered sessions | Date filter + message_count > 0 |
The messages discrepancy is arguably a bug: sessions.message_count should match the number of rows actually stored, not the parser's pre-filter count.