# Worked example: Acme Analytics (fictional)

Acme Analytics sells a B2B product with self-serve paid plans. The growth lead wants to double paid search spend and asks for a tracking audit first. Everything below is fictional and runs on the files in `assets/`.

## Files

- [../assets/example-acme-stack.md](../assets/example-acme-stack.md): the stack.md the agent reads first (hosts, IDs, routes, consent decision, value rule, owners).
- [../assets/example-acme-plan-events.csv](../assets/example-acme-plan-events.csv) and [../assets/example-acme-plan-properties.csv](../assets/example-acme-plan-properties.csv): tracking-plan-builder sheet tabs for four events (generate_lead, sign_up, begin_checkout, purchase).
- [../assets/example-acme-no-choice.har.json](../assets/example-acme-no-choice.har.json): banner ignored on home and /pricing/, UK egress.
- [../assets/example-acme-reject.har.json](../assets/example-acme-reject.har.json): reject all on home, then /pricing/.
- [../assets/example-acme-journey.har.json](../assets/example-acme-journey.har.json): two marked landing routes, accept, the demo form (invalid then valid), signup on app.example.com, the SPA route fixture, upgrade, hosted checkout on pay.example.net, the return and a reload.
- [../assets/example-acme-crm-export.csv](../assets/example-acme-crm-export.csv): HubSpot contacts created 2026-08-01 to 2026-10-31 with the demo form's hidden-field `gclid` property, HubSpot's own Google ad click ID and Facebook click ID, and UTMs; no personal columns.
- [../assets/example-acme-checklist.csv](../assets/example-acme-checklist.csv): the filled worksheet.

## Scope sheet summary (E-00)

- Level L3: GA4 and Google Ads read through their MCP servers, Meta read by the paid social owner, HubSpot and order exports, one approved test lead, one approved test signup and two approved test orders.
- Region: UK through IP egress. Consent model from decisions.md (2026-03-15): opt-in, advanced mode for Google tags, non-Google advertising tags in the marketing category; no Microsoft Ads or OpenAI Ads; URL passthrough and ads data redaction off.
- Approvals (web analytics lead and privacy owner, 2026-10-27): banner clicks; synthetic click IDs on the two landing routes; one test lead as qa-audit-01@example.com; one test signup on app.example.com; test orders ACME-TEST-0042 and ACME-TEST-0043 on the payment provider's test card; Meta test events. The paid media lead excludes the two test conversions by order reference.
- Markers: gclid=TeSter-123 and utm_campaign=TeSter-123-campaign, utm_source and utm_medium tester.
- Captures were exported from DevTools by the web analytics lead; the agent drove the same scenarios in its own isolated browser to read cookie names and the data layer.

## Commands

Run from the skill folder, because the example files sit in `assets/`. A real audit keeps its files in the project, as SKILL.md step 1 describes.

```bash
node scripts/har-audit.mjs assets/example-acme-no-choice.har.json --scenario no-choice --mode advanced --uet-mode basic \
  --expect-ids G-XXXXXXXXXX,AW-123456789,1234567890123456,1234567,7654321 --retired-ids 3333333 \
  --own-domains example.com --cmp-hosts cdn.consent.example.org

node scripts/har-audit.mjs assets/example-acme-reject.har.json --scenario reject --mode advanced --uet-mode basic \
  --expect-ids G-XXXXXXXXXX,AW-123456789,1234567890123456,1234567,7654321 --retired-ids 3333333 \
  --own-domains example.com --cmp-hosts cdn.consent.example.org

node scripts/har-audit.mjs assets/example-acme-journey.har.json --scenario journey --mode advanced \
  --expect-ids G-XXXXXXXXXX,AW-123456789,1234567890123456,1234567,7654321 --retired-ids 3333333 \
  --own-domains example.com --cmp-hosts cdn.consent.example.org \
  --markers gclid=TeSter-123,utm_campaign=TeSter-123-campaign \
  --pii qa-audit-01@example.com --approved-matching google-ads --server-routes meta:Purchase \
  --plan-events assets/example-acme-plan-events.csv --plan-properties assets/example-acme-plan-properties.csv \
  --order-value 99.00 --order-currency USD --route-fixture app.example.com/projects=5

node scripts/crm-attribution-coverage.mjs assets/example-acme-crm-export.csv --id "Record ID" \
  --created "Create date" --source "Original Traffic Source" --paid-search "Paid search" \
  --gclid gclid --paid-social "Paid social" --fbclid "Facebook click ID" \
  --utm-source utm_source --utm-medium utm_medium --utm-campaign utm_campaign \
  --since 2026-08-01 --until 2026-10-31

node scripts/crm-attribution-coverage.mjs assets/example-acme-crm-export.csv --id "Record ID" \
  --created "Create date" --source "Original Traffic Source" --paid-search "Paid search" \
  --gclid "Google ad click ID" --since 2026-08-01 --until 2026-10-31

node scripts/score-audit.mjs assets/example-acme-checklist.csv
```

## Expected output

No choice ends `1 FAIL, 1 WARN, 8 PASS. Nothing was sent or changed.` (exit 1):

```text
FAIL CON-02 no-choice: LinkedIn Insight Tag (1234567) sent 2 event request(s) to px.ads.linkedin.com before any choice on 2 page(s); loaded by page source (/assets/theme.js). Evidence: example-acme-no-choice.har.json entry 9 at +1.3s.
WARN INV-04 no-choice: LinkedIn Insight Tag is loaded by page source (/assets/theme.js), outside the tag manager; name its owner and check it passes the consent gate. Evidence: example-acme-no-choice.har.json entry 7 at +0.7s.
PASS CON-04 no-choice: 3 Google request(s) carried denied state (gcs=G100) as advanced mode allows.
```

Reject ends `2 FAIL, 1 WARN, 8 PASS.` The CMP's logging request marks the choice at +8.0s, which matches the recorded click time, so the first LinkedIn hit is a CON-02 case and the second a CON-03 case:

```text
FAIL CON-02 reject: LinkedIn Insight Tag (1234567) sent 1 event request(s) to px.ads.linkedin.com before any choice on 1 page(s); loaded by page source (/assets/theme.js). Evidence: example-acme-reject.har.json entry 9 at +1.3s.
FAIL CON-03 reject: LinkedIn Insight Tag (1234567) sent 1 event request(s) to px.ads.linkedin.com after reject on 1 page(s); loaded by page source (/assets/theme.js). Evidence: example-acme-reject.har.json entry 19 at +16.3s.
```

Neither consent capture lists an unclassified third-party host, so every host that received data is in the vendor table or is the CMP.

Journey ends `13 FAIL, 3 WARN, 20 PASS.` (exit 1). The FAIL and WARN lines:

```text
FAIL INV-01 journey: unexpected TikTok Pixel CXXXXXXXXXXXXXXXXXXX received 10 event request(s) on 10 page(s); loaded by GTM. Ask the owner; raise to S1 if the vendor is not disclosed in the CMP.
WARN INV-04 journey: LinkedIn Insight Tag is loaded by page source (/assets/theme.js), outside the tag manager; name its owner and check it passes the consent gate.
FAIL CNV-04 journey: GA4 purchase value 118.8 where the order is 99.00. Other destinations: Google Ads conversion 118.8 USD; Meta Pixel Purchase 118.8 USD; judge each against its value rule.
WARN EVT-02 journey: begin_checkout (checkout_started, tier 2): plan_tier "Team" is outside starter|team|business.
WARN EVT-05 journey: GA4 settings_opened carries the previous route's location (app.example.com/projects/42) after a newer page_view; check the page variables on route changes.
FAIL SST-02 journey: Meta Pixel Purchase sent 1 time(s) with no eventID (eid) while a server copy is documented; the platform keeps both copies.
FAIL XDS-05 journey: route fixture app.example.com/projects: 8 GA4 page views where 5 are expected; 3 location(s) sent twice within 2s (app.example.com/projects/42 30ms apart, app.example.com/projects/42/settings 30ms apart, app.example.com/projects/42 30ms apart), so two owners send page views.
FAIL XDS-03 journey: return from pay.example.net to app.example.com/billing/success?... started a new session with pay.example.net as referrer and a new session ID; list untaggable processors as unwanted referrals.
FAIL CID-01 journey: landing route /pricing: gclid dropped at 301 from /pricing to /pricing/; utm_campaign dropped at 301 from /pricing to /pricing/ (1 redirect hop(s)).
FAIL CID-04 journey: form submission HubSpot forms api.hsforms.com/submissions/v3/integration/submit/76543...: gclid case changed to tester-123.
FAIL PII-01 journey: test value qa-audit-01@example.com reached GA4 in 1 request(s) (parameters dl).
FAIL PII-01 journey: test value qa-audit-01@example.com reached Meta Pixel in 1 request(s) (parameters dl).
FAIL PII-01 journey: test value qa-audit-01@example.com reached TikTok Pixel in 1 request(s) (parameters context.page.url).
FAIL PII-01 journey: test value qa-audit-01@example.com reached LinkedIn Insight Tag in 1 request(s) (parameters url).
FAIL PII-01 journey: test value qa-audit-01@example.com reached HubSpot tracking in 1 request(s) (parameters pu).
FAIL PII-02 journey: test value qa-audit-01@example.com is in the page URL /demo (query parameter email); forms must use POST and never reflect fields into URLs.
```

Each line also ends with its evidence, for example `Evidence: example-acme-journey.har.json entry 143 at +186.5s.` Among the PASS lines: CNV-02 (the reload sent no second purchase), CNV-03 (transaction_id ACME-TEST-0042), XDS-01 and XDS-02 (www to app keeps the client and session), CID-01 for the home landing route, EVT-01 for all four planned events, PII-03 (the hashed email reached only Google Ads, after the grant) and SST-05, which also prints the route's server address next to the main document's. The export has no server addresses, so the agent runs a DNS lookup: sgtm.example.com resolves through a CNAME to the hosting provider, so Safari caps the cookies it sets to 7 days. SST-05 stays PASS and the cap goes under Expected limits.

The first CRM run, on the hidden-field `gclid` property that the offline import reads, ends `2 FAIL, 1 WARN, 1 PASS.`:

```text
PASS CID-06: control segment coverage 96.7%: 29 of 30 paid search records carry a gclid, gbraid or wbraid value, malformed ones included and judged below (bands: 95% PASS, 80 to 95% WARN). Missing: 40100030. Small sample (30 control rows): treat the rates as provisional.
FAIL CID-06: malformed or placeholder gclids 3.4%: 1 of 29 values (limit 1%). 40100029 placeholder {gclid... (7).
FAIL CID-06: gclids with no upper-case letter 46.4%: 13 of 28 well-formed values (limit 1%; real gclids are mixed case, so an all-lowercase cluster means a lowercasing step). First seen 2026-09-15; examples 40100016, 40100017, 40100018, 40100019, 40100020.
WARN CID-09: UTM violations on 3 of 42 tagged records (7.1%, limit 1%): utm_source case variant 2; medium missing while the other is set 1. Examples: 40100038 utm_source case variant "LinkedIn" of "linkedin"; 40100062 utm_source case variant "LinkedIn" of "linkedin"; 40100060 medium missing while the other is set.
```

Its weekly trend shows 0 of 5 lowercase-only gclids in the week of 2026-09-07 and 2 of 2 from the week of 2026-09-14, which dates the hidden-field release. One record carrying the test marker was excluded and must be flagged as test data.

The second run, on HubSpot's own Google ad click ID, ends `0 FAIL, 0 WARN, 3 PASS.`: the same 29 of 30 records carry a value, none is a placeholder and none is all lowercase. HubSpot fills that property itself, so the lowercasing and the `{gclid}` default both come from the demo form's hidden field, and CID-06 is rated on the property the import reads.

score-audit on the filled checklist (exit 1):

```text
- Verdict: Fail: privacy, grade F
- Score: 59.5 (shown for trend tracking); evidence coverage 94.4% (graded)
- Checks: 71 (37 PASS, 13 WARN, 16 FAIL, 4 NOT_CHECKED, 1 NA)
- Open S1 FAIL (privacy gate): CON-02, CON-03, PII-01, PII-02
- Open S2 FAIL (bidding gate): CNV-04, CNV-06, SST-02
```

## What the agent does with each finding

- CON-02 and CON-03: runs 2 and 3 of the no-choice capture (one throttled, E-02) show the same LinkedIn request, so the finding is confirmed and goes to P1. The loader is the theme script, so a GTM consent setting cannot fix it (FX-1).
- PII-01 and PII-02: the invalid submission is repeated once in a fresh context and leaks again. Only the test identity leaked, so no real personal data is involved; the agent tells the user the same day and drafts the note for the privacy owner, who decides whether to request GA4 data deletion for the affected dates. P1 (FX-2).
- INV-01: the growth lead confirms the TikTok pixel is left over from a May trial. The privacy owner confirms the CMP lists TikTok in the marketing category, so the gate stays S4 and the item is P4. Had TikTok been missing from the CMP, the gate would rise to S1.
- CNV-04: a second approved order, ACME-TEST-0043, also sends 118.8 where the net price is 99.00: the value includes 20 percent VAT, against the value rule. P2 (FX-5).
- SST-02: the paid social owner's Test Events screenshot (E-10) shows two Purchase events per test order, not deduplicated, and dataset stats by source (E-16) show browser and server purchases each close to the order count. P2 (FX-3).
- CNV-06 comes from the GAQL read (E-14), confirmed by the paid media lead's screenshot of the goal view: "Paid plan purchase" (WEBPAGE) and "purchase (GA4)" (GOOGLE_ANALYTICS_4_PURCHASE) are both primary in category PURCHASE. P2 (FX-4).
- XDS-03: the GA4 read shows pay.example.net as a session source, confirming the browser evidence. P3.
- XDS-05 and CFG-04 share one cause: enhanced measurement history page views are on while GTM also sends SPA page views (the Admin API read of the stream's enhanced measurement settings, E-13). One fix item, not two. P3.
- CID-01, CID-04, CID-05 and CID-06: the redirect is its own item; the lowercasing is one item covering the form POST, the CRM record (E-09 shows tester-123 in the custom property and TeSter-123 in HubSpot's own property) and the export trend from 2026-09-15. CID-08 moves with it: the Data Manager HubSpot connection maps the custom `gclid` property, and its rejected rows start on the same date. P3 (FX-6).
- EVT-02, EVT-05, EVT-06, CID-09, CFG-02, GOV-02 and GOV-03 go to P4 with their owners.
- The journey's not-checked list names app.example.com/api/signup as an own-host request that was not searched. It is the app's own signup endpoint, and stack.md lists no other first-party collector, so no `--collector-hosts` rerun is needed.
- Not checked: CON-07 and SST-06 (no server container access yet), CON-08 (consent-debugging booked), CID-07 (deal association not tested). Each has an owner and the access that would close it.

## Evidence ledger

- E-00: scope sheet, signed 2026-10-27.
- E-01: example-acme-no-choice.har.json and its har-audit output, run 1, 2026-10-28 09:30 UTC.
- E-02: no-choice runs 2 and 3, run 3 throttled to Slow 4G; same findings.
- E-03: example-acme-reject.har.json and its har-audit output, run 1, 09:40 UTC, click recorded at +8 seconds.
- E-04: accept captures of home and /pricing/, three runs, har-audit --scenario accept output.
- E-05: Tag Assistant Consent tab, first event on home, UK, run by the web analytics lead.
- E-06: example-acme-journey.har.json and its har-audit output, run 1, 10:00 UTC.
- E-07: journey runs 2 and 3 for the cross-domain and SPA cases, fresh clicks; same findings.
- E-08: SPA route fixture sheet, 8 steps, 5 page views expected, written before the run.
- E-09: HubSpot record read for the test lead: record ID and attribution fields only.
- E-10: test orders ACME-TEST-0042 and ACME-TEST-0043: order records, GA4 DebugView, Meta Test Events screenshot from the paid social owner.
- E-11: negative tests: validation error, declined test card, abandoned payment, reload, double click.
- E-12: GA4 MCP reads, 2026-10-29: property details, event names and key events for 28 days, custom definitions, links, session sources.
- E-13: GA4 Admin API reads (data retention, enhanced measurement, data redaction) and the web analytics lead's Admin screenshots (data filters, unwanted referrals, configured domains).
- E-14: Google Ads MCP GAQL reads, 2026-10-29: customer settings, landing pages and templates, conversion actions.
- E-15: Google Ads offline upload summaries for the Data Manager HubSpot connection.
- E-16: Meta reads: dataset details, stats by event source for 28 days, quality, event setup rules, custom conversions.
- E-17: crm-attribution-coverage output on the CRM export, one run per click ID property.
- E-18: order export joined to GA4 transaction IDs: 212 eligible orders over 28 completed days.
- E-19: CRM-accepted demo requests against GA4 generate_lead and the Demo request action, 28 completed days.
- E-20: published gtm.js read (light path); full gtm-container-audit booked.
- E-21: GTM user list from the account administrator; owners in stack.md.
- E-22: monitoring review: no alert or scheduled check on any tier-1 conversion.
- E-23: the two plan sheet tabs.
- E-24: BigQuery read-only queries: purchase transaction IDs and debug_mode traffic, 28 days.

## Two fix list entries

```markdown
### FX-1 Stop the theme-coded LinkedIn tag before consent (P1; CON-02, CON-03, INV-04)
- Finding: the LinkedIn Insight Tag (partner 1234567) loads from /assets/theme.js and sends
  px.ads.linkedin.com/collect before any choice and after reject on every www page (E-01, E-02, E-03).
- Affects: every UK and EEA visitor who has not accepted; LinkedIn is not used for bidding.
- System and owner: CMS theme, web team applies; web analytics lead verifies.
- Draft change: remove the Insight Tag snippet from /assets/theme.js. Add the LinkedIn Insight Tag
  in GTM-XXXXXXX with "Require additional consent for tag to fire" set to ad_storage,
  ad_user_data and ad_personalization, firing on all www pages.
- Do not: add a consent condition in GTM while the theme copy stays; that changes nothing.
- Test: no-choice and reject captures on home and /pricing/ show zero LinkedIn requests in
  3 of 3 runs, one throttled; an accept capture shows exactly one collect request per page.
- Test approval and data: web analytics lead, live site, UK egress, approval E-00.
- Rollback: republish the previous theme release and container version. Does not undo: data
  already sent to LinkedIn.
- Report annotation: growth lead annotates LinkedIn audience size from the release date.
- Effort: S. Hand-off skill: consent-debugging for the full region and withdrawal matrix.

### FX-3 Send the order-based eventID on the browser Purchase (P2; SST-02)
- Finding: the server container sends Purchase with event_id = order reference; the browser
  Purchase has no eventID, so Meta keeps both copies (E-06, E-10, E-16).
- Affects: every Meta purchase; Meta campaigns bid on Purchase.
- System and owner: app success page and GTM-XXXXXXX; app team and web analytics lead apply;
  paid social owner verifies in Events Manager.
- Draft change: push the order reference to the data layer on the success page and set it as
  the Meta Purchase eventID, the same value the server copy uses as event_id.
- Do not: switch off the server copy; it is the copy that survives blockers and late payments.
- Test: two approved test orders show deduplicated pairs in Test Events and one purchase each in
  dataset stats; har-audit SST-02 passes on the journey capture.
- Test approval and data: paid social owner; test card; ACME-TEST-0044 and ACME-TEST-0045.
- Rollback: republish the previous container version. Does not undo: duplicates already counted.
- Numbers that move: Meta purchases fall by about half from release; the paid social owner is
  told before release so the drop is not read as performance.
- Effort: M. Hand-off skill: meta-capi-setup (planned).
```

## Regression fixtures

Four fixtures hold the cases the scripts must keep getting right. After changing har-audit, run all four from the skill folder, then the three Acme captures above, and compare.

```bash
node scripts/har-audit.mjs assets/har-fixture-consent.har.json --scenario no-choice --mode advanced \
  --uet-mode advanced --expect-ids G-XXXXXXXXXX,1234567890123456,12345678,XXXXXXXXXXXXXXXX \
  --allow-hosts cdn.consent.example.org --pii jane@example.com --collector-hosts smetrics.example.com
```

Expected: `15 FAIL, 4 WARN, 8 PASS`, exit 1, and per case:

- CMP host request: no finding.
- GA4 page_view with gcs G100: counted as denied ("1 other(s) carried denied state as documented").
- GA4 scroll with gcs G111 before a choice: CON-04 FAIL.
- Hotjar event request: CON-02 FAIL, and INV-01 FAIL because Hotjar is not expected.
- Meta fbevents.js loaded with no /tr request: CON-02 WARN.
- LinkedIn collect loaded from the theme script: CON-02 FAIL, INV-04 WARN and INV-01 FAIL.
- TikTok body carrying the SHA-256 of jane@example.com before a choice: CON-02 FAIL, PII-03 FAIL and INV-01 FAIL.
- Microsoft UET pageLoad beacon to bat.bing.net with asc=D: CON-02 PASS under `--uet-mode advanced`. UET custom event with asc=G: CON-02 FAIL. Without `--uet-mode advanced` both fall in one CON-02 FAIL line and the run ends `15 FAIL, 4 WARN, 7 PASS`.
- OpenAI Ads pixel page_viewed to bzr.openai.com before a choice: CON-02 FAIL.
- Unlisted host visitor-id.example.org loaded by GTM and posting the test email: CON-02 WARN asking for classification, and PII-01 FAIL "reached unclassified host visitor-id.example.org", so no PII-01 PASS line.
- Cookie header with _ga, _fbp and cmp_state before a choice (sensitive export): CON-04 FAIL for _ga, CON-02 FAIL for _fbp, and an INFO line asking for `--essential-cookies` to judge cmp_state.
- Adobe-style /b/ss/ hit on smetrics.example.com before a choice, carrying the test email, with `--collector-hosts smetrics.example.com`: an inventory row, CON-02 FAIL and PII-01 FAIL "reached own-host collector smetrics.example.com". Without the flag both are WARNs naming an own-host collector candidate, and the run ends `13 FAIL, 6 WARN, 8 PASS`.
- PostHog path proxied at www.example.com/ingest/e/ with no flag naming it: CON-02 WARN "own-host collector candidate".

```bash
node scripts/har-audit.mjs assets/har-fixture-journey.har.json --scenario journey \
  --own-domains example.com,example.net --markers gclid=TeSter-123,utm_campaign=TeSter-123-campaign \
  --pii jane@example.com --server-routes meta,openai
```

Expected: `13 FAIL, 5 WARN, 9 PASS`, exit 1, and per case:

- GA4 POST batch with page_view and purchase lines: both parsed (the page view appears in the sequence; the purchase is judged).
- GA4 through a gateway path on www.example.com: recognised as a first-party route (SST-05 PASS, served from the page's own host).
- GA4 page_view copy to stats.g.doubleclick.net 40 ms after the google-analytics.com hit: no finding, because it is a Google signals copy (no second route, no SST-05 line, no page view pair).
- GA4 purchase with an empty transaction_id: CNV-03 FAIL.
- Two GA4 purchases with transaction_id order-1001: CNV-02 WARN.
- Meta Purchase twice with the same eid: CNV-02 FAIL.
- Meta Purchase without eid while `--server-routes meta`: SST-02 FAIL.
- Google Ads conversion twice without an order ID: CNV-02 FAIL.
- TikTok CompletePayment with event_id: no finding.
- OpenAI Ads order_created without event_id while `--server-routes openai`: SST-03 FAIL, listing the keys seen.
- 301 dropping utm_campaign: CID-01 FAIL.
- HubSpot submission with tester-123: CID-04 FAIL.
- Embedded frame from booking.example.org whose URL carries no marker: CID-04 WARN.
- Raw test email in a GA4 dl: PII-01 FAIL.
- Client ID change at an own-domain boundary with _gl present: XDS-01 FAIL (XDS-02 FAIL follows from the new session).
- Return with a payment host referrer and a session start: XDS-03 FAIL.
- Two page_view events for one location 30 ms apart: XDS-05 WARN.
- Event with 26 parameters: EVT-03 FAIL. Parameter value of 120 characters: EVT-03 WARN.
- The same purchase to google-analytics.com and to sgtm.example.com: SST-04 FAIL.
- A route on a *.run.app host: SST-05 WARN.

```bash
node scripts/har-audit.mjs assets/har-fixture-edges.har.json --scenario journey \
  --own-domains example.com --pii jane@example.com,+15555550100
```

Expected: `8 FAIL, 3 WARN, 7 PASS`, exit 1, and per case:

- Arrival from reviews.example.org with a session start: no XDS-03 finding, because a first arrival from outside is an entry, not a return.
- A thank-you URL carrying pat%40example.org: PII-02 FAIL for the page URL, and PII-01 FAIL "reached GA4 in 2 request(s) (parameters dl, dr)", because the address arrives double-encoded in dl and rides on in dr on the next page. Both print the address masked.
- A GA4 parameter holding a retina image name with an at sign (`hero-logo%402x.png` in the request): no finding, because asset names are not email addresses.
- Hosted payment on pay.example.com, an owned subdomain that sends no GA4: INV-05 WARN for that page, no XDS-04 finding for the hop into it, and XDS-03 FAIL for the return, which starts a new session with pay.example.com as referrer.
- Unlisted chat widget on chat-widget.example.org receiving a POST on an owned page: INV-06 WARN asking for classification.
- Mixpanel track POST whose `data` field holds base64 JSON with the test email: PII-01 FAIL "reached Mixpanel in 1 request(s) (parameters data (base64))". mixpanel-js sends this format on a proxy host or when configured, and JSON by default on mixpanel.com hosts.
- Snowplow POST to sp.example.com, an owned host, with no `--collector-hosts`: INV-06 WARN "own-host collector candidate".
- A thank-you URL carrying phone=5555550100, the national form of +15555550100, and the test email as a base64 `ref` value: PII-02 FAIL and PII-01 FAIL "reached GA4" for each test value.

```bash
node scripts/har-audit.mjs assets/har-fixture-accept.har.json --scenario accept --choice-at 6 \
  --expect-ids G-XXXXXXXXXX,1234567890123456,1234567 --allow-hosts consent.example.org --pii jane@example.com
```

Expected: `0 FAIL, 4 WARN, 10 PASS`, exit 0, and per case:

- The CMP logs accept all with a GET at +6.0s, so the moment comes from `--choice-at` (the capture line says so). Without the flag the capture line asks for it.
- GA4 page_view at +1.0s with gcs G100 and Meta PageView after the choice: CON-05 PASS for each.
- Google requests after accept still carrying gcs G100: CON-05 WARN (the consent update never reached the Google tags).
- LinkedIn partner 1234567 expected but silent: CON-05 WARN.
- Two Microsoft Clarity POSTs marked gzip on the Accept header, one garbled by the export and one with no body text: PII-01 WARN "2 request(s) to Microsoft Clarity carried bodies the script cannot read" in place of the PII-01 PASS line, the same line under not checked, and INV-01 WARN because /collect carries no ID.

Refusals: a capture with no startedDateTime, with some entries missing it, or with one time on every entry exits 2 with a request to export again, and so does an unknown destination key such as `--server-routes facebook`. A no-choice or reject run without `--mode` exits 2 with `pass --mode basic|advanced from the scope sheet`; an accept or journey run prints `(default)` after the mode in its first line.

score-audit regression: the Acme checklist must give 59.5, 94.4 percent and "Fail: privacy, grade F". With the four S1 FAILs set to PASS it must give 70.9 and "Grade capped at D: bidding integrity". A checklist at 60 to 79.9 percent coverage with an open S2 FAIL must say "Grade capped at D: bidding integrity (provisional)", and one under 60 percent must withhold the score and say "Insufficient evidence; bidding gate open".
