Skip to content

feat(consent): CMP adapters for Cookiebot, OneTrust, and Complianz - #41

Merged
Atroci merged 1 commit into
masterfrom
feat/consent-cmp-adapters
Sep 29, 2026
Merged

Atroci merged 1 commit into
masterfrom
feat/consent-cmp-adapters

Conversation

@Atroci

@Atroci Atroci commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Part of #39 (adapters; the cmp-booking-handoff example follows in clicktrail-examples). Implements specs/003-cmp-adapters (spec in #40).

Acceptance criterion

@vizuh/clicktrail-consent/cmp connects Cookiebot, OneTrust and Complianz to a ConsentHub:

  • the landing-page click survives a Cookiebot wipe that happens before its accept event;
  • decline and in-page withdrawal emit denied;
  • the import has no side effects without a window.

Design

  • An injectable target (window-like) seam, with no DOM access at import. sideEffects: false still holds.
  • state follows marketing consent, because storageAllowed() checks only state. This matches the WordPress bridge.
  • Deduplication per decision. The adapters stay subscribed (no once) and return disposers.
  • Cookiebot: emits only once hasResponse is set.
  • OneTrust: exact group matching (C00040 does not match C0004). Listens to OneTrustGroupsUpdated as well as the OptanonWrapper wrap.
  • Complianz: cmplz_has_consent(), falling back to event.detail.categories, per the Complianz developer guide.

Review notes

/code-review raised 4 findings. Fixed:

  • the install snippet overwriting OptanonWrapper silenced the adapter; it now also uses OneTrustGroupsUpdated;
  • dispose leaked when another wrapper sat on top; a disposed flag fixes it.

Documented rather than changed: OneTrust and Complianz report the CMP's effective state before an answer. With an opt-in banner that's denied, which is harmless. In an opt-out region the CMP itself configures, it's granted, the same state the CMP uses for its own tags, and the same as the WordPress bridge. The README states this. The spec's FR-004 wording ("never emit while pending") holds strictly only for Cookiebot, and #40 should be amended to match.

Validation

  • packages/consent: 19 tests (13 new) cover:
    • wipe then accept;
    • decline;
    • existing response at connect, then in-page withdrawal;
    • analytics-only consent does not unlock storage;
    • dispose;
    • OneTrust exact groups, host wrapper, snippet overwrite and wrapped dispose;
    • Complianz categories, cmplz_has_consent withdrawal and banner-open;
    • SSR no-op.
  • Full workspace: pnpm -r test green, pnpm typecheck clean, and verify:workspace passes for 23 packages.
  • Size: the cmp dist is 1,801 B gzip unminified, an upper bound against the 2 KB budget. The repo has no size tool, so none was added.
  • Built dist/cmp/index.js imports in Node without a window.
  • Not published. It ships with the next RC wave.

🤖 Generated with Claude Code

Implements specs/003-cmp-adapters (#39).

@vizuh/clicktrail-consent/cmp exports connectCookiebot, connectOneTrust,
and connectComplianz. Each forwards the CMP's effective decision to a
ConsentHub, deduplicates repeated events, stays subscribed so in-page
withdrawals emit denied, and returns a disposer. No DOM access at import;
a window-like target can be injected for tests.

- Cookiebot: emits only once hasResponse is set; listens to
  OnConsentReady/OnAccept/OnDecline (it may wipe storage before them).
- OneTrust: exact group matching; OneTrustGroupsUpdated plus a wrapped
  OptanonWrapper, so a later snippet reassignment cannot silence it.
- Complianz: cmplz_has_consent() or event.detail.categories on
  cmplz_fire_categories / cmplz_status_change.
- state follows marketing consent because storageAllowed() checks only
  state, matching the WordPress consent bridge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7de0f6284a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if (typeof hasConsent === 'function') {
marketing = !!hasConsent('marketing');
analytics = !!hasConsent('statistics');
} else if (Array.isArray(categories)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Handle category-only Complianz status changes

When cmplz_has_consent is unavailable—the explicitly supported fallback case—Complianz's later cmplz_status_change events provide the changed detail.category and detail.value, rather than a detail.categories array. After an initial cmplz_fire_categories grant, a subsequent marketing withdrawal therefore skips this branch and returns without notifying the hub, leaving stored attribution uncleared. Preserve the fallback category state and apply these category/value updates.

Useful? React with 👍 / 👎.

@Atroci
Atroci merged commit d3a380a into master Sep 29, 2026
12 checks passed
Atroci added a commit that referenced this pull request Sep 29, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Atroci added a commit that referenced this pull request Sep 29, 2026
* docs(specs): CMP adapters for @vizuh/clicktrail-consent

Spec-kit spec, plan and tasks for #39.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(specs): amend FR-004 to effective-decision semantics

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(specs): mark 003 status and tasks against #41

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant