Growth Measurement Audit
Audit the measurement layer beneath page-level analytics. Page audits ask "does this CTA fire an event?" This skill asks the prior question: when the event fires, does anything trustworthy happen to it?
Why this layer first: a real audit of a production stack (2026-08-06) found
every page-level event working correctly while the layer beneath discarded
the results — an untagged conversion domain, a dead property still carrying a
configured purchase key event, event parameters silently dropped because no
custom dimension was registered, one host double-tagged into two properties
with diverging counts, and five of six key events receiving no data. No page
crawl could have caught any of it. Numbers built on an unaudited measurement
layer are confident and wrong.
Which account to audit (target resolution)
Resolve the target in this order, and say which rule applied before making any calls:
- Named target wins. If the request names a domain, property, or account, audit that.
- Otherwise, default to the account in
data/property-registry.md— if it describes a real account. State it ("Auditing per the registry; name a different property to override") and proceed. Two files never bind this rule: anything nameddata/example-*(fictional documentation of the format, never a target) and a registry whose content says it is scrubbed / describes no account (treat as absent). - Neither? Ask the user which account/property to audit. Do not guess and do not sweep whatever the token happens to see without saying so.
Working copies, clones, and publishing
data/ is per-account state, not part of the runbook. The intended model:
- The published/master skill carries only
data/example-*files — fictional, clearly marked, safe to be public. - To use the skill on a real account, clone it. The clone's first
audit writes the real
data/property-registry.mdand findings; from then on the clone defaults to its own account. - Never publish real state. A real registry bundles property IDs, measurement IDs, event taxonomies, and traffic figures — together that is a ready-made analytics-poisoning kit (GA4's collect endpoint is unauthenticated; convincing spam requires exactly this taxonomy) plus competitive intelligence and social-engineering pretext material. If a clone with real state must be shared, share it tenant-scoped, never publicly.
Prerequisites
- The google-search MCP connector (GSC + GA4, read-only) — tools:
gsc_search_analytics,gsc_url_inspect,gsc_list_sitemaps,ga4_run_report,ga4_run_realtime,ga4_run_funnel,ga4_key_events. - Some checks need Admin API endpoints the connector does not yet expose
(
accountSummaries,customDimensions,dataStreams). Use the REST calls inreferences/checks.mdwith the connector's bearer token until those tools exist. - The authorising Google account needs Viewer on the GA4 properties and at least Restricted on the GSC properties. Scopes are not access: a perfect token still 403s for data the account cannot see.
Before you start: read prior state
If a real data/property-registry.md exists, read it first. It records the
property inventory and health from the last audit; your job then includes
answering "what changed since?" — not just re-describing the world. On
a first run (no registry), this audit creates it.
The audit sequence
Run the six checks in order — each depends on the inventory the previous one
built. Full procedures, REST endpoints, and healthy/unhealthy signatures are
in references/checks.md; read it before running.
-
Property inventory — list every GA4 property the account can see (
accountSummaries), then sweep each for 28-day life (ga4_run_report, metrics only). Dead properties with configured key events are fossils: someone instrumented conversions once and the tag was lost. Name them; do not silently skip them. -
Hostname reconciliation — for each live property,
ga4_run_reportwith dimensionhostName. Two failure shapes: a host the business converts on that appears in NO property (a measurement black hole), and one host appearing in TWO properties (double-tagging; counts will diverge and any benchmark from one is quietly wrong against the other). -
Key-event health —
ga4_key_eventsper property, then cross-check each against actual event volume (ga4_run_reportbyeventName). Three states: healthy (configured + receiving), fossil (configured, zero volume), and missing (high-volume conversion-intent events not starred — these count zero conversions no matter how well they perform). -
Custom-dimension coverage — list registered custom dimensions (
customDimensionsREST), then compare against parameters the site actually sends (visible in the GA4 UI's collected-params list, or known from the codebase). Unregistered params are collected and discarded — GA4 does not backfill, so every day unregistered is breakdown data lost permanently. This is always the most time-sensitive finding; say so. -
Cross-domain continuity — map the conversion journey's hostnames. Same registrable domain (site.example.com + example.com) shares the _ga cookie automatically; different registrable domains need the stream's cross-domain configuration. GA4 cannot join users across properties at all — if the journey spans two properties, no funnel tool can see it, and the fix is one property with multiple hosts, not attribution guesswork.
-
GSC coverage —
gsc_list_sitemapsand a sites listing per property; confirm each converting domain has a GSC property and the sitemap Google discovered matches what the site publishes.
Report format
ALWAYS use this structure (see data/example-findings.md for a complete
fictional example):
# Measurement audit — <account/site> — <date>
**Window:** <start>..<end> **How measured:** <tools, live calls>
## One sentence
<the single most consequential finding>
## Property inventory
<table: property, ID, 28-day events/users/sessions, verdict alive/dead/duplicated>
## Findings (F1..Fn, severity order)
<each: what, evidence with exact numbers, why it matters>
## Work order (W1..Wn, leverage order)
<each: the fix, who can do it, and an ACCEPTANCE CHECK runnable
through a named connector tool — a fix without a check is a hope>
Honesty rules
These exist because their violations produced real fabricated findings:
- An error is not a zero. A 403/429/timeout means "unknown", never "no data". A rate-limited probe once reported "64 of 100 pages empty" when all 64 were errors. If a call fails, report the failure, not an empty result.
- State the window and fetch date on every number. GSC finalized data lags ~2-3 days; GA4 intraday differs from finalized. Numbers without windows get misquoted.
- Reconcile at least one figure against the GA4 UI before trusting the API path end-to-end on a new property, and say you did.
- Expected values are hypotheses. If a known-answer check fails, the recorded expectation may be wrong rather than the system — investigate both directions before declaring a defect.
After the audit: write state back
Update this skill's resources so the next run can diff:
data/property-registry.md— the refreshed inventory and per-property health verdicts (format:data/example-registry.md).data/findings-<date>.md— the full report you produced.
Use save_skill_resource (skills MCP). Reads serve the draft version if one
exists, so written state is immediately readable — no publish needed between
runs.