Skip to content

feat(telemetry): workspace count and UI analytics#697

Merged
skevetter merged 7 commits into
mainfrom
flimsy-giraffe
Jul 21, 2026
Merged

feat(telemetry): workspace count and UI analytics#697
skevetter merged 7 commits into
mainfrom
flimsy-giraffe

Conversation

@skevetter

@skevetter skevetter commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Improves observability telemetry in two areas the current setup under-serves: tracking how many workspaces a user manages, and making the web UI (Electron desktop) analytics actually useful.

  • Per-user workspace count — a workspace_count gauge is emitted from the CLI on create and delete, keyed on the existing pseudonymous machine-id distinct ID (the same identity the desktop shares with spawned CLIs). The CLI is the single source of truth; because desktop create/delete shell out to the CLI, those are covered too. Only create and delete change the count, so both count-changing paths are instrumented.
  • Workspace event correlation, privacy-safe — desktop lifecycle events now carry a hashed workspace_ref (HMAC of the workspace id, non-reversible) so a workspace's events can be correlated without leaking raw names. workspace_rename no longer sends the raw workspace id.
  • Web UI analytics — renderer session tracking (session_start/session_end with idle-aware duration) and engagement events (ide_open, terminal_open, context_switch, settings_changed, key only), plus human-readable screen names on page_view.
  • Vendor-opaque logging — analytics log/user-facing strings no longer name the provider; only SDK imports/identifiers retain it.

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Added session and engagement analytics for key user actions (page views, context switches, settings changes, terminal launches, and IDE access).
    • Introduced privacy-conscious workspace analytics identifiers and workspace count telemetry.
  • Bug Fixes

    • Improved port discovery by continuing the search when individual port checks encounter errors.
  • Improvements

    • Enhanced route-to-screen tracking consistency.
    • Improved analytics diagnostics and ensured safe, graceful behavior when analytics is unavailable.

…alytics

Add a workspace_count gauge emitted from the CLI on create/delete so the
number of workspaces a user manages is tracked, keyed on the existing
pseudonymous machine-id distinct ID. The CLI is the single source of truth;
desktop-initiated create/delete already shell out to it.

Attach a hashed workspace_ref to desktop lifecycle events for per-workspace
correlation without leaking raw names, and stop workspace_rename from sending
the raw workspace id.

Add renderer session tracking (session_start/session_end with idle-aware
duration) and engagement events (ide_open, terminal_open, context_switch,
settings_changed), plus human-readable screen names on page_view.

Make analytics log messages vendor-opaque (no provider name in user-facing
strings).
@netlify

netlify Bot commented Jul 20, 2026

Copy link
Copy Markdown

Deploy Preview for images-devsy-sh canceled.

Name Link
🔨 Latest commit b043b08
🔍 Latest deploy log https://app.netlify.com/projects/images-devsy-sh/deploys/6a5eb4879e64430008e6409b

@netlify

netlify Bot commented Jul 20, 2026

Copy link
Copy Markdown

Deploy Preview for devsydev canceled.

Name Link
🔨 Latest commit b043b08
🔍 Latest deploy log https://app.netlify.com/projects/devsydev/deploys/6a5eb487e612ea0007ab1450

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Workspace commands now emit local workspace-count telemetry. Desktop analytics adds hashed workspace references, session tracking, normalized screens, and engagement events. Port probing continues after availability errors, while desktop tests and provider markup receive formatting-only updates.

Changes

CLI workspace telemetry

Layer / File(s) Summary
Workspace gauge collection and command wiring
pkg/workspace/list.go, pkg/telemetry/*, cmd/workspace/delete.go, cmd/workspace/up/up.go
Workspace-count events, shared event construction, no-op support, counting, and command integrations are added.
Analytics property contracts and diagnostics
pkg/telemetry/analytics/*
Reserved telemetry keys are centralized, property merging excludes them, tests cover the behavior, and diagnostics use generic analytics terminology.

Desktop workspace analytics

Layer / File(s) Summary
Hashed workspace event identifiers
desktop/src/main/analytics.ts, desktop/src/main/ipc.ts
Workspace references use cached HMAC-based identifiers in workspace IPC events.
Session and engagement tracking
desktop/src/renderer/src/App.svelte, desktop/src/renderer/src/lib/analytics.ts, desktop/src/renderer/src/pages/*
Renderer lifecycle tracking records sessions, normalized page views, context changes, settings changes, terminal opens, and IDE opens.

Port search handling

Layer / File(s) Summary
Continue scanning after probe errors
pkg/port/port.go
Port discovery continues after availability probe errors and accepts only error-free available results.

Formatting and markup maintenance

Layer / File(s) Summary
CLI and updater test formatting
desktop/src/main/__tests__/*
Existing test fixtures, assertions, imports, and arrays are reformatted without changing behavior.
Provider component markup
desktop/src/renderer/src/lib/components/provider/ProviderCard.svelte
Prop formatting and update badge span syntax are adjusted.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant App
  participant SessionTracking
  participant Analytics
  App->>SessionTracking: initSessionTracking()
  App->>Analytics: track page_view with path and screen
  SessionTracking->>Analytics: record session_start or session_end
  App->>Analytics: trackEngagement(feature, properties)
  App->>SessionTracking: teardown on destroy
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main telemetry changes: workspace counting and UI analytics.

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.

FindAvailablePort bailed out whenever IsAvailable returned an error, so a
port already in use (net.Listen fails with address-in-use) aborted the whole
search instead of moving to the next candidate. Under CI port contention this
flaked TestParseAddressAndPort_Empty. Treat a probe error as 'not this port'
and keep scanning.
@github-actions github-actions Bot added size/xl and removed size/l labels Jul 20, 2026
@skevetter
skevetter marked this pull request as ready for review July 20, 2026 21:49

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/telemetry/collect.go (1)

188-208: 🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Fix property overwrite and serialization in analytics payload.

Because both the event and user payloads store their stringified properties under the exact same key ("properties"), the analytics client's buildProperties function will overwrite the event properties with the user properties. This results in the complete loss of all event-specific data (e.g., command, version, desktop, is_ci).

Furthermore, stringifying these maps prevents the analytics backend from natively parsing and querying them as individual columns, as PostHog expects a flat dictionary of properties.

Flatten the properties directly into the payload map to resolve both issues.

🐛 Proposed fix to flatten the property maps
 func (d *cliCollector) recordEvent(
 	eventType string,
 	eventProperties, userProperties map[string]any,
 ) {
 	machineID := GetMachineID()
-	eventPropertiesRaw, _ := json.Marshal(eventProperties)
-	userPropertiesRaw, _ := json.Marshal(userProperties)
+
+	eventPayload := map[string]any{
+		"type":       eventType,
+		"machine_id": machineID,
+		"timestamp":  time.Now().Unix(),
+	}
+	for k, v := range eventProperties {
+		eventPayload[k] = v
+	}
+
+	userPayload := map[string]any{
+		"machine_id": machineID,
+		"timestamp":  time.Now().Unix(),
+	}
+	for k, v := range userProperties {
+		userPayload[k] = v
+	}
+
 	d.analyticsClient.RecordEvent(analytics.Event{
-		"event": {
-			"type":       eventType,
-			"machine_id": machineID,
-			"properties": string(eventPropertiesRaw),
-			"timestamp":  time.Now().Unix(),
-		},
-		"user": {
-			"machine_id": machineID,
-			"properties": string(userPropertiesRaw),
-			"timestamp":  time.Now().Unix(),
-		},
+		"event": eventPayload,
+		"user":  userPayload,
 	})
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/telemetry/collect.go` around lines 188 - 208, Update
cliCollector.recordEvent to flatten eventProperties and userProperties directly
into their respective analytics payload maps instead of serializing either map
under the shared "properties" key. Preserve the existing event metadata and user
metadata, ensure event-specific fields remain in the event payload, and remove
the json.Marshal-based string conversion.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@pkg/telemetry/collect.go`:
- Around line 188-208: Update cliCollector.recordEvent to flatten
eventProperties and userProperties directly into their respective analytics
payload maps instead of serializing either map under the shared "properties"
key. Preserve the existing event metadata and user metadata, ensure
event-specific fields remain in the event payload, and remove the
json.Marshal-based string conversion.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: dd4974a7-2f45-46c6-8198-a1f067bd858d

📥 Commits

Reviewing files that changed from the base of the PR and between 5be7aaa and 3b14483.

📒 Files selected for processing (18)
  • cmd/workspace/delete.go
  • cmd/workspace/up/up.go
  • desktop/src/main/__tests__/cli.test.ts
  • desktop/src/main/__tests__/updater.test.ts
  • desktop/src/main/analytics.ts
  • desktop/src/main/ipc.ts
  • desktop/src/renderer/src/App.svelte
  • desktop/src/renderer/src/lib/analytics.ts
  • desktop/src/renderer/src/lib/components/provider/ProviderCard.svelte
  • desktop/src/renderer/src/pages/ContextsPage.svelte
  • desktop/src/renderer/src/pages/SettingsPage.svelte
  • desktop/src/renderer/src/pages/TerminalsPage.svelte
  • desktop/src/renderer/src/pages/WorkspaceDetailPage.svelte
  • pkg/port/port.go
  • pkg/telemetry/analytics/client.go
  • pkg/telemetry/collect.go
  • pkg/telemetry/noop.go
  • pkg/workspace/list.go

recordEvent stored each property map as a JSON string under the same
"properties" key in both the event and user payloads. buildProperties merges
those two maps into one flat property set, so the user "properties" string
overwrote the event "properties" string — dropping every event-specific field
(command, version, is_ci, count) and sending the rest as an opaque JSON blob
instead of queryable columns.

Flatten both property maps directly into their payloads and exclude reserved
routing keys (type, machine_id, timestamp) when building properties. Add
regression tests for the merge and reserved-key handling.
….Copy

Address goconst (repeated "type"/"machine_id"/"timestamp" literals) and
modernize (map copy loop) lint findings: add KeyType/KeyMachineID/KeyTimestamp
constants in the analytics package, use them across producer, consumer, and
tests, and copy property maps with maps.Copy.
@skevetter
skevetter marked this pull request as draft July 20, 2026 22:44
@skevetter skevetter changed the title feat(telemetry): track per-user workspace count and improve web UI analytics feat(telemetry): workspace count and UI analytics Jul 21, 2026
@skevetter
skevetter marked this pull request as ready for review July 21, 2026 02:09

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
pkg/telemetry/analytics/client_test.go (1)

24-35: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Assert the length of the resulting map to ensure no extra keys are present.

While the current loop verifies that all expected keys are present and have the correct values, it does not verify that the map only contains these expected keys. Asserting the length of the map adds an extra layer of robustness to the test.

♻️ Proposed refactor
 	want := map[string]any{
 		"count":   7,
 		"version": "1.2.3",
 		"os_name": "darwin",
 		"os_arch": "arm64",
 	}
+
+	if len(got) != len(want) {
+		t.Errorf("got %d properties, want %d", len(got), len(want))
+	}
+
 	for k, v := range want {
 		if got[k] != v {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/telemetry/analytics/client_test.go` around lines 24 - 35, Update the test
assertion around the want/got map comparison to first verify that len(got)
equals len(want), then retain the existing per-key value checks. This ensures
the result contains exactly the expected keys without changing the existing
value validation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@pkg/telemetry/analytics/client_test.go`:
- Around line 24-35: Update the test assertion around the want/got map
comparison to first verify that len(got) equals len(want), then retain the
existing per-key value checks. This ensures the result contains exactly the
expected keys without changing the existing value validation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c397dfa3-b182-4d02-9ea2-0a73b4ad0cd5

📥 Commits

Reviewing files that changed from the base of the PR and between 3b14483 and b043b08.

📒 Files selected for processing (10)
  • cmd/workspace/delete.go
  • desktop/src/main/analytics.ts
  • desktop/src/renderer/src/lib/analytics.ts
  • desktop/src/renderer/src/pages/SettingsPage.svelte
  • pkg/port/port.go
  • pkg/telemetry/analytics/client.go
  • pkg/telemetry/analytics/client_test.go
  • pkg/telemetry/analytics/types.go
  • pkg/telemetry/collect.go
  • pkg/workspace/list.go
💤 Files with no reviewable changes (5)
  • pkg/port/port.go
  • pkg/workspace/list.go
  • desktop/src/main/analytics.ts
  • cmd/workspace/delete.go
  • desktop/src/renderer/src/pages/SettingsPage.svelte
🚧 Files skipped from review as they are similar to previous changes (2)
  • desktop/src/renderer/src/lib/analytics.ts
  • pkg/telemetry/collect.go

@skevetter
skevetter merged commit dcd9c20 into main Jul 21, 2026
66 checks passed
@skevetter
skevetter deleted the flimsy-giraffe branch July 21, 2026 02:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant