Skip to content

fix(#399): site/ — GA4 + consent banner on all 4 pages#400

Merged
atlas-apex merged 2 commits into
mainfrom
fix/GH-399-ga4-all-pages
May 24, 2026
Merged

fix(#399): site/ — GA4 + consent banner on all 4 pages#400
atlas-apex merged 2 commits into
mainfrom
fix/GH-399-ga4-all-pages

Conversation

@atlas-apex

Copy link
Copy Markdown
Collaborator

Summary

  • GA4 now fires on all 4 site pagesgtag.js + Consent Mode v2 default block copied verbatim from index.html into how-it-works.html, architecture.html, and skills.html. Pre-fix, only / was tracked; any visit landing directly on the 3 other pages (Twitter/LinkedIn shares from the v2.0 launch, search results, LLM citations from llms.txt which explicitly lists all 4) was invisible to GA4 and — worse — got no consent UI at all (a GDPR gap, not just a measurement gap).
  • Cookie consent banner now present on all 4 pages with the same Accept/Decline/Escape flow and localStorage.ay-consent persistence. A user who lands on /how-it-works first now gets the consent choice on that page, and the choice is honoured site-wide on subsequent navigation (same localStorage key).
  • Markers added for future sync — every copy/paste'd block is wrapped in <!-- begin: gtag --> / <!-- end: gtag -->, <!-- begin: consent --> / <!-- end: consent -->, and /* begin: consent-css */ / /* end: consent-css */. Static-HTML site has no build step, so a shared partial doesn't exist; the markers make it greppable when the GA4 ID, Consent Mode config, or banner copy needs to change in one place.
  • Dead-config cleanup — removed 'anonymize_ip': true from gtag('config', ...). This was a Universal Analytics flag that truncated the last octet of the IP; in GA4 all IPs are auto-anonymized by default and the parameter is a no-op. Removing it kills dead config without changing behaviour.

Testing

  • Coverage check shows parity across all 4 pages:
    site/index.html:        gtag-refs=6  google-refs=1  consent-banner=1  consent-css=1  begin-markers=3
    site/how-it-works.html: gtag-refs=6  google-refs=1  consent-banner=1  consent-css=1  begin-markers=3
    site/architecture.html: gtag-refs=6  google-refs=1  consent-banner=1  consent-css=1  begin-markers=3
    site/skills.html:       gtag-refs=6  google-refs=1  consent-banner=1  consent-css=1  begin-markers=3
    
  • grep anonymize_ip site/*.html returns 0 hits across the board (dead config removed)
  • No framework code changed — git diff --name-only origin/main..HEAD returns exactly 4 site files; no scope leak into hooks / skills / rules / agents
  • CSP allowlist already covers GA4 endpoints (script-src 'self' 'unsafe-inline' https://www.googletagmanager.com + img-src ... https://www.google-analytics.com + connect-src ... https://analytics.google.com); no CSP changes needed
  • Post-merge Netlify deploy preview will surface from this PR — visual check on each of the 4 pages that consent banner renders correctly and Accept flow grants analytics_storage
  • Post-deploy: GA4 Realtime should show traffic on /how-it-works, /architecture, /skills (currently flat-zero on those paths)

AgDR note

<!-- agdr: not-applicable --> marker present at the top: this PR is a pure HTML/CSS/JS copy-paste with no architectural decisions, no library choices, no new dependencies. The framework's existing Consent Mode v2 implementation is being applied to additional pages, not changed.

Leak-protection skip note

<!-- private-refs: allow --> marker present at the top: the body legitimately references yard.apexscript.com, the framework's own public marketing domain. The leak-protection hook trips on the apexscript token because of name-collision with a registered project (a recurring false-positive — same exception as the v2.0.0 launch PRs).

Out of scope (deliberately deferred)

  • Apexyard-owned privacy policy page. The consent banner currently links to policies.google.com/privacy (Google's policy). For full GDPR compliance the site should host its own privacy policy explaining what data the framework's marketing site collects, who it shares with, and how to request deletion. Separate ticket — out of scope for an analytics-coverage fix.
  • Self-hosted analytics (Plausible, Umami, etc.). GA4 is what's wired; swapping is a separate strategic call.
  • Auto-sync linter that fails CI when the marker-bounded blocks drift across pages. Worth considering once we have a second class of cross-page block; YAGNI for one block right now.

Glossary

Term Definition
GA4 Google Analytics 4 — Google's current analytics product. Identified by measurement ID G-G3EMR3CB02 for this site.
Consent Mode v2 Google's framework for declaring user-consent state (denied / granted) before any tracking fires. Required for sites serving EU traffic post-DMA.
analytics_storage The Consent Mode v2 axis controlling whether GA4 may set cookies and send events. Default denied; flipped to granted after the user clicks Accept.
anonymize_ip: true A Universal Analytics flag that truncated the last octet of the visitor's IP. No-op in GA4 (all IPs are auto-anonymized by default). Removed in this PR to kill dead config.
localStorage.ay-consent Per-domain persistence of the user's consent choice (granted / denied). One key for the whole site, so a choice made on one page is honoured on all subsequent navigation.
begin/end markers HTML comment / CSS comment pairs (<!-- begin: gtag --> ... <!-- end: gtag --> etc.) wrapping each cross-page block. Greppable anchors for future sync edits — no build step to do it automatically.
Cherry-pick release path Branch off upstream/main (NOT dev), commit the fix, PR to main. Avoids the v1.4→v2.0 squash divergence on dev. Same pattern as the v2.0.1 mobile UX fix (PR #396).

Closes #399

- Copy gtag.js + Consent Mode v2 init + cookie consent banner
  from index.html to how-it-works.html, architecture.html, skills.html.
- Wrap each block with begin/end markers so future sync edits
  are greppable across pages.
- Remove dead `anonymize_ip: true` from gtag config (no-op in GA4
  — all IPs auto-anonymized; carryover from Universal Analytics).

Closes #399
@netlify

netlify Bot commented May 24, 2026

Copy link
Copy Markdown

Deploy Preview for apexyard ready!

Name Link
🔨 Latest commit 399f605
🔍 Latest deploy log https://app.netlify.com/projects/apexyard/deploys/6a132d042598f5000890c176
😎 Deploy Preview https://deploy-preview-400--apexyard.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@atlas-apex atlas-apex left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Code Review: PR #400

Commit: e7ceb2eb47b4ff6c2b7cdb58817d04a2b5898294

(Submitting as --comment because GitHub blocks --request-changes on self-authored PRs. Treat as a blocker; the merge gate should refuse anyway because CI is red — see below.)

Summary

Adds GA4 + Consent Mode v2 + cookie consent banner to the three site pages that were missing them (how-it-works.html, architecture.html, skills.html), bringing them to parity with index.html. Also removes the dead anonymize_ip: true UA-era flag from the existing index.html gtag config. Cherry-pick-style release branch off main, single commit, 4 files touched, zero framework code.

Checklist Results

  • Architecture & Design: Pass (HTML/CSS/JS copy-paste; no architecture surface)
  • Code Quality: Pass (byte-identical blocks across 4 pages; markers paired)
  • Testing: Partial (deploy-preview smoke check listed as a post-merge step, which is correct for a Netlify-driven workflow)
  • Security: Pass (Consent Mode v2 ordering correct: default-denied → localStorage replay → config; no secrets; CSP already covers GA4 endpoints per PR body)
  • Performance: Pass (script is async; one extra script tag per page)
  • PR Description & Glossary: Pass (4 narrative summary bullets, 7-term glossary, both skip markers justified inline)
  • Summary Bullet Narrative: Pass (every bullet has verb + rationale)
  • Technical Decisions (AgDR): N/A (pure copy-paste of an existing pattern; <!-- agdr: not-applicable --> marker present and justified)
  • Adopter Handbooks: N/A (migration-safety is blocking but doesn't trigger — no migration files; clean-architecture-layers and commit-message-quality are advisory and clean)

Issues Found

BLOCKER — Red CI: site-counts drift detection failed

The <meta name="llm:*"> token-count / doc-length tags on two of the three newly-instrumented pages weren't refreshed to reflect the +126 lines of GA4 + consent code added by this PR. Per .claude/rules/pr-quality.md § "No Red CI Before Merge", red CI blocks merge regardless of cause.

CI output (https://github.com/me2resh/apexyard/actions/runs/26367005290/job/77612431706):

DRIFT: site/architecture.html — meta token-count=8864 chars=35458
       vs actual tokens=10201 chars=40807 (diff: 13% tokens / 13% chars; >5% tolerance)
DRIFT: site/skills.html — meta token-count=10658 chars=42632
       vs actual tokens=11920 chars=47681 (diff: 10% tokens / 10% chars; >5% tolerance)

Required updates (recomputed locally with wc -c < <file>; tokens = chars / 4):

File Current meta Required meta
site/architecture.html line 22 content="8864" content="10201"
site/architecture.html line 23 content="35458 chars" content="40807 chars"
site/skills.html line 22 content="10658" content="11920"
site/skills.html line 23 content="42632 chars" content="47681 chars"

After the edit, re-running bash .claude/hooks/tests/test_site_counts.sh locally should report all three drift-checked pages within tolerance. Note: adding the meta refresh will itself nudge chars/tokens by ~10 bytes — well inside the 5% tolerance.

Handbook Findings

None. The migration-safety handbook (blocking) doesn't trigger because no migration files are touched. The clean-architecture-layers and commit-message-quality handbooks (advisory) have nothing to flag.

Suggestions (non-blocking — for follow-up)

Drift-check scope coverage: the LLM-meta loop in .claude/hooks/tests/test_site_counts.sh:230 iterates over index.html, architecture.html, skills.html only — it doesn't cover site/how-it-works.html. Locally that file now drifts by ~22% (actual 8065 tok / 32263 chars vs meta 6572 tok / 26291 chars) but CI doesn't fail because the loop skips it. Worth a follow-up framework ticket to add how-it-works.html to the loop (and refresh its meta in the process), so a future PR that touches only how-it-works.html will get the same drift catch. Out of scope here.

Per-page coverage parity verification: PR body's coverage table shows gtag-refs=6 per page. I see 2 explicit G-G3EMR3CB02 references per page (script src + config). The "6" likely counts function calls including gtag('consent', 'default', ...), the localStorage replay update, gtag('js', ...), gtag('config', ...), the gtag('consent', 'update', ...) inside the banner IIFE, and the function gtag(){...} definition. Cross-page byte-identity confirmed via diff of the three marker-bounded blocks against index.html — all three blocks (gtag, consent, consent-css) are identical across all 4 pages.

Verdict

REQUEST CHANGES (submitted as COMMENT due to GitHub self-PR restriction) — red CI on site-counts drift detection. Refresh the four meta-tag values listed above, push, CI should go green. Everything else in the diff is clean.


Reviewed by Rex (Code Reviewer Agent)
Reviewed commit: e7ceb2eb47b4ff6c2b7cdb58817d04a2b5898294

The GA4 + consent block additions in the prior commit grew each page
by ~5–6K chars. Refreshing the per-page LLM payload-size meta tags
on all 4 site files (not just the 2 the test_site_counts.sh loop
scans) so token-budget-aware clients see accurate costs.

- index.html:        23189 → 23697 tok / 92757 → 94791 chars
- how-it-works.html:  6572 →  8065 tok / 26291 → 32263 chars
- architecture.html:  8864 → 10201 tok / 35458 → 40807 chars
- skills.html:       10658 → 11920 tok / 42632 → 47681 chars

Verified: test_site_counts.sh PASS — all scanned files within 5% of
actual.

Refs #399

@atlas-apex atlas-apex left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Re-review: PR #400

Commit: 399f605c00dba3d99b766681d016c17d7742254b

Summary

Fixup commit 399f605 addresses the sole blocker from the prior REQUEST_CHANGES (the red site-counts drift detection CI). The operator went beyond the minimal fix — refreshing meta tags on all 4 site pages, not just the 2 the loop currently scans. CI is green; drift on the new HEAD is effectively 0%.

Checklist Results

  • ✅ Architecture & Design: N/A (static HTML/CSS/JS copy-paste, no architectural surface)
  • ✅ Code Quality: Pass
  • ✅ Testing: Pass (site-counts drift detection CI green; local verification confirms 0% drift)
  • ✅ Security: Pass (no auth / crypto / secrets touched; Consent Mode v2 defaults denied, gated on explicit grant)
  • ✅ Performance: N/A
  • ✅ PR Description & Glossary: Pass
  • ⚠ Summary Bullet Narrative: Pass (all bullets explain what + why)
  • ✅ Technical Decisions (AgDR):N/A (skip marker present + valid — pure copy-paste of pre-existing implementation)
  • ✅ Adopter Handbooks: N/A (none load for this diff)

Drift verification on new HEAD

Local measurement against 399f605:

File Actual chars Declared chars Drift
site/index.html 94,791 94,791 0.00%
site/how-it-works.html 32,263 32,263 0.00%
site/architecture.html 40,808 40,807 0.00%
site/skills.html 47,681 47,681 0.00%

Loop tolerance is 5%; observed drift is effectively zero across all 4 files (1-byte discrepancy on architecture.html is the literal \n count rounding the chars-divided-by-4 token estimate by one — well inside tolerance).

Earlier PASS findings still hold

  • Scope unchanged — diff still confined to exactly 4 site/*.html files; no scope leak into framework code (hooks / skills / rules / agents)
  • GA4 + consent blocks byte-identical across all 4 pages:
    • gtag block sha256: f53ea9ed… (all 4 pages identical)
    • consent-css block sha256: 5549da23… (all 4 pages identical)
    • consent block sha256: 1759b8de… (all 4 pages identical)
  • anonymize_ip absentgrep -c anonymize_ip site/*.html returns 0 across the board
  • Begin/end markers paired — 3 begin + 3 end per file (gtag / consent / consent-css)
  • Glossary, summary bullets, skip markers — unchanged from prior review; still pass

Fixup-commit hygiene

  • Only 8 lines changed in 399f605 (4 files × 2 lines each — exactly the meta-tag pair)
  • No unintended churn; commit message accurately describes what changed and acknowledges the over-scope (4 files updated, not just the 2 the loop scans — operator chose accuracy over minimal compliance, which is the right call)

Issues Found

None.

Suggestions

  • nit (carryover from prior review) — the test_site_counts.sh loop is missing how-it-works.html. The operator already noted this is out of scope; worth filing as a follow-up so the next meta-tag refresh PR doesn't have to remember to update the unscanned files manually.
  • nit (cosmetic) — PR body Testing block reports gtag-refs=6 per file; observed count is gtag-refs=12. Likely a stale pre-fixup figure. Not a real issue (the parity property — same count across all 4 files — holds), but worth a one-line update if the author touches the description again.

Verdict

APPROVED


🤖 Reviewed by Rex (Code Reviewer Agent)
📌 Reviewed commit: 399f605c00dba3d99b766681d016c17d7742254b

@atlas-apex atlas-apex merged commit b29b266 into main May 24, 2026
7 checks passed
@atlas-apex atlas-apex deleted the fix/GH-399-ga4-all-pages branch May 24, 2026 17:06
This was referenced May 24, 2026
atlas-apex added a commit that referenced this pull request May 24, 2026
- Append [2.0.2] section above [2.0.1]
- Bump site/index.html version pill v2.0.1 → v2.0.2
- Site-only release; framework unchanged
- Underlying fix: #399 / PR #400 (GA4 + consent on all 4 pages)

Refs #401

Co-authored-by: me2resh <ahmed.abdelaliem@gmail.com>
OmarElaraby26 pushed a commit to OmarElaraby26/apexyard that referenced this pull request May 27, 2026
…h#400)

* fix(me2resh#399): site/ — GA4 + consent banner on all 4 pages

- Copy gtag.js + Consent Mode v2 init + cookie consent banner
  from index.html to how-it-works.html, architecture.html, skills.html.
- Wrap each block with begin/end markers so future sync edits
  are greppable across pages.
- Remove dead `anonymize_ip: true` from gtag config (no-op in GA4
  — all IPs auto-anonymized; carryover from Universal Analytics).

Closes me2resh#399

* fix(me2resh#399): refresh llm:token-count + llm:doc-length meta tags

The GA4 + consent block additions in the prior commit grew each page
by ~5–6K chars. Refreshing the per-page LLM payload-size meta tags
on all 4 site files (not just the 2 the test_site_counts.sh loop
scans) so token-budget-aware clients see accurate costs.

- index.html:        23189 → 23697 tok / 92757 → 94791 chars
- how-it-works.html:  6572 →  8065 tok / 26291 → 32263 chars
- architecture.html:  8864 → 10201 tok / 35458 → 40807 chars
- skills.html:       10658 → 11920 tok / 42632 → 47681 chars

Verified: test_site_counts.sh PASS — all scanned files within 5% of
actual.

Refs me2resh#399

---------

Co-authored-by: me2resh <ahmed.abdelaliem@gmail.com>
OmarElaraby26 pushed a commit to OmarElaraby26/apexyard that referenced this pull request May 27, 2026
…h#402)

- Append [2.0.2] section above [2.0.1]
- Bump site/index.html version pill v2.0.1 → v2.0.2
- Site-only release; framework unchanged
- Underlying fix: me2resh#399 / PR me2resh#400 (GA4 + consent on all 4 pages)

Refs me2resh#401

Co-authored-by: me2resh <ahmed.abdelaliem@gmail.com>
atlas-apex pushed a commit that referenced this pull request Jun 4, 2026
…sh-divergence

8 past release squash-commits (v1.2.0→v2.2.0 + site hotfix #400) were never
synced back into dev, leaving main↔dev diverged on ~50 files. dev is the
verified content superset (CHANGELOG complete, GA4/consent present, deletions
are dev-intentional), so we keep dev's tree and record main as an ancestor to
clear the merge conflict. Real release-sync follows.

Refs #508
mosta7il pushed a commit to mosta7il/apexyard that referenced this pull request Jun 8, 2026
…h#400)

* fix(me2resh#399): site/ — GA4 + consent banner on all 4 pages

- Copy gtag.js + Consent Mode v2 init + cookie consent banner
  from index.html to how-it-works.html, architecture.html, skills.html.
- Wrap each block with begin/end markers so future sync edits
  are greppable across pages.
- Remove dead `anonymize_ip: true` from gtag config (no-op in GA4
  — all IPs auto-anonymized; carryover from Universal Analytics).

Closes me2resh#399

* fix(me2resh#399): refresh llm:token-count + llm:doc-length meta tags

The GA4 + consent block additions in the prior commit grew each page
by ~5–6K chars. Refreshing the per-page LLM payload-size meta tags
on all 4 site files (not just the 2 the test_site_counts.sh loop
scans) so token-budget-aware clients see accurate costs.

- index.html:        23189 → 23697 tok / 92757 → 94791 chars
- how-it-works.html:  6572 →  8065 tok / 26291 → 32263 chars
- architecture.html:  8864 → 10201 tok / 35458 → 40807 chars
- skills.html:       10658 → 11920 tok / 42632 → 47681 chars

Verified: test_site_counts.sh PASS — all scanned files within 5% of
actual.

Refs me2resh#399

---------

Co-authored-by: me2resh <ahmed.abdelaliem@gmail.com>
mosta7il pushed a commit to mosta7il/apexyard that referenced this pull request Jun 8, 2026
…h#402)

- Append [2.0.2] section above [2.0.1]
- Bump site/index.html version pill v2.0.1 → v2.0.2
- Site-only release; framework unchanged
- Underlying fix: me2resh#399 / PR me2resh#400 (GA4 + consent on all 4 pages)

Refs me2resh#401

Co-authored-by: me2resh <ahmed.abdelaliem@gmail.com>
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.

[Bug] site/ — GA4 + consent banner only on index.html; 3 other pages blind

2 participants