Next: Nothing. The site is active.
claude "/cro-onboard Westbound Storage Group" runs the whole playbook.The agent runs on the site's cadence; proposals land in the queue.
consent.catchdigital.io/v1/consent.js in <head> on 5 of 5 pages
CRO Engine · Oct 16, 2:04 PM
phc_northgate… on 5 of 5 sampled pages
CRO Engine · Oct 16, 2:04 PM
pageviews in the last 24 hours
CRO Engine · Oct 16, 2:04 PM
7.3.0 on the live page, matches the manifest
CRO Engine · Oct 16, 2:04 PM
input masking on; replay sampled at 10%
CRO Engine · Oct 16, 2:04 PM
Generated for this site, so the pastes are always the current versions with this site's token. A person lands them: a PR on the repo, or custom code in Webflow.
Where: app/layout.tsx, inside <head> (a plain <script>, not next/script afterInteractive)
<script src="https://consent.catchdigital.io/v1/consent.js" defer></script>
First on any site that will get session replay or ad signals. The banner gates PostHog and tracking.js through the tg-tier contract. Run /consent-setup if the site has no catch-consent yet.
Where: app/layout.tsx, inside <head> (a plain <script>, not next/script afterInteractive)
<script>
// PostHog for Catch Website OS. Waits for the consent tier (catch-consent sets window.TG_TIER and
// TG_TIER_RESOLVED, then fires tg-tier-resolved on document with the tier string as detail). Strict
// tier: no cookies, no replay. Every input is masked in replay.
(function () {
var TOKEN = "phc_northgate41208", HOST = "https://us.i.posthog.com";
function start(tier) {
if (window.__cdPosthogStarted) return; window.__cdPosthogStarted = true;
var strict = tier === "strict", startMs = Math.round(window.performance ? performance.now() : 0);
!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.crossOrigin="anonymous",p.async=!0,p.src=s.api_host.replace(".i.posthog.com","-assets.i.posthog.com")+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="init capture register register_once unregister opt_out_capturing opt_in_capturing get_distinct_id get_session_id identify alias reset group onFeatureFlags getFeatureFlag".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
posthog.init(TOKEN, {
api_host: HOST,
persistence: strict ? "memory" : "localStorage+cookie",
disable_session_recording: strict,
session_recording: { maskAllInputs: true, maskTextSelector: "[data-ph-mask]" },
capture_pageview: true,
autocapture: false,
// The capture check: the tier this page ran under and how long after page load PostHog started.
before_send: function (e) { if (e && e.properties) { e.properties.cd_tier = tier; e.properties.cd_start_ms = startMs; } return e; }
});
}
// A "strict" set before the tier resolves is the fail-safe, not an answer (the legacy inline tier
// snippet sets it first): wait for the event unless consent.js says it already resolved.
if (window.TG_TIER_RESOLVED || (window.TG_TIER && window.TG_TIER !== "strict")) start(window.TG_TIER);
else document.addEventListener("tg-tier-resolved", function (e) { start((e && typeof e.detail === "string" ? e.detail : null) || window.TG_TIER || "strict"); });
setTimeout(function () { if (!window.__cdPosthogStarted) start(window.TG_TIER || "strict"); }, 4000); // no consent banner on the page: strict mode
})();
</script>unit_reserved fired on a test submit from 2 of 2 forms
CRO Engine · Oct 16, 2:04 PM
test lead received with ph_distinct_id and event_id
CRO Engine · Oct 16, 2:04 PM
distinct id resolves to a PostHog person with 1 session
CRO Engine · Oct 16, 2:04 PM
Catch's GitHub App can open PRs
CRO Engine · Oct 16, 2:04 PM
1 project per site. The snippet waits for the consent tier, runs cookieless with no replay on strict, masks every input, and never changes project settings. Replay sampling and billing limits are set on the project by hand (hard rule 5).
Where: app/layout.tsx, before </body>, as a plain inline <script>
<script>window.CatchDigitalConfig = { eventName: "unit_reserved" };</script>
<!-- paste utm-script/tracking.js 7.3.0 here, byte for byte, inside <script> tags -->A native paste, no hosted file. 7.3.0 adds ph_distinct_id, ph_session_id and event_id as hidden fields and captures the conversion in PostHog with the same event_id. When a new version ships, check C4 flags every site that is behind and this plan carries the new paste.
Where: Basin: the form's Webhooks setting
https://demo.os.catchdigital.io/api/ingest/cro_live_87tyw0gx3ezmi3bt
Pasted by a person. Add a webhook with this URL, format JSON (original, not wrapped). Turn off the duplicate-email spam rule on forms that submit more than once per session. The same URL accepts Basin, Webflow, Typeform, HubSpot and flat JSON; the shape is detected per submission.
Where: the repo github.com/CatchDigitalInc/northgate-storage
Install Catch's GitHub App on the repo so variants can go up as PRs behind a flag. Not needed for learning.
Installed Oct 16, 2025 via PR (github.com/CatchDigitalInc/northgate-storage/pull/12), tracking.js 7.3.0.