feat(telemetry): PR 4 — wire 7 core events to call sites#1433
Open
brennanb2025 wants to merge 4 commits intomainfrom
Open
feat(telemetry): PR 4 — wire 7 core events to call sites#1433brennanb2025 wants to merge 4 commits intomainfrom
brennanb2025 wants to merge 4 commits intomainfrom
Conversation
Wires app_opened, repo_added, workspace_created, agent_started, agent_error, and settings_changed to their call sites, completing the v1 event set per telemetry-implementation.md §PR 4. agent_started attribution flows from the renderer launch sites through pty:spawn so main fires the event only after spawn resolves; bare-shell tabs and failed spawns produce no event. Settings changes filter through SETTINGS_CHANGED_WHITELIST with pre/post diff to suppress no-op flips. workspace_created.source is validated against the closed enum at the IPC boundary; unknown surfaces map to 'unknown'. Stacks on #1372 (PR 1 foundations), #1374 (PR 2 transport), and #1385 (PR 3 first-launch UX). Co-authored-by: Orca <help@stably.ai>
…y-pr-4 # Conflicts: # src/shared/types.ts
Replace the mark-only firstAppOpenedFired flag with trackAppOpenedOnce(), which emits and flips the gate atomically. Existing-user banner Sure path now fires app_opened before telemetry_opted_in; banner ✕ acknowledge fires app_opened without an opt-in event. Removes the no-client early return on the opt-in branch so console-mirror builds still see the event. Threads launchSource: 'sidebar' through both Project-mode launchWorkItemDirect call sites added in #1424. Co-authored-by: Orca <help@stably.ai>
Conflicts resolved: - launch-work-item-direct.ts: kept telemetry threading (launchSource, telemetrySource, agent_started/agent_error events) on top of main's unified-picker refactor (pasteDraftWhenAgentReady, agentTrust preflight, buildAgentDraftLaunchPlan). Dropped launchFromBranch — main removed its only caller (CreateFromTab) so the helper is dead code. - NewWorkspaceComposerModal.tsx: dropped the Quick / Create-from tab UI removed in #1426; kept the telemetrySource passthrough into ComposerModalData and useComposerState. - TaskPage.tsx: kept main's behavior of opening the composer (telemetry flows via openComposerForItem / openComposerForLinearItem). - useComposerState.ts: merged imports — kept agent-paste-draft + draft launch plan from main, kept tuiAgentToAgentKind / AgentStartedTelemetry from telemetry branch. - useIpcEvents.ts: kept telemetrySource: 'shortcut' on Cmd+N composer open; dropped initialTab (no longer a tabbed UI). - CreateFromTab.tsx: accepted main's deletion. Co-authored-by: Orca <help@stably.ai>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Wires all 7 v1 telemetry events to their call sites, completing the event set per
telemetry-implementation.md§PR 4. Stacks on #1372 (foundations), #1374 (transport), and #1385 (first-launch UX). No new schema work.agent_startedattribution flows from each renderer launch site through the queued startup →pty:spawnIPC, and main fires the event only afterprovider.spawn()resolves — bare-shell tabs and failed spawns produce no event.agent_errorrides the same catch path for claude spawns and the work-item readiness timeout (5s →error_class: 'unknown', since process-startup has no v1 enum slot).repo_addedis suppressed when the user re-picks an existing repo;settings_changedfilters throughSETTINGS_CHANGED_WHITELISTwith a pre/post diff so no-op blur saves don't inflate adoption signal.workspace_created.sourceis validated against the closed enum at the IPC boundary — unknown surfaces map to'unknown'rather than dropping, so dashboard gaps surface as a slice.The renderer threading hops launch site →
queueTabStartupCommand→pendingStartupByTabId→pty-connection→pty-transport→pty:spawn. TheEventProps<'agent_started'>type carries the payload through every hop so a missing field is a typecheck error, and main re-validates each enum field on receive as defense-in-depth against a malformed or spoofed IPC.Test plan
pnpm test— all suites green, including the newclassify-error.test.ts,pty.test.ts → 'agent_started telemetry', andworktree-activation.test.tstelemetry-forwarding case.app_opened→ folder-pickerrepo_added→ composerworkspace_created→ agent clickagent_started, in order, in PostHog Live Events.agent_errorwitherror_class: 'binary_not_found';agent_starteddoes NOT fire.agent_errorno-leak: confirm in the PostHog event inspector thatmessageandstackare absent — onlyerror_class(+ optional whitelistederror_name) ride.workspace_created.sourceper surface: sidebar+, Landing, Cmd+J, keyboard shortcut, Tasks page → expectedsourceper surface; hand-crafted IPC with bogus source →'unknown'.settings_changedno-op suppression: re-save the same experimental toggle → no event. Flip it → event withvalue_kind: 'bool'. Flip a non-whitelisted setting (e.g. theme) → no event.app_openedreload dedup: Cmd+R the renderer → no secondapp_openedin the same session.repo_added.ORCA_BUILD_IDENTITY=rc ORCA_POSTHOG_WRITE_KEY=phc_...→ run the funnel → all 7 events show in PostHog 406068 withorca_channel: rc,$process_person_profile: false, no$geoip_*/$ip.Manual smoke test results — packaged build vs PostHog 406068
Ran a packaged official build (
ORCA_BUILD_IDENTITY=stable ORCA_POSTHOG_WRITE_KEY=phc_…) against an isolated user-data-dir. All 7 V1 events round-tripped to PostHog. Build artifact: arm64Orca.app, phc key embedded inapp.asar.Core funnel — new-user install (default opted-in)
app_openeddid-finish-loadrepo_addedmethod: folder_pickerworkspace_createdsource: sidebar,from_existing_branch: falseagent_startedagent_errorerror_class: unknown,agent_kind: codex(renderer-side path)settings_changedsetting_key: editorAutoSave,value_kind: booltelemetry_opted_in/telemetry_opted_outviacorrectConsent gating
existedBeforeTelemetryRelease:true,optedIn:null): zero events transmitted in 10 min — gate works.telemetry_opted_out { via: 'first_launch_banner' }✓setOptIn(true)from pending_banner) →telemetry_opted_in { via: 'settings' }. This is by design —deriveOptInVia(src/main/ipc/telemetry.ts:92) deliberately refuses to tagsetOptIn(true)asfirst_launch_bannerto keep that signal unforgeable from a compromised renderer; the silent-acknowledge ✕ path is the only legitimateoptedIn:truetransition for that cohort.Edge cases verified
repo_addedre-add suppression: re-add same path twice → exactly 1 event ✓settings_changedno-op suppression: re-save identical value → 0 events; flip → 1 event ✓settings_changedwhitelist: non-whitelisted setting (terminalFontSize) → 0 events; second whitelist member (experimentalAgentDashboard) → 1 event ✓workspace_created.sourceunknown fallback: bogus surface AND omittedtelemetrySourceboth land assource: 'unknown'✓workspace_created.from_existing_branch:trueonly whenbaseBranchis non-empty ✓agent_startedbare-shell: spawn withouttelemetryfield → 0 events ✓agent_startedinvalid enum: spawn withagent_kind: 'NOT_A_REAL_AGENT'→ dropped at thesafeParseboundary ✓app_openedreload dedup:location.reload()in renderer → no secondapp_opened(gate held) ✓app_opened, unknown event names, non-whitelistederror_name, rawerror_messageonagent_error— all dropped, none transmitted ✓telemetrySetOptInflips → only first 5 transmitted, 6th dropped AND state did not mutate (rate-limit fires before the write) ✓Review-fix flows (post-rebuild verification)
The review fix replaced the mark-only gate with
trackAppOpenedOnce()and addedapp_openedemission to two more code paths. Re-verified end-to-end:trackAppOpenedOnce())app_openedsetOptIn(true)frompending_banner)app_openedBEFOREtelemetry_opted_inapp_openedfirstapp_openedonly, notelemetry_opted_inNotes
agent_errorcatch path onpty.ts:805-836from a renderer-driven smoke test —pty:spawnswallows non-existentcwdand badshellOverriderather than throwing. Coverage there comes from the unit tests (classify-error.test.ts,pty.test.ts → 'agent_error'). The renderer-sideagent_error(5s readiness timeout inlaunch-work-item-direct.ts) was exercised end-to-end and lands correctly.Made with Orca 🐋