Skip to content

Reduce PostHog event volume and fix telemetry consent/privacy gaps #830

Description

@edelauna

Context

PostHog telemetry has been exceeding the free-tier monthly quota (Product analytics 1M, Session replay 5K, Error tracking 100K — all exceeded in the June 6–July 6 cycle, driven by a ~2.7M-event spike on June 7–9). Once a free-tier limit is exceeded, PostHog silently drops ingestion for the rest of the billing cycle, which is why PostHog showed near-zero events for weeks despite Marketplace installs (45K+) continuing to grow — this was initially mistaken for a broken telemetry pipeline before the billing page confirmed the real cause.

Separately, a telemetry best-practice gap analysis (see TELEMETRY_GAP_ANALYSIS.md in the repo root) found consent/privacy issues that are largely inherited unchanged from upstream Roo Code. Both problems live in the same subsystem (packages/telemetry, Task.ts, code-index services), so this issue bundles the fixes together.

Breakdown of the June 7–9 spike (~2.7M events):

Event Count People Per-person Verdict
Conversation Message 1,318,290 3,949 334 Real signal, tracked at message-granularity — expensive
Tool Used 804,551 3,761 214 Real signal, same issue
Code Index Error 254,786 309 825 Bug — looks like an unbounded file-watcher retry loop against a broken embedder/vector-store config
Checkpoint Created 153,787 2,910 53 Real usage
Model Cache Empty Response 50,095 3,023 17 Possibly tracking an expected/benign condition every time

Developer Notes

Part A — Event volume (get back under free tier)

A1. Circuit-breaker for CODE_INDEX_ERROR retry storm (highest impact)
309 people generated 254,786 events (9.4% of the entire monthly analytics quota) — consistent with a small number of installs stuck in a permanently-broken embedder/vector-store config, where file-watcher.ts re-triggers indexing on every file-system event with no backoff. Add a per-workspace circuit breaker: stop retrying/firing telemetry after N consecutive failures until the embedder config changes.

  • Files: src/services/code-index/processors/file-watcher.ts, src/services/code-index/manager.ts

A2. Aggregate Conversation Message into Task Completed
Currently fires once per message turn (Task.ts:2606, Task.ts:3523 via TelemetryService.captureConversationMessage). Replace with a messageCount property summarized once on Task Completed instead of one event per turn.

  • Files: src/core/task/Task.ts, packages/telemetry/src/TelemetryService.ts

A3. Aggregate Tool Used into Task Completed
Same treatment — 2 call sites, summarize as a per-task tool-usage breakdown (e.g. toolsUsed: { read: 12, edit: 8 }) instead of one event per tool call.

  • Files: src/core/task/Task.ts, packages/telemetry/src/TelemetryService.ts

A4. Review Model Cache Empty Response noise
Check whether this represents an expected/benign condition being tracked unconditionally; throttle or remove if so.

Part B — Privacy / consent gaps (from TELEMETRY_GAP_ANALYSIS.md)

B1. Fix consent banner dark pattern (Critical)
webview-ui/src/components/common/TelemetryBanner.tsx sends telemetrySetting: "enabled" when the user dismisses the banner with "×" — there's no neutral dismiss or explicit decline. Make "×" a neutral dismiss that leaves the setting as "unset" (routed to disabled), and add explicit Accept/Decline actions.

B2. Salt-hash the persistent machine ID (Critical)
Both PostHog clients (packages/telemetry/src/PostHogTelemetryClient.ts, webview-ui/src/utils/TelemetryClient.ts) use raw, unhashed vscode.env.machineId as the distinct ID. Hash it (e.g. SHA-256 + fixed salt) before use, or generate a separate installation UUID.

B3. Clarify PRIVACY.md dual-pipeline scope (High)
The "Telemetry" bullet says "does not collect your code or AI prompts" without noting this only applies to the PostHog path — the separate Cloud sync pipeline (packages/cloud/src/TelemetryClient.ts) does transmit task messages when cloud sync is enabled. Add one clarifying sentence.

B4. Adopt vscode.env.isTelemetryEnabled (Medium)
Code only checks the deprecated telemetry.telemetryLevel config setting. Add an onDidChangeTelemetryEnabled listener alongside it so the extension reacts live to VS Code's global telemetry toggle.

B5. Await telemetry shutdown properly (Medium)
extension.ts calls TelemetryService.instance.shutdown() in deactivate() without awaiting it, and TelemetryService.shutdown() itself uses forEach instead of Promise.all + await — risking dropped events (including the most diagnostically useful ones) on process exit.

Estimated scope

Roughly 300–500 changed lines across ~10–12 files, no new dependencies, no data/schema migrations, no breaking API changes. B1/B3/B5 are trivial (a few lines each); B2/B4 are small, mechanical changes; A1/A2/A3 need a bit of design judgment (retry threshold, aggregation shape) but are localized.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions