Skip to content

refactor(cli): extract telemetry delivery into transport.ts#2344

Open
jrusso1020 wants to merge 1 commit into
mainfrom
jrusso/extract-telemetry-transport
Open

refactor(cli): extract telemetry delivery into transport.ts#2344
jrusso1020 wants to merge 1 commit into
mainfrom
jrusso/extract-telemetry-transport

Conversation

@jrusso1020

Copy link
Copy Markdown
Collaborator

What

Extracts the reliability-critical telemetry delivery layer — the in-memory event queue, async flush(), and the exit-time detached-child flushSync() — out of packages/cli/src/telemetry/client.ts into a new transport.ts.

client.ts stays the CLI-facing policy layer:

  • shouldTrack() opt-out checks (dev mode, DO_NOT_TRACK, HYPERFRAMES_NO_TELEMETRY, config)
  • trackEvent() system-metadata enrichment
  • showTelemetryNotice() first-run disclosure

…and re-exports flush / flushSync, so events.ts, index.ts, and the cli.ts exit handlers keep importing from ./client.js unchanged.

Why

This is the code path that had the process-exit data-loss bug fixed in #2105 — render telemetry was ~6× undercounted and geographically US-skewed because the old drain-first flush emptied the queue before delivery confirmed, and the render command's process.exit() teardown killed the in-flight request. Isolating the delivery mechanism into its own focused, dependency-light module (only ./config + node builtins) keeps that subtle, reliability-critical path in one place and reduces client.ts to just policy.

Follow-up to the render-telemetry-gap investigation. A delivery-health canary was also added to the CLI Observability dashboardrender_complete ÷ successful render commands, which should sit ~1.0 and would surface any regression of this class immediately.

How

Pure code motion — no behavior change, public API identical. transport.ts owns the queue and stamps each event's dedup uuid + ISO timestamp in a new enqueue(); trackEvent() enriches with system metadata then calls enqueue(). buildPayload/flush/flushSync bodies are moved verbatim.

Test plan

  • vitest run src/telemetry/client.test.ts src/telemetry/events.test.ts38/38 pass (client.test.ts still validates queue-retention, uuid idempotency, and the detached-child flushSync path through the public API — unchanged)
  • oxlint clean, oxfmt --check clean
  • tsc --noEmit — no new type errors in telemetry/
  • bun run build succeeds

🤖 Generated with Claude Code

Split the reliability-critical delivery layer — the event queue, async
flush(), and the exit-time detached-child flushSync() — out of client.ts
into a new transport.ts. This is the code that had the exit-race data-loss
bug fixed in #2105; isolating it keeps that subtle path in one focused,
dependency-light module (config + node builtins only).

client.ts stays the CLI-facing policy layer (opt-out checks, system-metadata
enrichment, first-run notice) and re-exports flush/flushSync, so events.ts,
index.ts, and the cli.ts exit handlers keep importing from ./client.js
unchanged.

Pure code motion — public API identical, no behavior change. Telemetry
tests pass unchanged (client.test.ts still exercises delivery through the
public API).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@james-russo-rames-d-jusso james-russo-rames-d-jusso 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.

Reviewed at 30791869.

Verified this is genuinely pure code motion — no observable behavior change:

  • enqueue() equivalence: the new enqueue(event, properties, distinctId) calls randomUUID() and new Date().toISOString() at the same synchronous moment inside the caller's tick as the old inline eventQueue.push({...}) did — nothing observable moves. Field shape at rest is identical: {uuid, event, distinctId, properties, timestamp}, same order, same undefined-propagation for distinctId?.
  • flush() / flushSync() / buildPayload(): moved verbatim, no edits (checked with a byte-level compare of the moved bodies). The snapshot-first / delivered-set-only-after-fetch invariant that #2105 was built around is preserved intact.
  • Public API surface: client.ts re-exports flush, flushSync from transport, so every existing importer (events.ts, feedback.ts, render.ts, telemetryIdentity.ts, telemetry/index.ts — checked all seven from "./client.js" / from ".../telemetry/client.js" sites in the tree) keeps working with the same names. trackEvent / shouldTrack / showTelemetryNotice untouched.
  • POSTHOG_API_KEY phc_ prefix check stays in client.ts::shouldTrack (now via import), which is the right layer — that check gates "is telemetry configured at all," which is a policy question, not a transport concern.

Split cleanly matches the stated policy-vs-delivery framing: the reliability-critical exit-race path now sits in one file whose dependencies are just ./config + node:child_process + node:crypto, which is a nice invariant to have around a piece of code that had a real prod bug six PRs ago.

No new tests since the existing client.test.ts exercises delivery through the public API — appropriate for a no-behavior-change refactor. The dashboard canary (render_complete ÷ successful render commands ~1.0) is the right belt to pair with this suspenders.

LGTM from my side.

Review by Rames D Jusso

@miguel-heygen miguel-heygen left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Code review at 30791869: the policy/delivery split is clean and the public API remains stable. Additive to the existing review, I checked the refactor cleanup boundary: transport.ts:104-123 pairs the abort timer with clearTimeout in finally, and transport.ts:131-149 preserves detached-child + unref() exit delivery. Focused telemetry tests pass 38/38 locally.

I cannot stamp the current head yet because the required Tests on windows-latest check is red. The failure is an unrelated timeout in packages/cli/src/capture/contactSheet.test.ts:13 after 20s (the telemetry suites themselves passed), so this looks rerun-worthy rather than a transport defect; however it is a required check and the PR body does not account for it.

— Magi / deepwork

Verdict: COMMENT
Reasoning: The code motion and focused telemetry behavior are ready, but required Windows CI must be rerun to green before approval.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants