From b7bcb327385894748224a53fe44c3cb3dea28845 Mon Sep 17 00:00:00 2001 From: Erosika Date: Tue, 25 Aug 2026 16:44:37 -0400 Subject: [PATCH 1/5] feat(docs): load the GTM container on every docs page DEV-2465 step 1. Mintlify injects gtm.js on all docs pages; the container is audited to be inert on /docs before this merges, so the snippet loads and nothing fires. Cookiebot and GA4 arrive later as container publishes, consent first. Merging this publishes the docs within minutes, so it stays unmerged until Marc confirms the container audit. --- docs/docs.json | 74 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 57 insertions(+), 17 deletions(-) diff --git a/docs/docs.json b/docs/docs.json index b9d0e498..e0873acb 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -19,14 +19,21 @@ }, "favicon": "/favicon.svg", "contextual": { - "options": ["copy", "view", "chatgpt", "claude"] + "options": [ + "copy", + "view", + "chatgpt", + "claude" + ] }, "navigation": { "versions": [ { "version": "v3.1.0", "api": { - "openapi": ["v3/openapi.json"] + "openapi": [ + "v3/openapi.json" + ] }, "tabs": [ { @@ -90,7 +97,9 @@ "groups": [ { "group": "Overview", - "pages": ["v3/guides/overview"] + "pages": [ + "v3/guides/overview" + ] }, { "group": "Integrations", @@ -130,7 +139,9 @@ }, { "group": "Migrations", - "pages": ["v3/guides/migrations/mem0"] + "pages": [ + "v3/guides/migrations/mem0" + ] } ] }, @@ -160,7 +171,9 @@ "groups": [ { "group": "API Documentation", - "pages": ["v3/api-reference/introduction"] + "pages": [ + "v3/api-reference/introduction" + ] }, { "group": "workspaces", @@ -238,7 +251,9 @@ }, { "group": "miscellaneous", - "pages": ["v3/api-reference/endpoint/keys/create-key"] + "pages": [ + "v3/api-reference/endpoint/keys/create-key" + ] } ] }, @@ -259,7 +274,9 @@ { "version": "v2.5.1", "api": { - "openapi": ["v2/openapi.json"] + "openapi": [ + "v2/openapi.json" + ] }, "tabs": [ { @@ -306,11 +323,15 @@ "groups": [ { "group": "Getting Started", - "pages": ["v2/guides/overview"] + "pages": [ + "v2/guides/overview" + ] }, { "group": "Migrations", - "pages": ["v2/migrations/from-mem0"] + "pages": [ + "v2/migrations/from-mem0" + ] }, { "group": "Integrations", @@ -335,7 +356,9 @@ "groups": [ { "group": "API Documentation", - "pages": ["v2/api-reference/introduction"] + "pages": [ + "v2/api-reference/introduction" + ] }, { "group": "workspaces", @@ -439,7 +462,9 @@ { "version": "v1.1.0", "api": { - "openapi": ["openapi.json"] + "openapi": [ + "openapi.json" + ] }, "tabs": [ { @@ -469,15 +494,23 @@ "groups": [ { "group": "Getting Started", - "pages": ["v1/guides/overview", "v1/guides/streaming-response"] + "pages": [ + "v1/guides/overview", + "v1/guides/streaming-response" + ] }, { "group": "Application Interfaces", - "pages": ["v1/guides/discord", "v1/guides/honcho-mcp"] + "pages": [ + "v1/guides/discord", + "v1/guides/honcho-mcp" + ] }, { "group": "Personal Memory", - "pages": ["v1/guides/dialectic-endpoint"] + "pages": [ + "v1/guides/dialectic-endpoint" + ] } ] }, @@ -486,7 +519,9 @@ "groups": [ { "group": "API Documentation", - "pages": ["v1/api-reference/introduction"] + "pages": [ + "v1/api-reference/introduction" + ] }, { "group": "apps", @@ -534,7 +569,9 @@ }, { "group": "keys", - "pages": ["v1/api-reference/endpoint/keys/create-key"] + "pages": [ + "v1/api-reference/endpoint/keys/create-key" + ] }, { "group": "metamessages", @@ -595,6 +632,9 @@ "integrations": { "posthog": { "apiKey": "phc_1yrzzcgywqXGcerkkI4g7C0YfyPMcAKNOOvGcjTCiUk" + }, + "gtm": { + "tagId": "GTM-NSPT9PJF" } } -} +} \ No newline at end of file From 0b9ae0017009af27e662356407d8ed9c565cbb4c Mon Sep 17 00:00:00 2001 From: Erosika Date: Tue, 25 Aug 2026 16:45:18 -0400 Subject: [PATCH 2/5] feat(docs): PostHog loads only with a granting consent answer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DEV-2465 open question 5, option d. Mintlify's built-in integration loaded PostHog unconditionally on all 249 docs pages — a visitor who declined on the homepage was tracked one click later in the docs. The integration key comes out of docs.json; docs/posthog-consent.js loads PostHog directly instead, only when the CookieConsent cookie grants Statistics (or holds Cookiebot's -1 marker), and listens for the consent events so a grant on the docs banner itself loads it too. Trade recorded on the ticket: this bypasses the ph.mintlify.com proxy, so ad blockers reduce docs PostHog volume. Verify after deploy that Mintlify's page CSP allows us-assets.i.posthog.com; if it blocks, fall back to option c. --- docs/docs.json | 3 --- docs/posthog-consent.js | 43 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 docs/posthog-consent.js diff --git a/docs/docs.json b/docs/docs.json index e0873acb..b1dad2ba 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -630,9 +630,6 @@ } }, "integrations": { - "posthog": { - "apiKey": "phc_1yrzzcgywqXGcerkkI4g7C0YfyPMcAKNOOvGcjTCiUk" - }, "gtm": { "tagId": "GTM-NSPT9PJF" } diff --git a/docs/posthog-consent.js b/docs/posthog-consent.js new file mode 100644 index 00000000..214c6b1c --- /dev/null +++ b/docs/posthog-consent.js @@ -0,0 +1,43 @@ +// Loads PostHog only when the CookieConsent cookie grants Statistics; the +// cookie is host-scoped, so a landing-page answer covers the docs. +;(function () { + var KEY = 'phc_1yrzzcgywqXGcerkkI4g7C0YfyPMcAKNOOvGcjTCiUk' + var loaded = false + + function granted() { + var m = document.cookie.match(/CookieConsent=([^;]*)/) + if (!m) return false + var v = decodeURIComponent(m[1]) + // "-1" is Cookiebot's consent-not-required marker. + return v === '-1' || /statistics\s*:\s*true/.test(v) + } + + function loadPosthog() { + if (loaded) return + loaded = true + var s = document.createElement('script') + s.src = 'https://us-assets.i.posthog.com/static/array.js' + s.async = true + s.onload = function () { + window.posthog.init(KEY, { + api_host: 'https://us.i.posthog.com', + ui_host: 'https://us.posthog.com', + cross_subdomain_cookie: true, + person_profiles: 'identified_only', + }) + } + document.head.appendChild(s) + } + + if (granted()) { + loadPosthog() + return + } + // A grant made on the docs banner itself (step 2) loads it live. + var events = ['CookiebotOnConsentReady', 'CookiebotOnAccept'] + for (var i = 0; i < events.length; i++) { + window.addEventListener(events[i], function () { + if (granted()) loadPosthog() + }) + } +})() From b5d1a1ae540774e78a48cd66269c9a2cb5719c03 Mon Sep 17 00:00:00 2001 From: Erosika Date: Wed, 26 Aug 2026 10:25:26 -0400 Subject: [PATCH 3/5] fix(docs): loader survives a failed fetch and honors withdrawal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review findings on #1071. The cookie match now requires the exact CookieConsent name boundary. A failed array.js request resets the loaded flag so later consent events retry. And consent events now run a full sync: withdrawal opts an already running instance out, and a re-grant opts it back in — same behavior as the landing site's gate. --- docs/posthog-consent.js | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/docs/posthog-consent.js b/docs/posthog-consent.js index 214c6b1c..3e93da82 100644 --- a/docs/posthog-consent.js +++ b/docs/posthog-consent.js @@ -5,7 +5,7 @@ var loaded = false function granted() { - var m = document.cookie.match(/CookieConsent=([^;]*)/) + var m = document.cookie.match(/(?:^|;\s*)CookieConsent=([^;]*)/) if (!m) return false var v = decodeURIComponent(m[1]) // "-1" is Cookiebot's consent-not-required marker. @@ -18,6 +18,9 @@ var s = document.createElement('script') s.src = 'https://us-assets.i.posthog.com/static/array.js' s.async = true + s.onerror = function () { + loaded = false + } s.onload = function () { window.posthog.init(KEY, { api_host: 'https://us.i.posthog.com', @@ -29,15 +32,32 @@ document.head.appendChild(s) } - if (granted()) { - loadPosthog() - return + function sync() { + if (granted()) { + if (!loaded) { + loadPosthog() + } else if ( + window.posthog && + window.posthog.has_opted_out_capturing && + window.posthog.has_opted_out_capturing() + ) { + window.posthog.opt_in_capturing() + } + return + } + // Withdrawal mid-session: an already running instance must stop. + if (loaded && window.posthog && window.posthog.opt_out_capturing) { + window.posthog.opt_out_capturing() + } } - // A grant made on the docs banner itself (step 2) loads it live. - var events = ['CookiebotOnConsentReady', 'CookiebotOnAccept'] + + sync() + var events = [ + 'CookiebotOnConsentReady', + 'CookiebotOnAccept', + 'CookiebotOnDecline', + ] for (var i = 0; i < events.length; i++) { - window.addEventListener(events[i], function () { - if (granted()) loadPosthog() - }) + window.addEventListener(events[i], sync) } })() From f3db11ef3a05671a6e1c4779cb2a85e105152d56 Mon Sep 17 00:00:00 2001 From: Erosika Date: Thu, 27 Aug 2026 10:53:10 -0400 Subject: [PATCH 4/5] chore(docs): undo array reformatting in docs.json The integrations change is the only intended edit. The one-item arrays go back to their single-line form and the trailing newline returns. --- docs/docs.json | 71 ++++++++++++-------------------------------------- 1 file changed, 17 insertions(+), 54 deletions(-) diff --git a/docs/docs.json b/docs/docs.json index b1dad2ba..12b5fe01 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -19,21 +19,14 @@ }, "favicon": "/favicon.svg", "contextual": { - "options": [ - "copy", - "view", - "chatgpt", - "claude" - ] + "options": ["copy", "view", "chatgpt", "claude"] }, "navigation": { "versions": [ { "version": "v3.1.0", "api": { - "openapi": [ - "v3/openapi.json" - ] + "openapi": ["v3/openapi.json"] }, "tabs": [ { @@ -97,9 +90,7 @@ "groups": [ { "group": "Overview", - "pages": [ - "v3/guides/overview" - ] + "pages": ["v3/guides/overview"] }, { "group": "Integrations", @@ -139,9 +130,7 @@ }, { "group": "Migrations", - "pages": [ - "v3/guides/migrations/mem0" - ] + "pages": ["v3/guides/migrations/mem0"] } ] }, @@ -171,9 +160,7 @@ "groups": [ { "group": "API Documentation", - "pages": [ - "v3/api-reference/introduction" - ] + "pages": ["v3/api-reference/introduction"] }, { "group": "workspaces", @@ -251,9 +238,7 @@ }, { "group": "miscellaneous", - "pages": [ - "v3/api-reference/endpoint/keys/create-key" - ] + "pages": ["v3/api-reference/endpoint/keys/create-key"] } ] }, @@ -274,9 +259,7 @@ { "version": "v2.5.1", "api": { - "openapi": [ - "v2/openapi.json" - ] + "openapi": ["v2/openapi.json"] }, "tabs": [ { @@ -323,15 +306,11 @@ "groups": [ { "group": "Getting Started", - "pages": [ - "v2/guides/overview" - ] + "pages": ["v2/guides/overview"] }, { "group": "Migrations", - "pages": [ - "v2/migrations/from-mem0" - ] + "pages": ["v2/migrations/from-mem0"] }, { "group": "Integrations", @@ -356,9 +335,7 @@ "groups": [ { "group": "API Documentation", - "pages": [ - "v2/api-reference/introduction" - ] + "pages": ["v2/api-reference/introduction"] }, { "group": "workspaces", @@ -462,9 +439,7 @@ { "version": "v1.1.0", "api": { - "openapi": [ - "openapi.json" - ] + "openapi": ["openapi.json"] }, "tabs": [ { @@ -494,23 +469,15 @@ "groups": [ { "group": "Getting Started", - "pages": [ - "v1/guides/overview", - "v1/guides/streaming-response" - ] + "pages": ["v1/guides/overview", "v1/guides/streaming-response"] }, { "group": "Application Interfaces", - "pages": [ - "v1/guides/discord", - "v1/guides/honcho-mcp" - ] + "pages": ["v1/guides/discord", "v1/guides/honcho-mcp"] }, { "group": "Personal Memory", - "pages": [ - "v1/guides/dialectic-endpoint" - ] + "pages": ["v1/guides/dialectic-endpoint"] } ] }, @@ -519,9 +486,7 @@ "groups": [ { "group": "API Documentation", - "pages": [ - "v1/api-reference/introduction" - ] + "pages": ["v1/api-reference/introduction"] }, { "group": "apps", @@ -569,9 +534,7 @@ }, { "group": "keys", - "pages": [ - "v1/api-reference/endpoint/keys/create-key" - ] + "pages": ["v1/api-reference/endpoint/keys/create-key"] }, { "group": "metamessages", @@ -634,4 +597,4 @@ "tagId": "GTM-NSPT9PJF" } } -} \ No newline at end of file +} From 86f8eb3e6ddae9c79592c794f574377b474e3b96 Mon Sep 17 00:00:00 2001 From: Erosika Date: Thu, 27 Aug 2026 10:53:10 -0400 Subject: [PATCH 5/5] fix(docs): loader re-checks consent before init and captures SPA pageviews If consent is withdrawn while array.js downloads, sync() runs before window.posthog exists and the opt-out is skipped. onload now re-checks granted() and resets loaded so a later re-grant retries. Mintlify swaps pages without a reload, so capture_pageview: 'history_change' records navigation past the landing page. --- docs/posthog-consent.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/posthog-consent.js b/docs/posthog-consent.js index 3e93da82..acb47860 100644 --- a/docs/posthog-consent.js +++ b/docs/posthog-consent.js @@ -22,11 +22,17 @@ loaded = false } s.onload = function () { + // Consent withdrawn while array.js was downloading: skip init, allow a retry on re-grant. + if (!granted()) { + loaded = false + return + } window.posthog.init(KEY, { api_host: 'https://us.i.posthog.com', ui_host: 'https://us.posthog.com', cross_subdomain_cookie: true, person_profiles: 'identified_only', + capture_pageview: 'history_change', }) } document.head.appendChild(s)