Skip to content

chore(events): add cli-emitted events#230

Closed
LorrisSaintGenez wants to merge 7 commits into
mainfrom
feat/grout-349-cli-flow-events
Closed

chore(events): add cli-emitted events#230
LorrisSaintGenez wants to merge 7 commits into
mainfrom
feat/grout-349-cli-flow-events

Conversation

@LorrisSaintGenez

@LorrisSaintGenez LorrisSaintGenez commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Adds the CLI-emitted flow events for GROUT-349 on top of the existing Command Invoked: command lifecycle, OAuth auth flow, application create, and plan upgrade/downgrade funnels.

Event catalog (pkg/telemetry/events.go)

All event names and property schemas live in one catalog; call sites go through typed constructors so names and keys can't drift.

  • Command Invoked / Command Completed (duration_ms, succeeded, exit_code) / Command Failed (error_class, error_source, http_status)
  • CLI Auth Started / Browser Opened / Browser Failed / Callback Received / Completed / Failed, with flow (login|signup) and step on failures
  • CLI Application Create Started / Completed / Failed / Aborted, with triggered_from (auth_flow|explicit_command)
  • CLI Application Upgrade|Downgrade Started / Accepted Terms / Declined Terms / Failed / Completed, with requested_plan on Started (raw --plan, omitted when picked interactively) and a consistent from_plan/to_plan pair (plan ids, unknown fallback)

Every tracked event also carries:

  • version — semver for released binaries, main for source builds, so Segment destinations can route dev vs prod traffic to separate Amplitude projects
  • event_index — per-invocation emit order; the deterministic ordering ground truth

Delivery and ordering

  • Events are buffered into a single batch flushed once at the end of Execute, instead of the library's 5s periodic flushes — fixes events being split across requests and dropped at exit
  • The flush is bounded (3s HTTP timeout, 5s ceiling), so telemetry can never hang the CLI
  • Timestamps are spaced ≥1ms apart (Amplitude truncates event_time to ms and same-ms ordering is undocumented); event_index complements this deterministically
  • Lazy logins (EnsureAuthenticated, session expiry) now thread the command context, so auth flow events are also emitted on the most common auth path, attributed to the triggering command

Error classification (pkg/cmdutil/classify.go)

ClassifyError maps errors to a stable, low-cardinality error_class + error_source (+ http_status when carried). Dashboard non-2xx responses now return a typed dashboard.APIError carrying the status. Generic errors.New/fmt.Errorf root causes classify as unknown.

Beyond telemetry

  • root.go error handling is restructured: exit-code resolution is extracted to exitCodeForError so Command Completed can carry it. Printing behavior is unchanged.
  • New tests: single-ordered-batch regression, timestamp monotonicity, event_index, plan-change funnel events by direction, classifier table.

Follow-ups (not in this PR)

  • Segment/Amplitude config: prod destination filtered on version, millisecond resolution on the Amplitude project
  • Terminal events for no-op / dry-run / pre-API-error exits

@LorrisSaintGenez LorrisSaintGenez self-assigned this Jun 4, 2026
@codacy-production

codacy-production Bot commented Jun 4, 2026

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 46 complexity · 33 duplication

Metric Results
Complexity 46
Duplication 33

View in Codacy

TIP This summary will be updated as you push new changes.

LorrisSaintGenez and others added 5 commits June 9, 2026 16:33
CI only enforces gofumpt via golangci-lint; golines (run by 'task format')
rewrapped untouched code and added noise to the diff. AGENTS.md:
avoid unrelated formatting churn.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Started carried an ignored plan param; the other events mixed the raw
--plan input with the resolved template id under one 'plan' property.
Now: requested_plan (raw --plan, omitted when picked interactively) on
Started, and a from_plan/to_plan pair (plan ids, 'unknown' fallback) on
the accepted/declined/failed/completed events.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
version (semver for releases, 'main' for source builds) lets Segment
destinations route dev vs prod traffic to the right Amplitude project.
event_index is the per-invocation emit order: a deterministic ordering
key that doesn't depend on Amplitude's undocumented same-millisecond
tie-breaking, complementing the spaced timestamps.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
AuthStepBrowser was never emitted (browser failures have their own
non-fatal event); root.go now goes through the CommandInvoked
constructor like every other catalog event; and the error_class
fallback maps Go's generic error types to 'unknown' instead of letting
*errors.errorString dominate the dimension with no signal.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
EnsureAuthenticated and ReauthenticateIfExpired ran the OAuth flow with
context.Background(), silently dropping every CLI Auth * event on the
most common auth path (running any command while logged out, or with an
expired session). Thread the command context down so lazy logins emit
the same flow events, attributed to the command that triggered them.

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

1 participant