<!-- Fictional example. Output of scripts/audit_ga4.py on the Acme Store assets (see references/worked-example.md), with the agent's confirmation notes added under each FAIL that needs one, the proposed context updates filled in and the thresholds block trimmed. -->

# GA4 audit: Acme Store - GA4 (properties/123456789)

> Data loss risk: G18 22 days above the 1,000,000 standard daily export limit (peak 1,187,440 on 2026-10-27 in BigQuery, consent-denied pings included); Google pauses the daily export and does not reprocess paused days; at risk: daily tables still complete

## Baseline

- Property: properties/123456789, GOOGLE_ANALYTICS_STANDARD, PROPERTY_TYPE_ORDINARY, time zone America/New_York, currency USD, created 2023-03-14
- Read route: collector; scopes granted: https://www.googleapis.com/auth/analytics.readonly, https://www.googleapis.com/auth/cloud-platform; scope check: passed (cloud-platform noted)
- Read time: 2026-11-01T09:20:00Z; as of 2026-11-01; data window 2026-10-03 to 2026-10-30
- Inputs: snapshot, config, UI intake, BigQuery results, Google Ads results
- Script: ran scripts/audit_ga4.py, exit code 1
- Not readable: none
- Decisions this property feeds: Google Ads bidding, the weekly revenue report, the warehouse revenue model

## Score

- Score 25/100, not decision-grade, coverage 26 of 26 applicable rules (100.0%).
- Cap: G23 and G24 FAIL, so the grade can be no better than material issues.
- A Property and streams: 50 (weight 8)
- B Retention and identity: 50 (weight 6)
- C Traffic hygiene: 0 (weight 14)
- D Key events and values: 0 (weight 16)
- E Google Ads integration: 25 (weight 10)
- F BigQuery export: 50 (weight 8)
- G Custom definitions: 50 (weight 6)
- H Attribution and channels: 50 (weight 10)
- I Consent and privacy: 0 (weight 16)
- J Reporting integrity: 50 (weight 6)
- Previous audit: first audit

## Trust summary

- Acquisition by session: do not use until fixed (G06 FAIL, G07 FAIL, G08 FAIL, G09 FAIL, G10 FAIL, G15 WARN, G22 WARN)
- Key events and conversion rates: do not use until fixed (G06 FAIL, G07 FAIL, G11 FAIL, G12 FAIL, G14 FAIL)
- Revenue and ecommerce: do not use until fixed (G08 FAIL, G12 FAIL, G13 FAIL)
- Explorations and long date ranges: do not use until fixed (G04 FAIL, G25 WARN)
- Google Ads imports and bidding signals: do not use until fixed (G11 FAIL, G15 WARN, G16 FAIL, G23 FAIL)
- BigQuery and warehouse models: do not use until fixed (G18 FAIL, G20 WARN)
- EEA audiences and remarketing: do not use until fixed (G15 WARN, G23 FAIL)

## Analyst caveats

- Retention: explorations and funnels reach back about 2 months (to about 2026-09-01); standard reports are not limited by this setting.
- Sampling: explorations that read more than about 8 days sample on this property (average 1,218,734 events a day, default exploration limit 10,000,000 events per query).
- Thresholding: rows can be withheld in demographic probe; a withheld row is not a zero.
- Attribution: PAID_AND_ORGANIC_CHANNELS_DATA_DRIVEN, lookback 30 days for acquisition key events and 30 days for other key events. Model changes rewrite history; lookback changes apply forward.
- Identity: reporting identity BLENDED, Google signals GOOGLE_SIGNALS_ENABLED; user counts differ between identities.
- Modelled data: Blended reports show "including estimated user data". Modelled users appear only under Blended and are never in the BigQuery export, which explains part of any gap between the two (answer 11161109).
- Time zone: GA4 reports in America/New_York, Google Ads in America/New_York; BigQuery event_timestamp is UTC, event_date uses the property time zone.
- Key events: totals of all key events include form_submit, session_start; read per-event key event metrics until that is fixed.
- Window: 2026-10-03 to 2026-10-30; the last two days are left out because processing takes 24 to 48 hours. Google Ads data in GA4 covers a rolling 36 months.

## FAIL

- G02 streams-and-tag-coverage (priority P2):
  - FAIL www.example.com sends events to 2 streams ("Acme web" 85.0%, "Acme web (2023 rebuild)" 15.0%): double tagging splits users and sessions
  - WARN measurement ID G-XXXXXXXX ("Acme web (2023 rebuild)") is not in stack.md
  - WARN 2 web streams for example.com: Acme web, Acme web (2023 rebuild)
  - WARN 2 other pages show Not tagged or No recent activity in Tag coverage [intake tag.coverage, evidence shots/08-tag-coverage.png]
- G04 data-retention (priority P3):
  - FAIL event data retention TWO_MONTHS (2 months) is shorter than the 14 months needed; explorations and funnels lose older data for good
- G06 internal-traffic (priority P2):
  - FAIL rule "Office" sets traffic_type office but no Active or Testing internal traffic filter uses office [intake tag.internal_traffic_rules, evidence shots/03-internal-rules.png]
  - FAIL filter "Internal traffic" (active) excludes traffic_type internal, which no rule sets
  - Confirmation: unconfirmed. Proposed as a change list item for a property editor, with the measurement owner's approval: a second internal filter for traffic_type office in Testing, read with R11 after 24 to 36 hours to size staff traffic before the rule value changes.
- G07 developer-traffic (priority P1):
  - FAIL Developer traffic filter in Testing matches 61.0% of events: debug_mode is on production events (a debug_mode parameter set to false still enables debug mode); activating it would delete real data
  - WARN filter "Developer traffic" has been in Testing since 2026-08-12 (81 days), so nothing is excluded
  - Confirmation: data recheck. R11T read on 2026-10-30 and 2026-11-01 (61.0% and 60.4%); gtm-container-audit found debug_mode set to false on the Google tag configuration.
- G08 hostnames-and-hostname-filters (priority P2):
  - FAIL unexpected hostname staging.example.com: 2.1% of events, 380 key events
  - WARN unexpected hostname localhost: 0.1% of events, no key events
  - Confirmation: data recheck. R03 by date shows staging.example.com every weekday; QA confirmed the 380 key events are test orders.
- G09 referrals (priority P3):
  - FAIL paypal.com / referral holds 3.2% of key event credit and is not in unwanted referrals [intake tag.unwanted_referrals, evidence shots/02-unwanted-referrals.png]
  - FAIL owned domain shop.example.net is a referral source with 3.4% of sessions
  - Confirmation: data recheck. R06 for 2026-10-24 to 2026-10-30: paypal.com / referral 3.1% of key event credit; R05 shop.example.net 3.5% of sessions.
- G10 cross-domain-and-session-settings (priority P3):
  - FAIL example.net uses the same measurement ID but is missing from Configure your domains (shop.example.net is a referral with 3.4% of sessions; 12.0% of click events are outbound clicks to it) [intake tag.domains, evidence shots/01-domains.png]
  - Confirmation: unconfirmed. Planned: a fresh click from www.example.com to shop.example.net in a clean profile, no order placed, approved by the web analytics lead; expect _gl on arrival and the same client ID.
- G11 key-event-inventory (priority P2):
  - FAIL form_submit is a key event and enhanced measurement sends it on every submit attempt, including search and newsletter forms
  - FAIL session_start is a key event and it fires in nearly every session (marked the day the Google Ads link was created)
  - WARN 6 ecommerce steps are key events without a documented purpose: view_item, add_to_cart, begin_checkout, view_item_list, add_to_wishlist, view_search_results (all marked the day the Google Ads link with ads personalization was created)
  - WARN key events not in the tracking plan: file_download
  - WARN 12 key events, so totals of all key events mix unlike outcomes
  - Confirmation: data recheck. R01 shows session_start key events equal to sessions; the GAQL result lists no Google Ads action built on session_start or the six ecommerce steps.
- G12 key-event-counting-and-duplicates (priority P2):
  - FAIL purchase is counted Once per session, so repeat purchases in a session are dropped (the legacy default for key events migrated from Universal Analytics goals)
  - WARN generate_lead: 1.33 events per session in which it occurs (limit 1.2); fix the duplicate trigger, not the counting method
  - Confirmation: configuration and data. Key event settings screenshot shows Once per session; R01 purchase keyEvents 9,620 against eventCount 9,800.
- G13 purchase-and-value-integrity (priority P2):
  - FAIL 6.2% of 9,800 purchase events have no transaction_id (counts only, IDs not shown)
  - WARN 2.4% of transaction IDs appear on more than one purchase event (refreshes or two senders)
  - WARN GA4 records 9,800 purchases against 9,350 orders in the order system for the window (104.8%): duplicates, test orders or a second sender; order-level causes go to purchase-reconciliation
  - WARN gross item revenue (1,197,400) and gross purchase revenue (1,234,560) differ by 3.0%: value includes shipping or tax, or items are missing or priced wrongly, so item reports disagree with revenue
  - Confirmation: data recheck. R08 for 2026-10-24 to 2026-10-30: 6.0% of purchases without a transaction ID. The order system owner confirmed 9,350 orders for the window as an aggregate; staging test orders (G08) and repeated transaction IDs explain most of the gap, and order-level checks go to purchase-reconciliation.
- G14 event-rules-and-names (priority P2):
  - FAIL create rule in "Acme web" makes key event generate_lead (page_location contains /thank-you-contact) while tags already send generate_lead: double count
  - Confirmation: configuration. Create rule read from the snapshot; the tracking plan and gtm-container-audit both show GTM sends generate_lead after server acceptance.
- G16 ads-conversion-sharing (priority P2):
  - FAIL PURCHASE: GA4-imported "Acme Store - GA4 (web) purchase" and native or uploaded "Website purchase" are both primary, so one outcome counts twice for bidding
  - WARN conversion export scope is NOT_SELECTED_YET while GA4 key events are imported to Google Ads
  - WARN Universal Analytics conversion actions still ENABLED: UA Transactions
  - WARN primary actions with a click-through window shorter than the 45-day sales cycle: Acme Store - GA4 (web) purchase (30 days), Website purchase (30 days)
  - Confirmation: data and tags. GAQL result of 2026-11-01 (assets/example-acme-ads-conversions.json); the GA4 action imports the purchase key event and the website action's tag fires on the same order confirmation page, so both measure one outcome.
- G18 bigquery-volume-and-health (priority P1):
  - FAIL 22 days above the 1,000,000 standard daily export limit (peak 1,187,440 on 2026-10-27 in BigQuery, consent-denied pings included); Google pauses the daily export and does not reprocess paused days; at risk: daily tables still complete
  - Confirmation: data recheck. BQ3 daily counts include consent-denied pings; BQ1 shows daily tables complete through 2026-10-30 and no pause email has arrived, so the export is at risk, not yet paused.
- G23 consent-signals (priority P1):
  - FAIL stream "Acme web": ad_personalization not received, with 38% of traffic from the EEA, while the property shares data with Google Ads [intake consent.stream.Acme web, evidence shots/10-consent.png]
  - Confirmation: unconfirmed. Consent settings page read once on 2026-11-01; runtime proof planned through consent-debugging, then the page is read again 48 to 72 hours after the fix.
- G24 pii-and-redaction (priority P1):
  - FAIL 312 events with pageLocation on www.example.com/newsletter/confirm match email-like value; pii key email (query keys email,utm_source; values not shown)
  - FAIL 268 events with pageReferrer on www.example.com/newsletter/confirm match email-like value; pii key email (query keys email,utm_source; values not shown)
  - FAIL custom dimension customUser:user_email holds email-like or phone-like values (999 email-like, 0 phone-like values in the 28-day probe)
  - WARN email redaction is off on "Acme web", "Acme web (2023 rebuild)"
  - WARN query parameter redaction is off on "Acme web", "Acme web (2023 rebuild)" while PII keys were found (email)
  - WARN 1 Measurement Protocol secret(s) on Acme web: redaction does not cover Measurement Protocol or data import [intake mp_secrets.Acme web, evidence shots/15-mp-secrets-cropped.png]
  - WARN account data sharing with Google products & services is on without a recorded decision (account level; the privacy owner decides)
  - Confirmation: data recheck. R15 for 2026-10-24 to 2026-10-30: 74 events on the same path, values not copied; privacy owner informed on 2026-11-01.

## WARN

- G03 enhanced-measurement (priority P4):
  - WARN "Acme web": form interactions on while tags send generate_lead, sign_up; form_submit also fires on failed, search and newsletter forms
- G15 google-ads-links (priority P3):
  - WARN link to retired account 000-000-0000, ads personalization on
  - WARN 18.0% of 96,000 google / cpc sessions have no Google Ads customer ID (auto-tagging off, an unlinked account or manual UTMs)
- G19 custom-definition-design (priority P4):
  - WARN event custom definitions: 41 of 50 (82.0%)
  - WARN registered IDs or timestamps: client_id, session_id (use User-ID for users; IDs create (other) rows)
- G20 custom-definition-data (priority P4):
  - WARN only (not set) for 28 days: customEvent:experiment_variant (wrong parameter name or scope, or a dead slot)
  - WARN more than 500 distinct values in one day: customEvent:client_id (8,600), customEvent:session_id (12,400), customUser:user_email (2,140)
- G21 attribution-settings (priority P3):
  - WARN other key events lookback is 30 days, shorter than the 45-day sales cycle (it also shortens how long direct sessions keep an earlier source)
  - WARN Other key events lookback window changed from 90 to 30 days on 2026-07-09 has no annotation [intake change_history.1, evidence shots/14-change-history.png]
- G22 channel-groups-and-unassigned (priority P3):
  - WARN Unassigned holds 7.8% of sessions in the default channel group and 7.8% in the primary group "Acme channels" (diagnose with ga4-unassigned-not-set-diagnosis)
  - WARN primary group "Acme channels": AI Assistants sits after Referral, so assistant visits that arrive with medium referral land in Referral (first match wins)
- G25 thresholding-sampling-cardinality (priority P4):
  - WARN sampling horizon 8 days at the default exploration limit: explorations over more than 8 days sample (average 1,218,734 events a day)
- G26 diagnostics-and-change-records (priority P4):
  - WARN diagnostic "Missing aggregate identifiers (gbraid and gad_ parameters)" open since 2026-08-02 (91 days) with no owner [intake diagnostics.1, evidence shots/13-diagnostics.png]

## PASS

- G01 property-basics: time zone America/New_York, currency USD, GOOGLE_ANALYTICS_STANDARD, not in the trash
- G05 reporting-identity-and-user-id: identity BLENDED documented; modelling: including estimated user data
- G17 bigquery-link-config: daily export to projects/123456789012 (US) of every production stream, nothing needed excluded

## Not checked

- none

## Not applicable

- none

## Change list order

- P1 G07 FAIL
- P1 G18 FAIL
- P1 G23 FAIL
- P1 G24 FAIL
- P2 G02 FAIL
- P2 G06 FAIL
- P2 G08 FAIL
- P2 G11 FAIL
- P2 G12 FAIL
- P2 G13 FAIL
- P2 G14 FAIL
- P2 G16 FAIL
- P3 G04 FAIL
- P3 G09 FAIL
- P3 G10 FAIL
- P3 G15 WARN
- P3 G21 WARN
- P3 G22 WARN
- P4 G03 WARN
- P4 G19 WARN
- P4 G20 WARN
- P4 G25 WARN
- P4 G26 WARN

## Inventories

### Streams

- "Acme web": properties/123456789/dataStreams/1234567890, WEB_DATA_STREAM, G-XXXXXXXXXX, https://www.example.com, 29,400,000 events in the window
- "Acme web (2023 rebuild)": properties/123456789/dataStreams/2345678901, WEB_DATA_STREAM, G-XXXXXXXX, https://www.example.com, 4,724,543 events in the window

### Key events

- purchase: ONCE_PER_SESSION, default value none, deletable false, custom false, created 2023-06-02, 9,800 events in the window
- generate_lead: ONCE_PER_EVENT, default value 50 USD, deletable true, custom true, created 2023-06-02, 5,200 events in the window
- sign_up: ONCE_PER_EVENT, default value none, deletable true, custom true, created 2023-06-02, 2,450 events in the window
- form_submit: ONCE_PER_EVENT, default value none, deletable true, custom true, created 2023-09-18, 38,700 events in the window
- file_download: ONCE_PER_EVENT, default value none, deletable true, custom true, created 2023-09-18, 14,200 events in the window
- session_start: ONCE_PER_EVENT, default value none, deletable true, custom false, created 2024-02-20, 820,000 events in the window
- view_item: ONCE_PER_EVENT, default value none, deletable true, custom false, created 2024-02-20, 1,480,000 events in the window
- add_to_cart: ONCE_PER_EVENT, default value none, deletable true, custom false, created 2024-02-20, 61,500 events in the window
- begin_checkout: ONCE_PER_EVENT, default value none, deletable true, custom false, created 2024-02-20, 21,400 events in the window
- view_item_list: ONCE_PER_EVENT, default value none, deletable true, custom false, created 2024-02-20, 1,920,000 events in the window
- add_to_wishlist: ONCE_PER_EVENT, default value none, deletable true, custom false, created 2024-02-20, 9,800 events in the window
- view_search_results: ONCE_PER_EVENT, default value none, deletable true, custom false, created 2024-02-20, 152,000 events in the window

### Product links

- Google Ads 123-456-7890: personalization on, manager no
- Google Ads 000-000-0000: personalization on, manager no
- BigQuery projects/123456789012 (US): daily on, streaming off, fresh daily off, streams 1234567890, excluded none
- Other product links (intake): Search Console: www.example.com; Merchant Center: 1 account
- GA4 conversion "purchase" (intake): key_event=purchase; counting=once per event; click_window_days=30; engaged_view_window_days=3; channels=Google paid channels

### Account data sharing

- Google products & services on, Modeling contributions & business insights on, Technical support on, Recommendations for your business off

### Custom definitions

- event-scoped dimensions: 41 of 50
- user-scoped dimensions: 2 of 25
- item-scoped dimensions: 1 of 10
- custom metrics: 3 of 50, calculated metrics: 1 of 5

### Data filters (intake)

- name Internal traffic, type internal, operation exclude, value internal, state active, since 2025-01-15
- name Developer traffic, type developer, operation exclude, state testing, since 2026-08-12

### Channel groups

- "Default Channel Group": primary no, system defined yes, 19 channels
- "Acme channels": primary yes, system defined no, 19 channels

### Audiences and annotations

- audiences: 14, annotations: 1

## Proposed annotations

- 2026-07-09: "Other key events lookback window changed from 90 to 30 days". Attribution setting changed in Admin. Model changes rewrite history; lookback changes apply forward. Compare periods with care.

## Thresholds used

- Defaults, no overrides in the config. The script prints all 41 values here; trimmed in this example.

## Next step

- Write the change list from assets/change-list-template.md, ordered as above. Confirm each FAIL in G06 to G13, G16, G18, G23 and G24 before calling it confirmed.
- Proposed context updates (awaiting approval): stack.md, add stream "Acme web (2023 rebuild)" (G-XXXXXXXX) as found and pending removal, and note that retired account 000-000-0000 is still linked; decisions.md, record the event retention need, the account data sharing decision and the Google Ads export scope once chosen. Nothing is written until the user approves.

Score 25/100 (not decision-grade), coverage 26/26 rules. 15 FAIL, 8 WARN, 3 PASS. Nothing in GA4 was changed.
