Arguments:
<url or local path> [--gtm <container-id>] [--strict-mode]. Wherever<arguments>appears below, substitute the text the user typed after the skill name.
GA4 + GTM Audit
Comprehensive audit of a website's GA4 + GTM setup with live Playwright verification. Produces .ga4-audit/REPORT.md with prioritized fixes.
CRITICAL RULES
- Verify live, not just code. Use Playwright MCP (via
playwright-skill) to load the site, inspectdataLayer, network requests togoogle-analytics.com/googletagmanager.com, and the cookie banner state. - Check Consent Mode v2 compliance. Analytics must not fire before consent on EU visitors; verify default
analytics_storage: 'denied'and correctupdatecalls. - Never fabricate IDs. If you cannot see the GA4 Measurement ID, GTM Container ID, or Ads Conversion ID, say so -- do not guess.
- Write output to
.ga4-audit/for persistence and re-runs.
Pre-flight
Dependency check
Every live check in Phase 2 needs Playwright MCP tools (browser_navigate, browser_evaluate, browser_network_requests). If they are not available, warn the user:
Missing required plugin: playwright-skill
Live verification needs Playwright MCP tools to inspect dataLayer, network
requests, cookies, and the consent banner state in a real browser. Without
it, the audit is limited to reading source code.
playwright-skill is a declared dependency of digital-marketing, distributed
by its own upstream marketplace. Install it with:
claude plugin marketplace add lackeyjb/playwright-skill
claude plugin install playwright-skill@playwright-skill
Degraded fallback without Playwright: run a source-only audit (grep the codebase, fetch raw HTML via WebFetch). Report every check that needs a live browser as NOT VERIFIED, never as pass or fail. Say so explicitly in the report header so no reader mistakes an unrun check for a passing one.
Flags
--gtm <GTM-ID>-- skip container detection in Phase 1 and audit the given container ID. Still flag any other container ID found in the source as a duplicate.--strict-mode-- treat every Warning as Critical in the final report and print an explicitVERDICT: FAILline when any remains. Report text only; the command never sets a process exit code.
Phase 1 -- Discovery
Identify the target:
- Live URL, or an already-running local dev server. To find one, read the project's dev script and its configured port, or ask the user for the URL. Never start a dev server and never claim one was started.
- Extract GTM container ID (
GTM-XXXXXX) from<script src="...gtm.js?id=GTM-...">or<iframe src="...ns.html?id=GTM-...">. With--gtm <GTM-ID>, use the supplied ID instead and skip this step - Extract GA4 Measurement ID (
G-XXXXXXXX) from gtag config or dataLayer events - Detect CMP: iubenda, Cookiebot, Orestbida CookieConsent, OneTrust, Axeptio, custom
- Detect Consent Mode v2 integration: look for
gtag('consent', 'default', {...})andgtag('consent', 'update', {...})calls
Write discovery artifacts to .ga4-audit/01-discovery.md.
Phase 2 -- Live Verification with Playwright
Open the site in Playwright, capture:
Pre-consent state
- Before accepting cookies, record
dataLayercontents and any network requests tog/collect(GA4) orads/ga-audiences(Ads) - Expected: no hits unless Consent Mode defaults allow (e.g.,
ad_user_data: 'denied', analytics_storage: 'denied') - Flag if
g/collectfires before consent -- GDPR violation
Post-consent state
- Accept cookies, record the
gtag('consent', 'update', ...)call payload - Record subsequent
g/collecthits, their parameters (en,tid,cid,dl) - Confirm GTM is live: inspect
window.dataLayercontents and the network requests togoogletagmanager.com/gtm.js - Verify GA4 cookies are set:
_ga, plus_ga_<ID>where<ID>is the Measurement ID with theG-prefix stripped (G-ABC123produces_ga_ABC123)
Event coverage
For each page type (home, product, checkout, thank-you), capture:
dataLayer.push({event: 'page_view', ...})-- implicit if autotracking- Custom events:
add_to_cart,purchase,sign_up,lead, etc. - Enhanced E-commerce items array for
purchase
Write to .ga4-audit/02-verification.md.
Phase 3 -- Configuration Audit
Using the GA4 Admin API (if the user has API credentials configured for it) or a manual walkthrough of the GA4 admin UI with the user, verify:
GA4 Property
- Data streams configured (Web, iOS, Android as needed)
- Enhanced Measurement enabled for relevant events (scroll, outbound clicks, site search, video, file download, form interactions)
- Data retention set (2 months is the default and the safe EU choice; 14 months only with a documented business justification)
- Google Signals enabled only if remarketing is needed AND consent has
ad_user_data: 'granted'
IP anonymization needs no verification. GA4 truncates IPs by design and exposes no setting to check or toggle (see gdpr-compliance-eu.md).
Key Events (Conversions)
-
purchasemarked as Key Event (always) - Business-specific events marked (form_submit, begin_checkout, generate_lead)
- No double-counting (avoid marking both
clickandgenerate_leadfor the same action) - Key Event value set where monetary (
valueparameter)
Audiences (for Remarketing)
- "All Users" audience exists (GA4 default)
- Retargeting audiences: cart abandoners, high-intent visitors, past purchasers
- Predictive audiences (likely-to-purchase, likely-to-churn) enabled -- note 28-day backfill (not immediate)
- Audience triggers fire on correct events
Ads Linking
- GA4 property linked to Google Ads account
- Key Events imported into Ads as Conversions
- Enhanced Conversions enabled for key event imports (hashed email, phone)
- Consent signals propagated to Ads (
ad_storage,ad_user_data,ad_personalization)
Write to .ga4-audit/03-config.md.
Phase 4 -- Consent Mode v2 Deep Check
Consent Mode v2 is mandatory for EU traffic since March 2024.
Default state
// Required BEFORE gtag('config', 'G-...') or GTM load
gtag('consent', 'default', {
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
analytics_storage: 'denied',
wait_for_update: 500 // ms, optional but strongly recommended
});
Consent Mode v2 requires exactly these four signals. functionality_storage, personalization_storage, and security_storage are optional extras: accept them if the CMP sets them, never flag them as missing.
Update on acceptance
// After user accepts
gtag('consent', 'update', {
ad_storage: 'granted',
ad_user_data: 'granted',
ad_personalization: 'granted',
analytics_storage: 'granted'
});
Flag:
- Default call missing -> all traffic denied by default; no modeled conversions
- Default call fires AFTER GTM/gtag load -> race condition
- Granular consent categories not mapped (EU requires 4 separate signals, not just one "analytics")
-
wait_for_updatemissing -> events fire with denied before update arrives
Write to .ga4-audit/04-consent.md.
Phase 5 -- Report
Generate .ga4-audit/REPORT.md:
# GA4 + GTM Audit Report -- <url> -- <date>
## Summary
- GTM Container: <GTM-XXXXXX>
- GA4 Property: <G-XXXXXXXX>
- CMP Detected: <iubenda / Cookiebot / etc.>
- Consent Mode v2: [COMPLIANT | PARTIAL | MISSING]
- Live verification: [PLAYWRIGHT | SOURCE-ONLY, live checks NOT VERIFIED]
- VERDICT: [PASS | FAIL] <!-- with --strict-mode, any Warning promotes to Critical and forces FAIL -->
## Critical (GDPR / data-loss risk)
- ...
## High (breaking measurement)
- ...
## Medium (best-practice gaps)
- ...
## Nice-to-have
- ...
## Auto-implementable fixes
Code snippets ready to paste for each fix.
Synergies
- Playwright-based verification ->
playwright-skill - GA4/GTM knowledge base ->
digital-marketing:ga4-implementationskill - Cookie banner (CMP) selection + config ->
business:privacy-doc-generator - Full SEO audit (separate) ->
/digital-marketing:seo-audit