Skip to content

feat(analytics-controller): add marketingCampaignCookieId state field and setMarketingCampaignCookieId action - #10373

Merged
gauthierpetetin merged 3 commits into
mainfrom
gauthierpetetin/add-marketing-campaign-cookie-id-to-analytics-controller
Sep 23, 2026
Merged

gauthierpetetin merged 3 commits into
mainfrom
gauthierpetetin/add-marketing-campaign-cookie-id-to-analytics-controller

Conversation

@gauthierpetetin

@gauthierpetetin gauthierpetetin commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Adds marketingCampaignCookieId?: string | null to AnalyticsControllerState (persisted, backward-compatible optional field)
  • Adds setMarketingCampaignCookieId(id: string | null) method exposed via the messenger
  • Automatically clears the field to null when optOutOfMarketing or resetMarketingConsentDecision is called
  • Exports the new AnalyticsControllerSetMarketingCampaignCookieIdAction type

Motivation

marketingCampaignCookieId currently lives in MetaMetricsController in metamask-extension. Since AnalyticsController already owns all other marketing consent state (optedInToMarketing, marketingConsentDecisionMade), this field belongs here. Moving it to core also allows the auto-clear on opt-out to live in the controller itself, so clients no longer need to null it out manually after calling optOutOfMarketing or resetMarketingConsentDecision.

Test plan

  • yarn workspace @metamask/analytics-controller run test — all tests pass, 100% coverage maintained
  • yarn workspace @metamask/analytics-controller run build — no type errors

🤖 Generated with Claude Code


Note

Medium Risk
Introduces persisted marketing attribution data tied to consent clearing rules; clients must migrate from extension-local storage without mishandling opt-out semantics.

Overview
Adds persisted marketingCampaignCookieId to AnalyticsController so clients can store a marketing attribution identifier (e.g. a GA client ID) alongside existing marketing consent state, defaulting to null and remaining optional for older persisted state.

Exposes setMarketingCampaignCookieId(string | null) on the messenger (with a matching action type export). Marketing consent flows now reset the ID to null on optOutOfMarketing and resetMarketingConsentDecision; product analytics optOut / resetConsentDecision intentionally leave it unchanged. Tests and the package changelog cover defaults, persistence metadata, set/clear behavior, and the consent split.

Reviewed by Cursor Bugbot for commit c93969f. Bugbot is set up for automated code reviews on this repo. Configure here.

… and setMarketingCampaignCookieId action

Moves marketingCampaignCookieId from metamask-extension's MetaMetricsController
into AnalyticsController, where all other marketing consent state already lives.

The field is automatically cleared when optOutOfMarketing or
resetMarketingConsentDecision is called, so clients no longer need to
null it out manually after those calls.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
gauthierpetetin and others added 2 commits September 22, 2026 21:31
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tion types

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Add messenger action regression tests, including the null case.

Review effort: Lite
Findings: None

What changed in this PR

This PR adds persisted marketingCampaignCookieId state and a messenger setter, clearing the value during marketing consent opt-out/reset flows.

Changes:

  • Adds state, defaults, persistence metadata, and consent-clearing behavior.
  • Exposes the setter action and type.
  • Adds tests and changelog documentation.

The messenger-facing setter still needs regression coverage, including the null case.

File Summary
packages/​analytics-controller/​src/​index.ts Exports the new action type.
packages/​analytics-controller/​src/​AnalyticsController.ts Adds state, persistence, setter, and clearing behavior.
packages/​analytics-controller/​src/​AnalyticsController.test.ts Tests defaults, persistence, and consent behavior.
packages/​analytics-controller/​src/​AnalyticsController-method-action-types.ts Defines and unions the new messenger action.
packages/​analytics-controller/​CHANGELOG.md Documents the feature.

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

@mcmire mcmire left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, LGTM!

@NicolasMassart NicolasMassart left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The missing messenger-level test is a reasonable follow-up/non-blocking improvement, not enough to withhold approval given the implementation and existing direct coverage.

@gauthierpetetin
gauthierpetetin added this pull request to the merge queue Sep 23, 2026
Merged via the queue into main with commit 3f2fe03 Sep 23, 2026
156 checks passed
@gauthierpetetin
gauthierpetetin deleted the gauthierpetetin/add-marketing-campaign-cookie-id-to-analytics-controller branch September 23, 2026 10:54
@gauthierpetetin

Copy link
Copy Markdown
Contributor Author

@metamaskbot publish-preview

@gauthierpetetin

Copy link
Copy Markdown
Contributor Author

Missing test added in follow-up PR: #10398

pull Bot pushed a commit to Reality2byte/core that referenced this pull request Sep 23, 2026
… `setMarketingCampaignCookieId` (MetaMask#10398)

## Description

Follow-up to MetaMask#10373, addressing the [Copilot review
comment](MetaMask#10373 (review))
asking for messenger action regression tests for
`setMarketingCampaignCookieId`, including the `null` case.

The tests added in MetaMask#10373 call the controller method directly, which
would keep passing if the method were removed from
`MESSENGER_EXPOSED_METHODS` or the action type union drifted. This adds
two tests in the `setMarketingCampaignCookieId` describe block that
invoke `AnalyticsController:setMarketingCampaignCookieId` through the
messenger:

- with a value → state is updated
- with `null` → `marketingCampaignCookieId` is cleared

## Verification

- Full `@metamask/analytics-controller` test suite passes (100% coverage
maintained)
- Regression value confirmed: temporarily removing the action from
`MESSENGER_EXPOSED_METHODS` fails exactly these two tests while the
direct-call tests still pass
- `yarn lint:eslint` passes

## Checklist

- [x] Tests pass
- [x] Lint passes
- [ ] No changelog entry needed (test-only change)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Test-only change with no production code or runtime behavior
modifications.
> 
> **Overview**
> Adds **two regression tests** in the `setMarketingCampaignCookieId`
suite that exercise the
**`AnalyticsController:setMarketingCampaignCookieId` messenger action**
instead of calling the controller method directly.
> 
> One test asserts that invoking the action with a GA cookie ID updates
`marketingCampaignCookieId` in state; the other asserts that passing
**`null`** clears it. Together they guard against the action being
dropped from **`MESSENGER_EXPOSED_METHODS`** or action-type drift while
direct-call tests would still pass.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
be4d68b. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
pull Bot pushed a commit to Reality2byte/core that referenced this pull request Sep 23, 2026
## Explanation

Publish `@metamask/analytics-controller@3.2.0` so clients can consume
the additive changes that have accumulated on `main` since 3.1.0,
including the new `marketingCampaignCookieId` state field and
`setMarketingCampaignCookieId` messenger action
([MetaMask#10373](MetaMask#10373)).

Minor bump: the release only adds new, optional API surface — a new
state field initialized to `null` and a new messenger action — so it is
not breaking. The field is automatically cleared when the user opts out
of marketing or resets their marketing consent decision.

## References

- Add `marketingCampaignCookieId` state field and
`setMarketingCampaignCookieId` action to `AnalyticsController`:
MetaMask#10373
- Add messenger action regression tests for
`setMarketingCampaignCookieId`:
MetaMask#10398

## Checklist

- [x] I've updated the test suite for new or updated code as appropriate
- [x] I've updated documentation (JSDoc, Markdown, etc.) for new or
updated code as appropriate
- [x] I've communicated my changes to consumers by [updating changelogs
for packages I've
changed](https://github.com/MetaMask/core/tree/main/docs/processes/updating-changelogs.md)
- [ ] I've introduced [breaking
changes](https://github.com/MetaMask/core/tree/main/docs/processes/breaking-changes.md)
in this PR and have prepared draft pull requests for clients and
consumer packages to resolve them

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Release-only version and changelog updates with a minor,
backward-compatible analytics API bump; no runtime logic changes in this
PR.
> 
> **Overview**
> Cuts **monorepo release 1281.0.0** by publishing
**`@metamask/analytics-controller@3.2.0`** and wiring dependents to it.
> 
> The analytics-controller changelog documents **3.2.0** as additive
API: a **`marketingCampaignCookieId`** state field (default **`null`**)
and **`setMarketingCampaignCookieId`**, with the ID cleared on
**`optOutOfMarketing`** or **`resetMarketingConsentDecision`**.
**`network-controller`** and **`wallet-cli`** bump
**`@metamask/analytics-controller`** to **`^3.2.0`**; **`yarn.lock`**
and package version fields are updated accordingly. No application
source changes appear in this diff—only release metadata and dependency
pins.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
9e777df. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
pull Bot pushed a commit to Eric-Johnson-1/metamask-extension that referenced this pull request Sep 24, 2026
MetaMask#46600)

## **Description**

Moves ownership of the marketing campaign cookie id
(`marketingCampaignCookieId`) from `MetaMetricsController` to
`AnalyticsController`, which now supports the field as of
`@metamask/analytics-controller@3.2.0` (core PR MetaMask#10373).

**Context:** `AnalyticsController` already owns the marketing consent
state (`optedInToMarketing`, `marketingConsentDecisionMade`), and core
MetaMask#10373 added the cookie id plus a
`AnalyticsController:setMarketingCampaignCookieId` messenger action to
it, with controller-side auto-clear on `optOutOfMarketing` and
`resetMarketingConsentDecision`. This completes the migration on the
extension side and resolves [mcmire's comment on
MetaMask#46473](MetaMask#46473 (comment)):
the clearing is now a controller action invoked through the messenger,
and the manual clearing blocks are gone from `analytics.ts`.

**Solution:**
- Dependency bumped to stable `@metamask/analytics-controller@^3.2.0`
(no preview builds).
- Migration `229` moves the persisted cookie id value from
`MetaMetricsController` state to `AnalyticsController` state, preserving
both the string and the historically-supported numeric forms.
- `MetaMetricsController` no longer holds any state or exposed methods
(the controller shell remains).
- The platform adapter reads the cookie via
`AnalyticsController:getState`; `metamask-controller.js` writes via the
new controller action.
- `setParticipateInMetaMetrics` no longer clears the cookie at all, and
`setDataCollectionForMarketing` no longer clears it manually — the
controller handles clearing. **Accepted behavior change:** the cookie id
is retained after a full metrics opt-out / onboarding reset while
marketing consent remains on (core's `optOut`/`resetConsentDecision`
intentionally do not clear it).
- Flat-state type, Sentry-state flag, E2E fixtures/snapshots and unit
tests follow the new owner.

## **Changelog**

CHANGELOG entry: null

## **Related issues**

Fixes:

Depends on: MetaMask/core#10373 (shipped in
`@metamask/analytics-controller@3.2.0`)
Follow-up context:
MetaMask#46473 (comment)

## **Manual testing steps**

1. Enable the marketing consent toggle during onboarding (or via
Settings → Privacy → Data collection for marketing toggle).
2. Visit a website (e.g. metamask.io), open the inspector console, and
execute the following:

```
window.postMessage(
  {
    target: 'metamask-contentscript',
    data: {
      name: 'metamask-cookie-handler',
      data: {
        jsonrpc: '2.0',
        method: 'getCookieFromMarketingPage',
        params: [{ ga_client_id: 99999, origin: window.location.origin }],
        id: Date.now(),
        origin: window.location.origin,
      },
    },
  },
  window.location.origin,
);
```
3. Confirm AnalyticsController state has marketingCampaignCookieId set
to 99999 (check in the inspector).
4. Toggle "Data collection for marketing" off in Privacy Settings and
confirm marketingCampaignCookieId is cleared.
5. Do the same steps again with marketing consent toggle disabled during
onboarding and confirm marketingCampaignCookieId is not set.

<!--
## **Screenshots/Recordings**

### **Before**

### **After**
-->

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask
Extension Coding
Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md).
- [x] I've completed the PR template to the best of my ability
- [x] I’ve included tests if applicable
- [x] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [x] I’ve applied the right labels on the PR (see [labeling
guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)).
Not required for external contributors.

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches persisted analytics/marketing identity state and consent
clearing behavior across upgrades; migration 229 must run correctly for
existing users.
> 
> **Overview**
> **`marketingCampaignCookieId`** is moved from
**`MetaMetricsController`** to **`AnalyticsController`** (via
**`@metamask/analytics-controller@^3.2.0`**), with migration **229**
copying persisted string/numeric values and stripping the field from
**`MetaMetricsController`**, which is now an empty controller shell.
> 
> Reads and writes go through **`AnalyticsController:getState`** and
**`AnalyticsController:setMarketingCampaignCookieId`** (platform
adapter, marketing-page cookie handler, messenger delegation). Extension
helpers **`setParticipateInMetaMetrics`** /
**`setDataCollectionForMarketing`** no longer manually null the cookie;
clearing is delegated to core on marketing opt-out. **Opting out of
MetaMetrics alone no longer clears the cookie** (intentional behavior
change). Types, Sentry flags, fixtures, and tests follow the new owner.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
4ac3158. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
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.

4 participants