fix(telemetry)!: spell PostHog properties in snake_case - #193
fix(telemetry)!: spell PostHog properties in snake_case#193thecodedrift wants to merge 2 commits into
Conversation
PostHog's own properties are `$current_url` and `$lib_version`. Ours were a mix, which makes a property list unsearchable: you have to know which spelling a property happened to be born with before you can filter on it. Five renames, on the wire only: `cliVersion` -> `cli_version`, `scaffoldVersion` -> `scaffold_version`, `durationMs` -> `duration_ms`, `loggedIn` -> `logged_in`, `ruleCount` -> `rule_count`. `cli`, `anonymous`, `command`, `success` are single words; `gh_owner` was already correct. `groupIdentify`'s `groupType`/`groupKey` are the SDK's parameter names, not properties we choose, and are untouched. The TypeScript feeding those properties stays camelCase. `RunContext` is a domain type, not a wire format, so `duration_ms: context.durationMs` is the translation happening at the one boundary where it belongs rather than camelCase leaking outward or snake_case leaking inward. `taskless info --json` keeps its `loggedIn` field. Two wire formats with two audiences, each following its own ecosystem's convention, so the same value appearing as `loggedIn` in JSON and `logged_in` in telemetry is correct rather than a discrepancy to reconcile. The analytics spec now says so normatively, so the next property added does not have to rediscover it. Spec and tests move with the code. The `analytics` spec named `cliVersion` and `scaffoldVersion` in the standard-properties requirement and four scenarios, and `durationMs`/`loggedIn` in the cli_run requirement; leaving any of them would have left the spec asserting a name nothing emits. Its cli_run requirement already said `cli_version`, which is the inconsistency this fixes, now true rather than aspirational. Hard cut, no dual-emit. Historical events keep the old spellings permanently, so a query spanning the release needs both, and saved insights filtering on these five need updating by hand. Fixes #189 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`cli_check_completed` carries `errorCount` and `warningCount`, built into an object forty lines before the `capture()` that sends them, which is why the audit behind the rename table missed both. The analytics spec named them as well. Folded into this change rather than left for a follow-up. The cost of the rename is not the edit, it is the historical discontinuity: events emitted before the release keep the old spelling forever, and saved insights need updating by hand. That cost is paid once per release. Renaming five now and two later pays it twice for the same total work, and this branch already carries the changeset and the dashboard-migration note that make it payable once. The locals stay camelCase. They are ordinary TypeScript, not a payload, and the translation happens at the wire boundary, matching how `duration_ms: context.durationMs` reads in `telemetry-run.ts`.
fc7ef7e to
0f704b7
Compare
|
Closing. The premise behind this PR is wrong, and the evidence I collected supported the opposite conclusion. The codebase was consistent, not inconsistent. Measured on
That is a deliberate split:
The justification I wrote here does not survive either. PostHog spells So this PR renames seven properties away from the house convention, pays a permanent discontinuity in event history to do it, and adds a normative spec requirement asserting the wrong rule. The minimal correct change is the inverse: rename Two things worth carrying forward from here rather than losing:
Correction recorded on #189. |
Spells every PostHog property in
snake_case, matching PostHog's own convention.The rule this establishes
snake_case.camelCase.Two wire formats with two audiences: PostHog properties are read in insights and dashboards, JSON payloads are parsed by agents and scripts. Each follows its own ecosystem. The same value can legitimately appear under both spellings, and that is correct rather than something to reconcile.
The convention is now stated normatively in the
analyticsspec rather than being implied by whichever names happened to exist.Renames
cliVersioncli_versionscaffoldVersionscaffold_versiondurationMsduration_mscli_runloggedInlogged_incli_runruleCountrule_countcli_rule_created,cli_rule_improvederrorCounterror_countcli_check_completedwarningCountwarning_countcli_check_completedUnchanged because they are single words or already correct:
cli,anonymous,command,meta,status,success,findings,gh_owner.groupTypeandgroupKeyare parameters of the PostHog SDK'sgroupIdentifycall, not properties we name, so they are untouched.JSON is already compliant and is not touched. Every field across
packages/cli/src/schemas/*.tsis camelCase.The last two were nearly missed
errorCountandwarningCountare built into an object incheck.tsabout forty lines before thecapture()that sends them, so an audit that greps aroundcapture(does not surface them. They were found by tracing the event instead.They are folded in here rather than left for a follow-up because the cost of this rename is not the edit, it is the discontinuity, and that cost is paid once per release. Renaming five now and two later pays it twice for the same total work.
Breaking, and how
This is a hard cut with no dual-emit window. Historical events keep the old spellings permanently, so any PostHog query spanning this release needs both names to see a whole series. Saved insights, dashboards and cohorts filtering or breaking down on these seven need updating by hand; they will not error, they will silently return only the events emitted before the release.
camelCase locals are deliberate
Only the wire boundary changes.
RunContext.durationMsandRunContext.loggedInstay camelCase because they are domain types, not payloads; the translation happens where the event is emitted (duration_ms: context.durationMs). Same for thecheckcounters. This keepsRunContextconstruction across the codebase and tests untouched.Untouched for the same reason:
loggedInintaskless info --json(a JSON field, correctly camelCase), andinstall.cliVersionin.taskless/taskless.json(a manifest field, not telemetry).A latent contradiction, now resolved
The
cli_runrequirement in the analytics spec already saidcli_versionwhile the code emittedcliVersion. Resolved in the code's favour.Archived changes are not rewritten
openspec/changes/archive/**is left alone. Those record what actually shipped, andcliVersiongenuinely was what shipped. Rewriting them would falsify the record, which is the same thing this changeset warns consumers about on the PostHog side.Verification
pnpm typecheck,pnpm lint,pnpm test(929),pnpm cli check(exit 0),pnpm openspec validate --all --strict(24 passed).Fixes #189