From 1c895c7db6c681afcee1c82ce40a8bc26984deb8 Mon Sep 17 00:00:00 2001 From: Arjun <6592213+arkml@users.noreply.github.com> Date: Wed, 15 Jul 2026 07:50:39 +0530 Subject: [PATCH] feat(x): stamp platform=desktop on all analytics events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Registered as a posthog-js super property in the renderer and merged into every posthog-node capture/identify in main, plus set as a person property. Segments desktop traffic from the legacy web dashboard's anonymous autocapture if it ever shares the PostHog project. Desktop app only — the web app is legacy and intentionally untouched. Co-Authored-By: Claude Fable 5 --- apps/x/ANALYTICS.md | 3 ++- apps/x/apps/renderer/src/lib/analytics.ts | 12 +++++------- apps/x/packages/core/src/analytics/posthog.ts | 12 +++++++----- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/apps/x/ANALYTICS.md b/apps/x/ANALYTICS.md index 7cc9054f..e933ab9a 100644 --- a/apps/x/ANALYTICS.md +++ b/apps/x/ANALYTICS.md @@ -16,7 +16,7 @@ ## Event catalog -All PostHog events include `app_version` automatically. Main-process events add it in `packages/core/src/analytics/posthog.ts`; renderer events get it from the `analytics:bootstrap` IPC payload and an initialization-time `before_send` hook. +All PostHog events include `app_version` and `platform: 'desktop'` automatically. Main-process events add them in `packages/core/src/analytics/posthog.ts`; renderer events get them from the `analytics:bootstrap` IPC payload via `posthog.register` (plus an initialization-time `before_send` hook for `app_version`). `platform` guards against the legacy web dashboard's autocapture (`apps/rowboat`, unidentified by design) muddying desktop dashboards if it ever shares the project. ### `llm_usage` @@ -192,6 +192,7 @@ Persistent across sessions for the same user. Set via `posthog.people.set` or as | `email` | main on identify | From `/v1/me`; powers PostHog cohort match + integrations | | `plan`, `status` | main on identify | Subscription state | | `api_url` | both processes (init + identify) | Distinguishes prod / staging / custom — assign meaning in PostHog dashboard. `https://api.x.rowboatlabs.com` = production | +| `platform` | both processes (init + identify) | Always `desktop` from this app; segments desktop users from any other surface | | `app_version` | both processes (init + identify) | Electron app version; also included automatically on every event | | `signed_in` | renderer | `true` while rowboat OAuth is connected | | `{provider}_connected` | renderer | One of `gmail`, `calendar`, `slack`, `rowboat` | diff --git a/apps/x/apps/renderer/src/lib/analytics.ts b/apps/x/apps/renderer/src/lib/analytics.ts index 612442cb..5acf3d84 100644 --- a/apps/x/apps/renderer/src/lib/analytics.ts +++ b/apps/x/apps/renderer/src/lib/analytics.ts @@ -11,18 +11,16 @@ export function configureAnalyticsContext(props: { appVersion?: string; apiUrl?: appVersion = props.appVersion?.trim() || undefined apiUrl = props.apiUrl?.trim() || undefined - const eventProperties = appVersionProperties() - if (Object.keys(eventProperties).length > 0) { - posthog.register(eventProperties) - } + // `platform` distinguishes desktop events from any other surface (e.g. the + // web dashboard's autocapture) sharing the PostHog project. + const eventProperties = { platform: 'desktop', ...appVersionProperties() } + posthog.register(eventProperties) const personProperties = { ...(apiUrl ? { api_url: apiUrl } : {}), ...eventProperties, } - if (Object.keys(personProperties).length > 0) { - posthog.people.set(personProperties) - } + posthog.people.set(personProperties) } export function identifyUser(userId: string, properties?: Record) { diff --git a/apps/x/packages/core/src/analytics/posthog.ts b/apps/x/packages/core/src/analytics/posthog.ts index d3d1e55c..326049d6 100644 --- a/apps/x/packages/core/src/analytics/posthog.ts +++ b/apps/x/packages/core/src/analytics/posthog.ts @@ -30,7 +30,7 @@ function getClient(): PostHog | null { // distinguishes prod / staging / custom — meaning is assigned in PostHog). client.identify({ distinctId: getInstallationId(), - properties: { api_url: API_URL, ...appVersionProperties() }, + properties: { api_url: API_URL, ...baseProperties() }, }); } catch (err) { console.error('[Analytics] Failed to init PostHog:', err); @@ -43,8 +43,10 @@ function activeDistinctId(): string { return identifiedUserId ?? getInstallationId(); } -function appVersionProperties(): Record { - return APP_VERSION ? { app_version: APP_VERSION } : {}; +// Stamped on every event so desktop traffic is distinguishable from any other +// surface (e.g. the web dashboard's autocapture) sharing the PostHog project. +function baseProperties(): Record { + return { platform: 'desktop', ...(APP_VERSION ? { app_version: APP_VERSION } : {}) }; } export function capture(event: string, properties?: Record): void { @@ -56,7 +58,7 @@ export function capture(event: string, properties?: Record): vo event, properties: { ...properties, - ...appVersionProperties(), + ...baseProperties(), }, }); } catch (err) { @@ -76,7 +78,7 @@ export function identify(userId: string, properties?: Record): properties: { ...properties, api_url: API_URL, - ...appVersionProperties(), + ...baseProperties(), }, }); identifiedUserId = userId;