feat(telemetry): instrument 12-stage onboarding funnel#433
Conversation
Adds measurement-only telemetry across the install → activation → success → retention journey. Activation = first PR raised, success = first PR merged, retention magic number = 4 returns on distinct calendar days. Backend: - daemon/onboarding_cdc.go: CDC subscriber turns pr_created/pr_updated/ pr_review_thread_resolved into per-session ao.session.pr_* events plus once-per-install ao.onboarding.first_pr_* milestones, gated by a durable file-based milestoneStore at <dataDir>/telemetry_milestones.json. - daemon/onboarding_prereqs.go: boot-goroutine probe emits ao.onboarding.prereqs_checked (per-check booleans) and a one-shot prereqs_ready when git + tmux (POSIX) + claude|codex + gh auth all pass. - lifecycle/manager.go: ApplyActivitySignal emits ao.session.first_agent_output once per spawn on the first authoritative activity signal. - telemetry/posthog.go: allowlist entries for every new event. Frontend: - shared/telemetry.ts: launch-state (installDay, distinctActiveDays) persisted to <dataDir>/telemetry_app_launches.json; pure computeLaunchUpdate derives isFirstLaunch/isReturnDay/returnCount/isRetained/daysSinceInstall. - renderer/lib/telemetry.ts: ao.app.active carries is_first_launch; ao.app.returned fires on new-day launches with return_count / is_retained / days_since_install. Storage: shared distinct_id from telemetry_install_id keeps daemon + renderer events on one PostHog person. milestoneStore and launch-state files are daemon-owned and renderer-owned respectively, avoiding the install-id race. Refs #432 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Heads-up on PR size — the +5.5k / -3.7k header is misleading. Only my commit Both are repo-wide baseline concerns, not scope of this PR. Happy to open a follow-up adding the lockfile to |
Add PostHog instrumentation for app failures and user CTAs: - daemon_failure (machine-readable code on DaemonStatus, IPC-forwarded) - api_error central interceptor (categorized, IDs stripped) - terminal_attach_failed (pane error + open timeout) - CTA triads: task_create, session_kill, settings_save, orchestrator_spawn (board/restore_dialog), notification open/read All events sanitized: project_id hashed, enum-only codes, raw error messages never sent. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Events capturedFull telemetry surface on this branch. All share one Onboarding funnel — daemon-side (
|
Closes #432.
Summary
Measurement-only instrumentation of the 12-stage onboarding funnel (install → activation → success → retention). No UX changes, no first-run wizard. Activation = first PR raised, success = first PR merged, retention magic number = 4 returns on distinct calendar days.
Backend emitters:
backend/internal/daemon/onboarding_cdc.go— subscribes to the CDC broadcaster; turnspr_created/pr_updated/pr_review_thread_resolvedinto per-sessionao.session.pr_*events plus once-per-installao.onboarding.first_pr_*milestones. Gated by a durable file-basedmilestoneStoreat<dataDir>/telemetry_milestones.json(needed because broadcaster events carry no prior-state history, and daemon restarts must not re-emitfirst_*).backend/internal/daemon/onboarding_prereqs.go— boot-goroutine probe:ao.onboarding.prereqs_checked(per-check booleans for git / tmux on POSIX / claude|codex / gh auth) plus one-shotprereqs_readywhen all pass. Skips entirely on later boots once readiness is claimed.backend/internal/lifecycle/manager.go—firstAgentOutputEventfires once per spawn on the first authoritative activity signal.backend/internal/adapters/telemetry/posthog.go—remotePayloadAllowlistentries for every new event.Frontend emitters:
frontend/src/shared/telemetry.ts—LaunchStatepersisted to<dataDir>/telemetry_app_launches.json(renderer-owned; avoids install-id race). PurecomputeLaunchUpdatederivesisFirstLaunch/isReturnDay/returnCount/isRetained/daysSinceInstall.RETENTION_MAGIC_NUMBER = 4.frontend/src/renderer/lib/telemetry.ts—ao.app.activenow carriesis_first_launch;ao.app.returnedfires on new-day launches withreturn_count/is_retained/days_since_install.Shared distinct_id (
<dataDir>/telemetry_install_id) keeps daemon + renderer events on one PostHog person.See #432 for the full stage-by-stage table, dedup key list, user flows, and PostHog funnel configuration.
Test plan
go test ./backend/internal/daemon/... ./backend/internal/lifecycle/... ./backend/internal/adapters/telemetry/...TestE2E_OnboardingFunnelThroughStoredrives real SQLite store → triggers → CDC poller → broadcaster → subscriber → sinknpm test -- telemetryinfrontend/npm run make) contains all six funnel event strings in bundled daemonHOME=$(mktemp -d) AO_PORT=<free> AO_TELEMETRY_REMOTE=posthog … daemon) —ao.daemon.started+ao.onboarding.prereqs_checkedreached PostHog with no rejection🤖 Generated with Claude Code