Skip to content

feat(marketing): add PostHog analytics with AI-referral tagging - #42

Merged
rclanan merged 1 commit into
mainfrom
analytics/posthog-ai-referrals
Oct 19, 2026
Merged

rclanan merged 1 commit into
mainfrom
analytics/posthog-ai-referrals

Conversation

@rclanan

@rclanan rclanan commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

What this does

  • Adds public/analytics.js, loaded with <script src="/public/analytics.js" defer></script> from renderSharedHeadBlock() in scripts/build-pages.js, so all 53 generated pages include it. Redirect stubs don't include it because they navigate away immediately. The root HTML was regenerated with build-pages.js --force (one added line per page; --check is clean).
  • Loads posthog-js with the official loader stub from the US cloud (https://us-assets.i.posthog.com/static/array.js, API https://us.i.posthog.com), the same region the HelloSaaSy sites use.
  • Key injection: the committed file keeps POSTHOG_KEY = "__POSTHOG_KEY__", which keeps it inert (nothing is loaded or sent). When POSTHOG_KEY is set in the environment, npm run build:site writes it into dist/public/analytics.js after checking that it looks like phc_...; a malformed value fails the build. deploy-pages.yml passes secrets.POSTHOG_KEY to that step. The root fallback HTML and the repo never contain the key.
  • AI-referral tagging: classify(document.referrer, location) returns chatgpt | perplexity | claude | gemini | copilot | meta_ai | grok | deepseek | mistral | you | phind or null, from the referrer host (subdomains included; Copilot also via edgeservices.bing.com/edgesvc/chat) or utm_source (e.g. utm_source=chatgpt.com). Same-site referrers return null. On a match, once per tab session (sessionStorage guard in try/catch), it calls posthog.register({ai_referrer}) and captures ai_referral_landing with ai_engine, landing_path, referrer_host, utm_source, and $set_once: {initial_ai_referrer}. It also mirrors the event to gtag if GA ever appears; there is none today.
  • Privacy page (src/pages/privacy.html): it said "does not run analytics or tracking scripts". It now describes cookieless PostHog and the one sessionStorage key, and the "Last updated" date is bumped. Please review the wording.
  • npm test (node:test) is added and run in verify-pages-build.yml. README documents the setup.
  • No CSP exists on this site, so nothing to update there.

Consent and persistence choice

The site has no cookie banner, no GA, and sets no cookies. Its privacy page promised none. PostHog therefore runs cookieless (persistence: 'memory'): no cross-visit identifier, and each page load gets a fresh anonymous ID. To keep AI attribution across pages within one visit, the engine name is stored under one sessionStorage key (ai_referrer), cleared when the tab closes, and re-registered on later pages. Also set: disable_session_recording: true, disable_surveys: true, autocapture: { element_allowlist: ['a', 'button', 'form'] } (inputs are never autocaptured), and person_profiles: 'identified_only'.

Known limitation: legacy URLs that hit generated redirect stubs (e.g. /compare-wiremock.html to /vs/wiremock.html, /blog.html) drop the query string, and the destination sees a same-site referrer. AI visits that land on an old URL therefore count as plain pageviews, not ai_referral_landing. A follow-up could forward location.search in renderRedirectPage.

Conflicts with #41

None expected. This PR doesn't touch src/pages/index.html, src/pages/faq.html, or public/llms.txt content. The regenerated root index.html and faq.html gain one <head> line. git merge-tree against seo/llms-title-faq-schema merges cleanly. Whichever merges second should rerun npm run build:pages (or --force) if --check reports drift.

What Ray needs to do

  1. In PostHog (US cloud, https://us.posthog.com) create a project for mockforge.dev, or pick the one it should report to, and copy its Project API key (phc_...).
  2. Add it as a repository secret: Settings → Secrets and variables → Actions → New repository secret, name POSTHOG_KEY. Or run gh secret set POSTHOG_KEY -R SaaSy-Solutions/mockforge.dev.
  3. Review the privacy-page wording.
  4. Mark ready and merge. Deploy runs only on push to main (deploy-pages.yml); to turn analytics on without a code change later, re-run that workflow via workflow_dispatch after adding the secret.
  5. Optional: in PostHog, build an insight on ai_referral_landing broken down by ai_engine.

Verification

  • npm test: 10 passing. Covers every engine host and lookalikes (max.ai, notchatgpt.com, chatgpt.com.evil.example), utm_source, the same-site rule, placeholder inertness, and a simulated browser run with a test key (loader URL, init config, register/capture payloads, once-per-session guard, gtag mirror, blocked sessionStorage). It also runs a real build-pages.js <tmpdir> with POSTHOG_KEY=phc_buildtestkey: the key is injected into dist/public/analytics.js, the committed file keeps the placeholder, and every generated page includes the script exactly once. A malformed key fails the build.
  • npm run build:css && npm run build:site: built 53 pages; dist/index.html, dist/faq.html, and dist/vs/wiremock.html each include the script once. check:links --internal-only: no broken links. build-pages.js --check: in sync.
  • Deploy safety: deploy-pages.yml triggers only on push to main plus manual dispatch. verify-pages-build.yml and runner-guard.yml run on PRs but only build and check. Repo deployments show only github-pages from main.

🤖 Generated with Claude Code

Load public/analytics.js (deferred) from the shared head block so every
generated page gets it. It initializes PostHog cookieless, without
session recording or input autocapture, and tags visits referred by AI
assistants with an ai_referral_landing event and an ai_referrer super
property.

The committed file keeps an inert placeholder key; build:site injects
POSTHOG_KEY from the environment and the deploy workflow passes the
POSTHOG_KEY secret. The privacy page now describes the analytics, and
the verify workflow runs the new node:test suite.

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

codedig-ai Bot commented Oct 16, 2026

Copy link
Copy Markdown

🔴 CodeDig PR Analysis — FAILED; analysis max severity: high; shown blockers: 1 (high); budget: 11.225/100 points consumed, 88.775 points remaining (green)

The gate failed with blocking findings marked below. Budget status does not override the recorded gate verdict.

🤖 Summary

This PR is high risk (1 findings, 3 files changed).

Summary

Metric Value
Files Changed 3
Total Findings ⚠️ 1
Critical 0
High 🟠 1
Public API Changes 0
New PII Flows 0
Complexity Spikes ⚠️ 1
Co-change Risks 0

Coverage data: not available.

⚠️ Key Findings

  • [high] High complexity in public/analytics.js (blocking) — public/analytics.js

✅ Recommendations

  • Consider refactoring to reduce complexity. Extract methods, simplify conditionals, or use polymorphism to replace complex branching.
🧪 Test Coverage Delta (no data)

Coverage data not available for this repo — enable via Settings → Coverage

Detailed Findings

[high] High complexity in public/analytics.js

Cyclomatic complexity spike detected: public/analytics.js (complexity: 44, delta: +44)
Affected files: public/analytics.js
Recommendation: Consider refactoring to reduce complexity. Extract methods, simplify conditionals, or use polymorphism to replace complex branching.

→ View full report


Powered by CodeDig

@rclanan
rclanan marked this pull request as ready for review October 19, 2026 20:06
@rclanan
rclanan merged commit 905bc8f into main Oct 19, 2026
2 of 3 checks passed
@codedig-ai

codedig-ai Bot commented Oct 19, 2026

Copy link
Copy Markdown

🔴 CodeDig PR Analysis — FAILED; analysis max severity: high; shown blockers: 1 (high); budget: 11.225/100 points consumed, 88.775 points remaining (green)

The gate failed with blocking findings marked below. Budget status does not override the recorded gate verdict.

🤖 Summary

This PR is high risk (1 findings, 3 files changed).

Summary

Metric Value
Files Changed 3
Total Findings ⚠️ 1
Critical 0
High 🟠 1
Public API Changes 0
New PII Flows 0
Complexity Spikes ⚠️ 1
Co-change Risks 0

Coverage data: not available.

⚠️ Key Findings

  • [high] High complexity in public/analytics.js (blocking) — public/analytics.js

✅ Recommendations

  • Consider refactoring to reduce complexity. Extract methods, simplify conditionals, or use polymorphism to replace complex branching.
🧪 Test Coverage Delta (no data)

Coverage data not available for this repo — enable via Settings → Coverage

Detailed Findings

[high] High complexity in public/analytics.js

Cyclomatic complexity spike detected: public/analytics.js (complexity: 44, delta: +44)
Affected files: public/analytics.js
Recommendation: Consider refactoring to reduce complexity. Extract methods, simplify conditionals, or use polymorphism to replace complex branching.

→ View full report


Powered by CodeDig

@codedig-ai codedig-ai Bot added codedig/blast-low Blast radius low — ≤ 10 callers affected codedig/effort-1 Effort score 1/5 — minimal blast radius and complexity codedig/risk-green Risk budget < 50 % — safe to merge labels Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codedig/blast-low Blast radius low — ≤ 10 callers affected codedig/effort-1 Effort score 1/5 — minimal blast radius and complexity codedig/risk-green Risk budget < 50 % — safe to merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant