Repository navigation
Standardize property naming: snake_case for PostHog, camelCase for JSON #189
Description
The rule
- PostHog properties are
snake_case. - JSON payload fields are
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's convention. The same datum can therefore appear under both spellings, and that is correct rather than a mistake to reconcile.
Where we stand
JSON is already compliant. Every field across packages/cli/src/schemas/*.ts is camelCase; a scan for snake_case field names returns nothing. No change needed, and the rule is worth writing down so it stays that way.
PostHog is mixed. gh_owner (added in the #179 work) follows the rule; everything older does not.
Changes needed
Standard properties attached to every event, in packages/cli/src/telemetry.ts:
| current | should be |
|---|---|
cliVersion |
cli_version |
scaffoldVersion |
scaffold_version |
cli |
unchanged (single word) |
gh_owner |
unchanged (already correct) |
Per-event properties passed by callers:
| current | should be |
|---|---|
durationMs |
duration_ms |
loggedIn |
logged_in |
ruleCount |
rule_count |
anonymous, command, meta, status, success |
unchanged (single word) |
Not in scope: groupType and groupKey are parameters of the PostHog SDK's groupIdentify call, not properties we name.
What else has to move
The analytics spec names these properties normatively and would need a delta in the same change. openspec/specs/analytics/spec.md, requirement "All capture calls include standard properties", spells cliVersion and scaffoldVersion in the requirement text and in four scenarios. The "CLI version is the version the build reports as its own" requirement names cliVersion too.
Tests assert the current names, at minimum packages/cli/test/telemetry.test.ts.
The part that needs a decision
This breaks existing PostHog insights. Any saved insight, dashboard, funnel, or cohort filtering on cliVersion, scaffoldVersion, durationMs, loggedIn, or ruleCount stops matching at the rename, and historical events keep the old spelling forever. So the options are:
- Rename and accept the discontinuity. Simplest code, and every event after the release is consistent. Existing insights need updating by hand, and any query spanning the boundary needs both spellings.
- Dual-write for a period — emit both spellings, migrate insights, then drop the old. No discontinuity, at the cost of duplicated properties on every event and a second change later to finish it.
- Leave the legacy names and apply the rule only to new properties. Zero disruption; the inconsistency is permanent and every future reader has to learn which era a property comes from.
Worth noting the volume is low enough that option 1 is plausible: five properties, and the CLI's event taxonomy is small.
Notes
Found while adding gh_owner for #179. That property was written snake_case deliberately, so it is already on the right side of this and needs no follow-up. Nothing here blocks #179.
Activity
theCodeDrift commentedon Aug 26, 2026
Decision: rename all five. Option 1.
Volume and stage are both small enough, and consistency is worth more than the discontinuity:
cliVersioncli_versionscaffoldVersionscaffold_versiondurationMsduration_msloggedInlogged_inruleCountrule_countcli,anonymous,command,meta,status,successare single words and unaffected.gh_owneris already correct.Accepted cost: historical events keep the old spellings permanently, so any query spanning the release needs both. Saved insights and dashboards filtering on these five need updating by hand at the same time.
Must land together in one change, or the rename is worse than the inconsistency:
packages/cli/src/telemetry.tsand at the per-event call sites.analyticsspec — it namescliVersionandscaffoldVersionnormatively in the "All capture calls include standard properties" requirement, in four of its scenarios, and again in "CLI version is the version the build reports as its own".packages/cli/test/telemetry.test.ts.Splitting those leaves the spec asserting a name the code no longer emits, which is the same failure this session already hit once on #190 (a spec scenario naming
authafter the field moved toinfo).Worth doing before more properties accumulate: five is a cheap rename, fifteen is not.
fix(telemetry)!: spell PostHog properties in snake_case
theCodeDrift commentedon Aug 26, 2026
Correction: this issue's premise is wrong, and so is the decision recorded above. #193 is closed rather than merged.
I filed this claiming PostHog properties were inconsistently named and that five (later seven) needed renaming to
snake_case. Measured properly onmainat3c78ffe, the codebase was consistent:cli_run,cli_check_completed,cli_rule_created, and 7 more, allsnake_casecli,cliVersion,scaffoldVersion,durationMs,loggedIn,ruleCount,errorCount,warningCount, allcamelCaseA deliberate split:
snake_casefor the event taxonomy,camelCasefor properties. Every property followed it, without exception.gh_owneris the exception, and I introduced it in #190. When the property was renamed fromgh_orgtogh_ownerI carried the underscore over without checking what its neighbours looked like. This issue then audited, found onesnake_caseproperty beside sevencamelCaseones, and drew exactly the wrong conclusion: that the seven were wrong rather than the one.The supporting argument does not hold either. PostHog spells
$current_urland$lib_versionthat way because they are its own reserved properties. That is a fact about PostHog's namespace, not a recommendation for a project's own property names.The cost of acting on it would have been real and permanent: renaming seven properties away from the house convention, with historical events keeping the old spellings forever and every saved insight needing a hand edit. Paid to make the codebase less consistent.
What is actually happening
gh_ownertoghOwner. One property, added this release and not yet in a stable build, so there is no historical discontinuity and no dashboard migration.analyticsspec will state the real rule so the next audit does not have to infer it from whichever names happen to exist. That absence is what let this happen: with nothing normative to check against, a single outlier reads as evidence about the majority.Also wrong, for the record
The #193 changeset bumped
patchwhile both its commits were marked!, and comparable behaviour changes here useminor. Moot now, but noting it so the mistake is not repeated in a change that does ship.Reopening this issue would mean re-arguing a settled convention. Closing it as not-planned instead; the
ghOwnerfix and the spec requirement are tracked on the new PR.