Skip to content

feat(analytics-controller): add independent marketing consent and Segment context flag - #10232

Open
gauthierpetetin wants to merge 5 commits into
mainfrom
feat/analytics-marketing-consent
Open

gauthierpetetin wants to merge 5 commits into
mainfrom
feat/analytics-marketing-consent

Conversation

@gauthierpetetin

Copy link
Copy Markdown
Contributor

Explanation

Product analytics (optedIn) and marketing analytics (optedInToMarketing) are independent consent lanes on AnalyticsController.

Named track / view events are classified first against the persisted marketingEventNames list. Marketing names are governed only by marketing consent. Everything else is governed only by product consent. Queues, fragments, and delivery share the same machinery for both lanes. identify has no name, so it stays on product consent.

At capture, named payloads stamp context.marketing (true or false) so Segment destinations can separate marketing from product without reading properties. Destinations should treat a missing flag as product (older clients).

Phase 1: there is no remote fetch yet. #fetchMarketingEventNames is a no-op stub. With an empty list, every named event is treated as product, so marketing consent APIs exist but do not change classification until a list is seeded or persisted.

References

  • Related to marketing consent / dual-lane analytics work (config-registry marketing-events fetch deferred to a later phase)

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Made with Cursor

…ment context flag

Classify named track/view events as marketing or product, gate each lane on its own consent, and stamp context.marketing for destinations. Phase 1 keeps marketingEventNames as a persisted/seeded list without a remote fetch.

Co-authored-by: Cursor <cursoragent@cursor.com>

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 15829c6. Configure here.

Comment thread packages/analytics-controller/src/AnalyticsController.ts
Regenerate messenger action types, fix formatting, link the changelog to
#10232, and classify fragments from event names only so caller
context.marketing cannot bypass the correct consent lane.

Co-authored-by: Cursor <cursoragent@cursor.com>

Copilot AI 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.

🟡 Changes recommended

Consent-lane handling issues remain for identify events, persisted fragments, and mixed-fragment emission.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds independent marketing consent handling and Segment context flags to AnalyticsController.

Changes:

  • Adds marketing consent state, selectors, APIs, and actions.
  • Classifies events and routes queues/fragments by consent lane.
  • Updates tests, documentation, and changelog.
File summaries
File Summary
packages/analytics-controller/src/selectors.ts Adds marketing consent selectors.
packages/analytics-controller/src/selectors.test.ts Tests the new selectors.
packages/analytics-controller/src/index.ts Exports new action types.
packages/analytics-controller/src/AnalyticsController.ts Implements consent lanes and event classification.
packages/analytics-controller/src/AnalyticsController.test.ts Tests marketing consent behavior and lane handling.
packages/analytics-controller/src/AnalyticsController-method-action-types.ts Defines marketing consent actions.
packages/analytics-controller/README.md Documents marketing consent and event classification.
packages/analytics-controller/CHANGELOG.md Records the new functionality.
Review details

Suppressed comments (3)

packages/analytics-controller/src/AnalyticsController.test.ts:5012

  • This test name says the context remains unset, but the assertion verifies that context.marketing: false is added. The contradictory name makes the intended backward-compatibility behavior unclear; rename it to describe the stamped context.
    it('keeps context unset when updating a persisted fragment that has none', async () => {

packages/analytics-controller/src/AnalyticsController.ts:809

  • The public init and #maybeResolveLocation documentation still describes geolocation as being deferred until optIn, but this new condition also starts it after optInToMarketing. Update those JSDoc blocks to document both consent lanes, since this changes when location data is requested.
    // Resolve geolocation only when the user is already opted in to product or
    // marketing analytics. For undecided or opted-out users it is deferred to
    // {@link optIn} / {@link optInToMarketing}. Awaited so that an already-opted-in
    // session has location available before events replay.

packages/analytics-controller/src/AnalyticsController.ts:1623

  • This admits a mixed fragment as the marketing lane, but #emitEventFragment still sends each declared name through trackEvent, which reclassifies it from the individual name. With product consent off and marketing consent on, a mixed fragment's product initialEvent is therefore silently dropped even though the changelog says mixed fragments are treated as marketing. Propagate the fragment lane through emission (or otherwise make the mixed-fragment policy consistent) so all of its lifecycle events are gated and stamped the same way.
    const captureAllowed = fragment
      ? this.#isCaptureAllowed(this.#laneFromFragment(fragment))
      : this.#isCaptureAllowed(AnalyticsLane.Product) ||
        this.#isCaptureAllowed(AnalyticsLane.Marketing);
  • Files reviewed: 8/8 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/analytics-controller/src/AnalyticsController.ts
Comment thread packages/analytics-controller/src/AnalyticsController.ts Outdated
gauthierpetetin and others added 3 commits September 15, 2026 07:06
Classify identify before trusting context.marketing so a caller-supplied
marketing stamp cannot retain identify across product opt-out.

Co-authored-by: Cursor <cursoragent@cursor.com>
…ng lanes

Keep persisted fragments on their capture-time lane when marketingEventNames
is missing or changed, while create still classifies and stamps from names.

Co-authored-by: Cursor <cursoragent@cursor.com>
Make #laneFromQueuedEvent exhaustive after the identify-first check so the
unreachable product fallback no longer breaks the coverage threshold.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants