Skip to content

feature: local-usage-stats (4/4) - #1134

Open
myk1yt wants to merge 116 commits into
Zoo-Code-Org:mainfrom
myk1yt:pr/b16-stats-ui-v2
Open

feature: local-usage-stats (4/4)#1134
myk1yt wants to merge 116 commits into
Zoo-Code-Org:mainfrom
myk1yt:pr/b16-stats-ui-v2

Conversation

@myk1yt

@myk1yt myk1yt commented Aug 4, 2026

Copy link
Copy Markdown

Stack Position

Description

https://www.youtube.com/shorts/UHnnOCM1_f0

Full Feature Description

  • Feature Branch: feature/local-usage-stats
  • Feature Name: Local Usage Statistics
  • Purpose: Resolves the problem where users cannot locally view token usage, cache effects, cost, and period-based trends by provider, and where differing usage formats across providers make consistent aggregation difficult. Provides a privacy-preserving dashboard that collects only numeric usage and non-secret identifiers locally, without collecting prompts, responses, or credentials.
  • Full Change Description: B13 adds data-minimized event/query contracts and an append-only NDJSON event store. B14 adds aggregation by date, provider, model, and mode, cache ratio, and provider-aware cost recalculation. B15 records final usage exactly once from the API attempt completion path, including success/error/cancel/retry. B16 adds transactional SQLite projection, idempotent migration, local-day rollup, query/stream IPC, stale epoch prevention, and dashboard summary/session/heatmap UI.
  • Impact Scope: Affects usage-stats.ts, src/services/stats, the provider/task capture paths Task.ts, the stats IPC usageStatsMessageHandler.ts, and the UI DashboardView.tsx and useDashboardStatsStream.ts.
  • Errors and Edge Cases: Raw events are append-only and derived rollups must be reconstructable. Duplicate idempotency keys are not re-recorded. Corrupt tails preserve the valid prefix and leave only a hash in the quarantine report instead of the original text. Migrations must be transactional/idempotent. Local day and DST boundaries are calculated per-timestamp by offset. Previous subscription epochs must not overwrite new range results. The store must not contain prompts, responses, API keys, endpoint credentials, or workspace paths.
  • Testing Method: Run contract/store, aggregation/cost, exactly-once capture, database/migration/projection/stream, IPC, dashboard reducer/component, performance, locale, and visual tests step by step. Manually create complete/cancel/retry attempts, verify event counts, then rapidly switch ranges in two dashboard windows and add events, verifying convergence without stale loading or duplicate totals. Inspect stored files to confirm no sensitive fields are present.

Why Split Into 17 PRs

Instead of submitting this feature as a single unified PR, it was split into individual PRs because as code size grows, safely reviewing a PR becomes very difficult. The feature was broken into mutually exclusive individual PRs so that each can be reviewed independently.

What This PR Specifically Changes

Adds SQLite projection, transactional/idempotent migration, local-day rollup, rebuild/query/stream IPC, epoch guard, dashboard summary/session/heatmap/loading/retry UI, and visual coverage. Removes other feature files.

Included Files

  • src/services/stats/UsageStatsDatabase.ts
  • src/services/stats/UsageStatsMigration.ts
  • src/services/stats/UsageStatsProjection.ts
  • src/services/stats/UsageStatsStreamCoordinator.ts
  • src/core/webview/usageStatsMessageHandler.ts
  • webview-ui/src/components/dashboard/DashboardView.tsx
  • webview-ui/src/components/dashboard/useDashboardStatsStream.ts
  • dashboard/stats component, locale, backend/UI/contract test
  • webview-ui/src/components/dashboard/DashboardView.visual.tsx

Exclusion Scope

  • All task-organization files
  • Unrelated provider implementation/fixtures
  • Session report and repair script
  • Duplicate B13-B15 implementations
  • All items in the common removal rules

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Too many files!

This PR contains 186 files, which is 36 over the limit of 150.

To get a review, reduce the PR to 150 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2bb75c64-5537-4d7c-848d-ab6ae7e8f0da

📥 Commits

Reviewing files that changed from the base of the PR and between 5afa9b3 and 857eea8.

⛔ Files ignored due to path filters (3)
  • webview-ui/src/components/dashboard/__tests__/__screenshots__/stats-daily-chart.png is excluded by !**/*.png
  • webview-ui/src/components/dashboard/__tests__/__screenshots__/stats-provider-breakdown.png is excluded by !**/*.png
  • webview-ui/src/components/dashboard/__tests__/__screenshots__/stats-summary-overview.png is excluded by !**/*.png
📒 Files selected for processing (186)
  • apps/vscode-e2e/src/suite/usage-stats-ui.test.ts
  • packages/types/src/__tests__/dashboard-stats-stream.spec.ts
  • packages/types/src/__tests__/usage-stats.spec.ts
  • packages/types/src/index.ts
  • packages/types/src/providers/mimo.ts
  • packages/types/src/providers/qwen-code.ts
  • packages/types/src/task-organization.ts
  • packages/types/src/usage-stats.ts
  • packages/types/src/vscode-extension-host.ts
  • packages/types/src/vscode.ts
  • progress.txt
  • src/__mocks__/vscode.js
  • src/__tests__/task-run-dispatch.spec.ts
  • src/activate/registerCommands.ts
  • src/api/providers/__tests__/anthropic-vertex.spec.ts
  • src/api/providers/__tests__/kenari.spec.ts
  • src/api/providers/__tests__/mimo.spec.ts
  • src/api/providers/__tests__/mistral.spec.ts
  • src/api/providers/__tests__/moonshot.spec.ts
  • src/api/providers/__tests__/openai-usage-tracking.spec.ts
  • src/api/providers/__tests__/openai.spec.ts
  • src/api/providers/anthropic-vertex.ts
  • src/api/providers/kenari.ts
  • src/api/providers/mimo.ts
  • src/api/providers/mistral.ts
  • src/api/providers/moonshot.ts
  • src/api/providers/openai-codex.ts
  • src/api/providers/openai.ts
  • src/api/transform/__tests__/vscode-lm-format.spec.ts
  • src/core/task-persistence/TaskHistoryStore.ts
  • src/core/task-persistence/TaskOrganizationStore.ts
  • src/core/task-persistence/__tests__/TaskHistoryStore.spec.ts
  • src/core/task-persistence/__tests__/TaskOrganizationStore.spec.ts
  • src/core/task-persistence/index.ts
  • src/core/task/Task.ts
  • src/core/task/__tests__/Task.dispose.test.ts
  • src/core/task/__tests__/Task.persistence.spec.ts
  • src/core/task/__tests__/Task.spec.ts
  • src/core/task/__tests__/Task.usage-stats.spec.ts
  • src/core/webview/ClineProvider.ts
  • src/core/webview/__tests__/ClineProvider.apiHandlerRebuild.spec.ts
  • src/core/webview/__tests__/ClineProvider.flicker-free-cancel.spec.ts
  • src/core/webview/__tests__/ClineProvider.lockApiConfig.spec.ts
  • src/core/webview/__tests__/ClineProvider.spec.ts
  • src/core/webview/__tests__/ClineProvider.sticky-mode.spec.ts
  • src/core/webview/__tests__/ClineProvider.sticky-profile.spec.ts
  • src/core/webview/__tests__/ClineProvider.taskHistory.spec.ts
  • src/core/webview/__tests__/usageStatsMessageHandler.spec.ts
  • src/core/webview/__tests__/usageStatsMessageRouting.spec.ts
  • src/core/webview/taskOrganizationMessageHandler.ts
  • src/core/webview/usageStatsMessageHandler.ts
  • src/core/webview/webviewMessageHandler.ts
  • src/eslint-suppressions.json
  • src/package.json
  • src/package.nls.ca.json
  • src/package.nls.de.json
  • src/package.nls.es.json
  • src/package.nls.fr.json
  • src/package.nls.hi.json
  • src/package.nls.id.json
  • src/package.nls.it.json
  • src/package.nls.ja.json
  • src/package.nls.json
  • src/package.nls.ko.json
  • src/package.nls.nl.json
  • src/package.nls.pl.json
  • src/package.nls.pt-BR.json
  • src/package.nls.ru.json
  • src/package.nls.tr.json
  • src/package.nls.vi.json
  • src/package.nls.zh-CN.json
  • src/package.nls.zh-TW.json
  • src/services/stats/DashboardTaskCatalog.ts
  • src/services/stats/DashboardTaskProjection.ts
  • src/services/stats/UsageAggregator.ts
  • src/services/stats/UsageEventStore.ts
  • src/services/stats/UsageRecorder.ts
  • src/services/stats/UsageStatsDatabase.ts
  • src/services/stats/UsageStatsMigration.ts
  • src/services/stats/UsageStatsProjection.ts
  • src/services/stats/UsageStatsService.ts
  • src/services/stats/UsageStatsStreamCoordinator.ts
  • src/services/stats/__tests__/DashboardTaskCatalog.spec.ts
  • src/services/stats/__tests__/DashboardTaskProjection.spec.ts
  • src/services/stats/__tests__/UsageAggregator.spec.ts
  • src/services/stats/__tests__/UsageEventStore.spec.ts
  • src/services/stats/__tests__/UsageRecorder.spec.ts
  • src/services/stats/__tests__/UsageStatsDatabase.spec.ts
  • src/services/stats/__tests__/UsageStatsMigration.spec.ts
  • src/services/stats/__tests__/UsageStatsProjection.spec.ts
  • src/services/stats/__tests__/UsageStatsService.spec.ts
  • src/services/stats/__tests__/UsageStatsStreamCoordinator.spec.ts
  • src/services/stats/__tests__/costRecalculation.spec.ts
  • src/services/stats/__tests__/dashboard-frontend-query-bug.spec.ts
  • src/services/stats/__tests__/dashboard-preset-change-bug.spec.ts
  • src/services/stats/__tests__/dashboard-sink-identity-bug.spec.ts
  • src/services/stats/__tests__/dashboard-timezone-preset-bug.spec.ts
  • src/services/stats/__tests__/dashboardStatsPerformance.spec.ts
  • src/services/stats/__tests__/statsQueryRange.spec.ts
  • src/services/stats/costRecalculation.ts
  • src/services/stats/index.ts
  • src/services/stats/statsQueryRange.ts
  • src/shared/globalFileNames.ts
  • src/utils/__tests__/safeWriteJson.spec.ts
  • src/utils/safeWriteJson.ts
  • src/vitest.config.ts
  • webview-ui/playwright-ct.config.ts
  • webview-ui/src/App.tsx
  • webview-ui/src/components/ErrorBoundary.tsx
  • webview-ui/src/components/dashboard/AnimatedNumber.tsx
  • webview-ui/src/components/dashboard/DashboardSummary.tsx
  • webview-ui/src/components/dashboard/DashboardView.tsx
  • webview-ui/src/components/dashboard/SessionDetail.tsx
  • webview-ui/src/components/dashboard/TaskList.tsx
  • webview-ui/src/components/dashboard/__tests__/AnimatedNumber.spec.tsx
  • webview-ui/src/components/dashboard/__tests__/DashboardSummary.spec.tsx
  • webview-ui/src/components/dashboard/__tests__/DashboardView.spec.tsx
  • webview-ui/src/components/dashboard/__tests__/SessionDetail.spec.tsx
  • webview-ui/src/components/dashboard/__tests__/StatsPanel.visual.fixture.tsx
  • webview-ui/src/components/dashboard/__tests__/StatsPanel.visual.tsx
  • webview-ui/src/components/dashboard/__tests__/TaskList.spec.tsx
  • webview-ui/src/components/dashboard/__tests__/TaskList.visual.fixture.tsx
  • webview-ui/src/components/dashboard/__tests__/TaskList.visual.tsx
  • webview-ui/src/components/dashboard/__tests__/dashboardStreamReducer.spec.ts
  • webview-ui/src/components/dashboard/__tests__/useDashboardStatsStream.spec.tsx
  • webview-ui/src/components/dashboard/dashboardStreamReducer.ts
  • webview-ui/src/components/dashboard/useAnimatedCounter.ts
  • webview-ui/src/components/dashboard/useDashboardStatsStream.ts
  • webview-ui/src/components/stats/UsageHeatmap.tsx
  • webview-ui/src/components/stats/__tests__/UsageHeatmap.spec.tsx
  • webview-ui/src/i18n/locales/ca/common.json
  • webview-ui/src/i18n/locales/ca/dashboard.json
  • webview-ui/src/i18n/locales/ca/stats.json
  • webview-ui/src/i18n/locales/de/common.json
  • webview-ui/src/i18n/locales/de/dashboard.json
  • webview-ui/src/i18n/locales/de/stats.json
  • webview-ui/src/i18n/locales/en/common.json
  • webview-ui/src/i18n/locales/en/dashboard.json
  • webview-ui/src/i18n/locales/en/stats.json
  • webview-ui/src/i18n/locales/es/common.json
  • webview-ui/src/i18n/locales/es/dashboard.json
  • webview-ui/src/i18n/locales/es/stats.json
  • webview-ui/src/i18n/locales/fr/common.json
  • webview-ui/src/i18n/locales/fr/dashboard.json
  • webview-ui/src/i18n/locales/fr/stats.json
  • webview-ui/src/i18n/locales/hi/common.json
  • webview-ui/src/i18n/locales/hi/dashboard.json
  • webview-ui/src/i18n/locales/hi/stats.json
  • webview-ui/src/i18n/locales/id/common.json
  • webview-ui/src/i18n/locales/id/dashboard.json
  • webview-ui/src/i18n/locales/id/stats.json
  • webview-ui/src/i18n/locales/it/common.json
  • webview-ui/src/i18n/locales/it/dashboard.json
  • webview-ui/src/i18n/locales/it/stats.json
  • webview-ui/src/i18n/locales/ja/common.json
  • webview-ui/src/i18n/locales/ja/dashboard.json
  • webview-ui/src/i18n/locales/ja/stats.json
  • webview-ui/src/i18n/locales/ko/common.json
  • webview-ui/src/i18n/locales/ko/dashboard.json
  • webview-ui/src/i18n/locales/ko/stats.json
  • webview-ui/src/i18n/locales/nl/common.json
  • webview-ui/src/i18n/locales/nl/dashboard.json
  • webview-ui/src/i18n/locales/nl/stats.json
  • webview-ui/src/i18n/locales/pl/common.json
  • webview-ui/src/i18n/locales/pl/dashboard.json
  • webview-ui/src/i18n/locales/pl/stats.json
  • webview-ui/src/i18n/locales/pt-BR/common.json
  • webview-ui/src/i18n/locales/pt-BR/dashboard.json
  • webview-ui/src/i18n/locales/pt-BR/stats.json
  • webview-ui/src/i18n/locales/ru/common.json
  • webview-ui/src/i18n/locales/ru/dashboard.json
  • webview-ui/src/i18n/locales/ru/stats.json
  • webview-ui/src/i18n/locales/tr/common.json
  • webview-ui/src/i18n/locales/tr/dashboard.json
  • webview-ui/src/i18n/locales/tr/stats.json
  • webview-ui/src/i18n/locales/vi/common.json
  • webview-ui/src/i18n/locales/vi/dashboard.json
  • webview-ui/src/i18n/locales/vi/stats.json
  • webview-ui/src/i18n/locales/zh-CN/common.json
  • webview-ui/src/i18n/locales/zh-CN/dashboard.json
  • webview-ui/src/i18n/locales/zh-CN/stats.json
  • webview-ui/src/i18n/locales/zh-TW/common.json
  • webview-ui/src/i18n/locales/zh-TW/dashboard.json
  • webview-ui/src/i18n/locales/zh-TW/stats.json
  • webview-ui/src/utils/__tests__/formatNumber.spec.ts
  • webview-ui/src/utils/formatNumber.ts

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the awaiting-review PR changes are ready and waiting for maintainer re-review label Aug 4, 2026
@myk1yt
myk1yt force-pushed the pr/b16-stats-ui-v2 branch 2 times, most recently from f398395 to 2fe7eb2 Compare August 4, 2026 20:41
@github-actions github-actions Bot added has-conflicts PR has merge conflicts with the base branch awaiting-review PR changes are ready and waiting for maintainer re-review and removed awaiting-review PR changes are ready and waiting for maintainer re-review has-conflicts PR has merge conflicts with the base branch labels Aug 4, 2026
@myk1yt
myk1yt force-pushed the pr/b16-stats-ui-v2 branch 2 times, most recently from 307a751 to 6ba1f75 Compare August 5, 2026 09:24
@github-actions github-actions Bot removed the awaiting-review PR changes are ready and waiting for maintainer re-review label Aug 5, 2026
@myk1yt
myk1yt force-pushed the pr/b16-stats-ui-v2 branch 2 times, most recently from 66c6878 to 79d9135 Compare August 6, 2026 07:28
@github-actions github-actions Bot added has-conflicts PR has merge conflicts with the base branch awaiting-review PR changes are ready and waiting for maintainer re-review and removed has-conflicts PR has merge conflicts with the base branch awaiting-review PR changes are ready and waiting for maintainer re-review labels Aug 6, 2026
k1yt and others added 6 commits August 7, 2026 03:28
…cit-any

Add new test file to eslint-suppressions.json with count of 26
no-explicit-any suppressions. These are standard test patterns
(mock objects, private property access via 'as any') consistent
with other test files in the suppressions list.

Fixes CI lint failure in PR #25 compile (lint) job.
@github-actions github-actions Bot added awaiting-review PR changes are ready and waiting for maintainer re-review and removed has-conflicts PR has merge conflicts with the base branch labels Aug 6, 2026
@github-actions github-actions Bot removed the awaiting-review PR changes are ready and waiting for maintainer re-review label Aug 7, 2026
Zoo (VP) added 3 commits August 7, 2026 16:21
… coverage

- Add coverage tests for UsageStatsService, UsageStatsStreamCoordinator, UsageStatsMigration, UsageStatsProjection, DashboardTaskProjection, UsageAggregator, costRecalculation, and UsageRecorder.

- Add new safeWriteJson spec to cover rollback and failure paths.

- Add dashboard webview tests for TaskList, AnimatedNumber, SessionDetail, dashboardStreamReducer, and useDashboardStatsStream edge cases.

- Fix vscode mock so RelativePattern is constructible for file watcher tests.
@github-actions github-actions Bot added the awaiting-review PR changes are ready and waiting for maintainer re-review label Aug 7, 2026
@myk1yt myk1yt closed this Aug 7, 2026
@myk1yt
myk1yt deleted the pr/b16-stats-ui-v2 branch August 7, 2026 13:06
@myk1yt
myk1yt restored the pr/b16-stats-ui-v2 branch August 7, 2026 13:32
@myk1yt myk1yt reopened this Aug 7, 2026
@github-actions github-actions Bot removed the awaiting-review PR changes are ready and waiting for maintainer re-review label Aug 8, 2026
Zoo (VP) added 2 commits August 8, 2026 11:55
- playwright-ct.config.ts: alias '@/i18n/TranslationContext' to the CT
  mock. DashboardSummary and UsageHeatmap import useAppTranslation via
  '@/i18n/...' which bypassed the existing '@src/i18n/...' alias,
  pulling the real TranslationContext -> ExtensionStateContext ->
  @roo-code/types barrel (zod) into the CT bundle and throwing
  'ReferenceError: z is not defined' at mount. Fixes all 3 webview
  visual snapshot failures (bundle error, not pixel mismatch).
- useDashboardStatsStream.spec.tsx: add coverage for the 10s loading
  timeout ERROR dispatch and the requestTaskPage no-cursor guard to
  close the patch-coverage shortfall.
@github-actions github-actions Bot added the awaiting-review PR changes are ready and waiting for maintainer re-review label Aug 8, 2026
Zoo (VP) added 2 commits August 8, 2026 13:03
…compatibility

Root cause: DashboardSummary and UsageHeatmap components use StandardTooltip
which requires a Radix TooltipProvider in the component tree. Playwright CT
mounts components without the app's provider hierarchy, causing silent render
failures (elements not found).

Additionally, the ProviderBreakdownFixture was defined inline in the test file,
which Playwright CT cannot mount (requires test story or external fixture).

Fix: Extract all fixtures into StatsPanel.visual.fixture.tsx with proper
TranslationContext.Provider and TooltipProvider wrappers.

Run: https://github.com/Zoo-Code-Org/Zoo-Code/actions/runs/31237627413
…ov coverage gap

- Move data-testid in ProviderBreakdownFixture from outer div to table
  (Playwright CT cannot find data-testid on root-level mount element)
- Add CI-generated baseline screenshots for StatsPanel visual tests
- Add 5 coverage tests for handleRebuildUsageStats and
  handleGetDashboardTaskDetail error branches (79.86% -> 80%+)
@github-actions github-actions Bot added has-conflicts PR has merge conflicts with the base branch and removed awaiting-review PR changes are ready and waiting for maintainer re-review labels Aug 8, 2026
@github-actions github-actions Bot added awaiting-review PR changes are ready and waiting for maintainer re-review and removed has-conflicts PR has merge conflicts with the base branch awaiting-review PR changes are ready and waiting for maintainer re-review labels Aug 8, 2026
- mimo.spec.ts: replace manual 'any[]' stream-collection loop with the
  typed collectStream() helper. The merge in b4c997b introduced an extra
  'any[]' that pushed the file to 19 no-explicit-any violations, exceeding
  the eslint-suppressions budget of 18 and failing the compile job.
- UsageStatsMigration.spec.ts: reduce checkpoint-resume test from 2500 to
  1200 events (still crosses the 1000-event batch boundary). The 2500-event
  run exceeded the 60s test timeout on windows-latest under parallel load;
  the per-event db.prepare() hot path makes SQLite writes the dominant cost.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants