fix(core): keep session-attribution props on minimal $feature_flag_called events - #4287
Conversation
…lled events The minimal `$feature_flag_called` allowlist (MINIMAL_FLAG_CALLED_EVENT_PROPERTIES) rebuilt the event from a strict allowlist that omitted every referrer and `utm_*`/campaign param. The server session table derives session-initial UTM and channel type from whichever event lands first in a session, so a minimal flag-called event firing first nulled out the whole session's attribution. Add the referrer and campaign-param keys (kept in sync with the browser SDK's CAMPAIGN_PARAMS and the server session table) to the allowlist so they survive minimization. Entries are inert on SDKs that never set them. Generated-By: PostHog Code Task-Id: 9dd7e155-821a-4fe9-93bf-1b7e9c0a1c06
|
Size Change: +5.29 kB (+0.03%) Total Size: 18.2 MB 📦 View Changed
ℹ️ View Unchanged
|
|
I reproduced the attribution loss on the real minimal
Local evidence: base stripped |
|
Addressed the review feedback in
Validation on Node 24.18.0:
Autoreview against |
posthog-node Compliance ReportDate: 2026-08-04 14:09:41 UTC ✅ All Tests Passed!111/111 tests passed Capture_V1 Tests✅ 94/94 tests passed View Details
Feature_Flags Tests✅ 17/17 tests passed View Details
|
posthog-js Compliance ReportDate: 2026-08-04 14:16:06 UTC ✅ All Tests Passed!26/26 tests passed Capture Tests✅ 26/26 tests passed View Details
|
|
Refreshed onto latest Post-merge validation on Node 24.18.1 / pnpm 11.7.0:
Contract rechecked in the real code/tests: all 24 canonical campaign/session-attribution keys plus |
|
Final rolling-main refresh complete at signed head
PR state was otherwise left unchanged. |
…to minimal-event allowlist ## Why [posthog-js#4287](PostHog/posthog-js#4287) ("keep session-attribution props on minimal $feature_flag_called events") fixed a web-analytics data-correctness bug: the minimal-event allowlist added by posthog-js#4172 (documented here in [#16](#16)) stripped every referrer and campaign/click-id super property (`utm_source`, `gclid`, `$referring_domain`, etc.). Web-analytics session-initial UTM attribution and channel-type grouping are read from the **first event in a session**, and a minimized `$feature_flag_called` event can be that first event — so a flag call landing first in a session silently nulled out the session's whole attribution. `openspec/specs/feature-flag-called-tracker/spec.md`'s "Minimal event mode for non-experiment flags" requirement lists allowlist categories at a category level but never mentions referrer or campaign/UTM properties anywhere — not in the category list, the posthog-python reference literal, or the "everything else... SHALL be stripped" clause. This is a real spec gap: an SDK implementing minimal-event-mode strictly per the current text would (correctly, per the spec) strip these and reproduce the same bug. ## What this PR does - Adds a "Session-attribution properties" category to the minimal-event allowlist — `$referring_domain` and canonical campaign/click-id super properties (`utm_source`, `utm_medium`, `utm_campaign`, `utm_content`, `utm_term`, `gad_source`, `mc_cid`, `gclid`/`fbclid`-style ids) — noting that full `$referrer` remains excluded (only `$referring_domain` and bare campaign-param keys survive), preserving the minimal event's privacy/payload-size intent. - Adds one new `@client` scenario asserting session-attribution properties are retained while `$referrer` itself is still stripped. - Change proposed and archived on this branch per this repo's one-PR-per-change convention (`openspec/changes/archive/2026-08-05-fix-flag-called-session-attribution/`). - `openspec validate --specs --strict` passes (59/59). ## Uncertain / flagged for reviewer attention - Only posthog-js/posthog-node (the shared `@posthog/core` implementation) were confirmed to have shipped this bug and fix. No other in-scope SDK currently ships a minimal-event mode per #16's own audit, so this is a documentation-only correction, not a new cross-platform parity gap — flagged as a follow-up check in `tasks.md` §3.1 for if/when another SDK adds minimal-event mode. Generated-By: PostHog Code Task-Id: 457dcd47-67c0-4fb7-830b-c3d13a790c8d
…lled events (#4287) * fix(core): keep session-attribution props on minimal $feature_flag_called events The minimal `$feature_flag_called` allowlist (MINIMAL_FLAG_CALLED_EVENT_PROPERTIES) rebuilt the event from a strict allowlist that omitted every referrer and `utm_*`/campaign param. The server session table derives session-initial UTM and channel type from whichever event lands first in a session, so a minimal flag-called event firing first nulled out the whole session's attribution. Add the referrer and campaign-param keys (kept in sync with the browser SDK's CAMPAIGN_PARAMS and the server session table) to the allowlist so they survive minimization. Entries are inert on SDKs that never set them. Generated-By: PostHog Code Task-Id: 9dd7e155-821a-4fe9-93bf-1b7e9c0a1c06 * fix: complete minimal flag attribution coverage --------- Co-authored-by: posthog[bot] <206114724+posthog[bot]@users.noreply.github.com> Co-authored-by: Manoel Aranda Neto <marandaneto@gmail.com>
…to minimal-event allowlist (#26) ## Why [posthog-js#4287](PostHog/posthog-js#4287) ("keep session-attribution props on minimal $feature_flag_called events") fixed a web-analytics data-correctness bug: the minimal-event allowlist added by posthog-js#4172 (documented here in [#16](#16)) stripped every referrer and campaign/click-id super property (`utm_source`, `gclid`, `$referring_domain`, etc.). Web-analytics session-initial UTM attribution and channel-type grouping are read from the **first event in a session**, and a minimized `$feature_flag_called` event can be that first event — so a flag call landing first in a session silently nulled out the session's whole attribution. `openspec/specs/feature-flag-called-tracker/spec.md`'s "Minimal event mode for non-experiment flags" requirement lists allowlist categories at a category level but never mentions referrer or campaign/UTM properties anywhere — not in the category list, the posthog-python reference literal, or the "everything else... SHALL be stripped" clause. This is a real spec gap: an SDK implementing minimal-event-mode strictly per the current text would (correctly, per the spec) strip these and reproduce the same bug. ## What this PR does - Adds a "Session-attribution properties" category to the minimal-event allowlist — `$referring_domain` and canonical campaign/click-id super properties (`utm_source`, `utm_medium`, `utm_campaign`, `utm_content`, `utm_term`, `gad_source`, `mc_cid`, `gclid`/`fbclid`-style ids) — noting that full `$referrer` remains excluded (only `$referring_domain` and bare campaign-param keys survive), preserving the minimal event's privacy/payload-size intent. - Adds one new `@client` scenario asserting session-attribution properties are retained while `$referrer` itself is still stripped. - Change proposed and archived on this branch per this repo's one-PR-per-change convention (`openspec/changes/archive/2026-08-05-fix-flag-called-session-attribution/`). - `openspec validate --specs --strict` passes (59/59). ## Uncertain / flagged for reviewer attention - Only posthog-js/posthog-node (the shared `@posthog/core` implementation) were confirmed to have shipped this bug and fix. No other in-scope SDK currently ships a minimal-event mode per #16's own audit, so this is a documentation-only correction, not a new cross-platform parity gap — flagged as a follow-up check in `tasks.md` §3.1 for if/when another SDK adds minimal-event mode. Generated-By: PostHog Code Task-Id: 457dcd47-67c0-4fb7-830b-c3d13a790c8d Co-authored-by: posthog[bot] <206114724+posthog[bot]@users.noreply.github.com>
Problem
Web analytics session-level UTM attribution keys off the first event in a session. posthog-js can make that first event a
$feature_flag_called— and for projects gated into the new minimal$feature_flag_calledpath (MINIMAL_FLAG_CALLED_EVENT_PROPERTIESinpackages/core/src/featureFlagUtils.ts), the event is rebuilt from a strict allowlist that omitted every referrer andutm_*/campaign param.Campaign params ride along as super properties under their bare names (
utm_source,gclid, …) and are exactly what the server session table reads to build session-initial UTM and channel type. So a UTM-less minimal flag-called event landing first silently set the whole session's attribution to NULL — corrupting web-analytics campaign attribution and channel-type grouping for any project that uses minimal flag-called events together with flags + campaign tracking. It degrades data correctness rather than erroring, so it looks like a config gap rather than a bug.Changes
$referring_domainand all canonical campaign params (utm_source/medium/campaign/content/term,gad_source,mc_cid, and click-ID params) to the minimal allowlist, kept in sync with the browser SDK'sCAMPAIGN_PARAMSand the server session table. Full$referrerremains excluded to preserve the minimal event's privacy and payload-size contract.$referrerand unrelated super properties remain stripped.The fix lives in
@posthog/core, so it applies to both the browser SDK and posthog-node through the sharedminimizeFlagCalledEventProperties.Out of scope
The originating report also bundled two docs/support clarifications for tickets where the SDK behaves as designed (SPA pageviews via
capture_pageview: 'history_change';identified_onlyonly suppressing$initial_utm_*person props). Those are documentation changes that live outside this repo and are not addressed here.Release info Sub-libraries affected
Libraries affected
Checklist
If releasing new changes
pnpm changesetto generate a changeset file🤖 Agent context
Autonomy: Fully autonomous
Investigated with Claude Code (PostHog Desktop). Traced the reported symptom to its root cause: campaign params are stored as bare-named super properties and consumed by the server session table for session-initial UTM, but the minimal
$feature_flag_calledallowlist introduced in #4172 excluded them. Chose to add the canonical campaign keys and$referring_domaindirectly to the core allowlist (rather than threading them throughtransportKeys) because these are canonical PostHog property names shared with the server, matching how$current_url/$pathnameare already handled. Verified via the existing minimal-flag-called browser suite plus a new UTM-preservation test; ran@posthog/coreunit tests (780 passing) and lint.Created with PostHog Desktop from this inbox report.