# Worked example: Acme Reports (fictional)

Acme Reports is a fictional B2B SaaS product with workspaces, 14-day trials, and monthly and annual plans, selling in the US, UK and EU. Three systems email the same people: Customer.io for product lifecycle, HubSpot for the newsletter, webinars and a trial nurture, and the billing platform for receipts and failed payments. Depth: Full, after the lifecycle lead and privacy owner approved the seed test plan. Windows: 2026-09-15 to 2026-10-14 for inventory counts, 2026-10-08 to 2026-10-14 for the send log sample, 90 days for imports and preference changes.

## Files

- [../assets/example-acme-stack.md](../assets/example-acme-stack.md): the `stack.md` and decisions excerpt the agent reads first.
- [../assets/example-acme-inventory.csv](../assets/example-acme-inventory.csv): 12 rows, one per live journey, recurring send and external owner, with sends per journey for JC09.
- [../assets/example-acme-sends.csv](../assets/example-acme-sends.csv): 75 sends to 22 pseudonymous profiles (p001 to p025) from both senders.
- [../assets/example-acme-preference-changes.csv](../assets/example-acme-preference-changes.csv): 6 preference changes with their sources.
- [../assets/example-acme-holdout.csv](../assets/example-acme-holdout.csv): the sampled holdout members p021 to p025, without membership dates. The holdout is set per workspace, so the list carries every profile in each held-out workspace.
- [../assets/example-acme-newsletter-source.txt](../assets/example-acme-newsletter-source.txt) and [../assets/example-acme-lifecycle-source.txt](../assets/example-acme-lifecycle-source.txt): raw sources saved from a seed inbox.
- [../assets/example-acme-gap-list.csv](../assets/example-acme-gap-list.csv): the finished gap list, 23 rows.

## Commands

Run from the skill folder. In a real audit, call each script by its path and keep the exports in the audit folder of the user's project.

```bash
python3 scripts/inventory_audit.py assets/example-acme-inventory.csv --model saas \
  --global-holdout yes --policy-caps-all-optional yes --as-of 2026-10-14
python3 scripts/message_pressure.py assets/example-acme-sends.csv \
  --prefs assets/example-acme-preference-changes.csv --holdout assets/example-acme-holdout.csv \
  --cap-24h 2 --cap-7d 5 --quiet 21:00-08:00
python3 scripts/check_message_headers.py assets/example-acme-newsletter-source.txt \
  assets/example-acme-lifecycle-source.txt --class marketing --microsoft-volume low
python3 scripts/holdout_math.py plan --baseline 0.21 --eligible-per-period 1200 \
  --control-share 0.10 --periods 3 --target-mde-pp 3
python3 scripts/holdout_math.py result --treat-n 3310 --treat-conv 745 \
  --control-n 290 --control-conv 58 --expected-control-share 0.10
python3 scripts/gap_list_check.py assets/example-acme-gap-list.csv
```

Where each flag comes from:

- `--model saas`: stack.md.
- `--global-holdout yes`: the 2026-07-01 holdout decision.
- `--policy-caps-all-optional yes`, `--cap-24h 2`, `--cap-7d 5` and `--quiet 21:00-08:00`: the 2026-06-01 cap policy, which counts rolling windows, so `--window` stays rolling.
- `--as-of 2026-10-14`: the last day of the window.
- `--microsoft-volume low`: example.com sends about 3,100 a day to Microsoft consumer mailboxes, below 5,000 (DL02). `--volume` stays bulk: about 7,000 personal Gmail recipients per send day.

## Expected output (excerpts)

inventory_audit.py ends with `9 FAIL, 15 WARN, 15 PASS, 5 INFO. Nothing was changed.` and exit status 1:

```text
FAIL JC02 coverage-ratio: journey "Activation: connect first data source" [cio-12] entered 186 of 300 expected (ratio 0.62, below 0.75): trigger leakage. ...
FAIL JC03 stale-live-journey: journey "Usage decline check-in" [cio-15] is live with 0 entries in the window while the source shows 57 qualifying occurrences; last entry 2026-09-03 (41 days before 2026-10-14). ...
FAIL JC04 exit-on-goal: journey "Onboarding tips" [cio-03] has 4 message steps and up to 72 hours of delay but no exit on its goal (exit_on_goal: no). ...
FAIL FC02 config-coverage: journey "Acme Monthly newsletter" [hs-05] is a live marketing journey with frequency_capped: no while the policy caps all optional messages.
FAIL PC07 transactional-misuse: journey "Feature announcement: scheduled reports" [cio-21] is a promotional type (product_updates) classed transactional, so it bypasses preferences and caps. ...
WARN JC01 expected-journeys: required type renewal_notice (revenue-critical) has no live journey and no external owner. Priority hint P1. ...
WARN JC07 collisions: 2 live or external journeys share type trial_expiry: cio-07 "Trial ending in 3 days" (customerio, live); hs-02 "Trial expiry nurture" (hubspot, live). ...
WARN TD01 authoritative-source: journey "Activation: connect first data source" [cio-12] is triggered client-side (trigger_source: client) on an account state (workspace_created). ...
PASS JC09 live-journey-not-sending: 10 live journeys with entries sent messages and report no step that does not send: cio-12, cio-03, cio-07, hs-02, cio-18, hs-05, hs-06, cio-21, cio-20, cio-09. ...
```

The other FAILs are JC04 on hs-02 and cio-18 and FC02 on hs-02 and hs-06. The WARNs add JC05 on four journeys, JC06 on hs-02 and cio-09, JC07 on the payment_failure pair, JC08 on cio-18 and cio-09, and MH02 on the three HubSpot journeys. INFO lists the recommended gaps: signup_completion, usage_limit, upgrade_or_expansion, cancellation, winback. The coverage-by-stage block shows the stages with no journey at all:

```text
  retention: cio-15 (customerio, live); bill-01 (billing, external); cio-18 (customerio, live). Missing required: renewal_notice. Missing recommended: cancellation.
  expansion: no live or external journey. Missing recommended: upgrade_or_expansion.
  winback: no live or external journey. Missing recommended: winback.
```

message_pressure.py ends with `6 FAIL, 1 WARN, 0 PASS. Nothing was changed.` and exit status 1:

```text
  Recipients with counted sends: 21 of 22 profiles in the log (distribution below covers these).
  Peak in any rolling 24 hours: p50 1, p90 3, p99 3, max 3
FAIL FC05 observed-pressure: 3 recipients above 2 in a rolling 24 hours: p004 (3), p009 (3), p013 (3)
FAIL FC05 observed-pressure: 1 recipient above 5 in a rolling 7 days: p013 (6)
FAIL FC04 cross-sender-budget: cross-sender: 3 of 3 over-cap recipients were within every sender's own cap (no shared budget across senders)
  Keys: 5 of 5 preference-log profiles appear in the send log.
FAIL PC02 sends-after-opt-out: 1 marketing send more than 48 hours after a restrictive change in its scope, or after a stale write reversed it (hubspot 1)
  p017 hubspot hs-05 topic:newsletter, 76.8 hours after the change at 2026-10-09T09:15Z (preference_centre); a stale write reversed it at 2026-10-10T02:00Z
FAIL PC01 authority: 1 suspected stale write: an 'on' from a sync, import or unclassified source after an 'off' with no customer action between; restriction kept
  Keys: 2 of 5 holdout members appear in the send log.
FAIL MH03 contamination: 1 marketing send to holdout members (hubspot 1; holdout list of 5)
  Membership dates not given: check the earliest flagged send against the assignment date (assigned_at and released_at columns limit the count to membership).
WARN FC07 quiet-hours: 1 counted non-SMS send inside 21:00-08:00 recipient local time
    cio-07 + hs-02: 4 profiles
```

check_message_headers.py ends with `2 files: 2 FAIL, 2 WARN, 8 PASS. Nothing was changed.` and exit status 1. The newsletter: DL05 FAIL (dmarc=fail), DL06 PASS, DL04 WARN (passing DKIM d=example.net does not align), DL07 FAIL (list-unsubscribe-post is not in h=), DL07 WARN (preferences link only), DL08 PASS. The lifecycle message passes all six. Each receiver line names the authserv-id mx.example.org.

holdout_math.py plan prints `Minimum detectable effect: 6.34 percentage points`, then `Target 3.00 pp: needs 13.4 periods at a 10% control share (control 1,608 units), or 4.8 periods at 50/50 (2,894 units per arm)` and MH09 WARN, exit status 0.

holdout_math.py result prints `Difference: +2.51 percentage points (relative lift +12.5%)`, `95% interval for the difference (unpooled): -2.31 to +7.33 percentage points`, `Not proven: the interval includes 0.` and `FAIL MH05 sample-ratio: observed control share 8.06% against the intended 10.00%; chi-square 15.12, p = 0.000101 (below 0.0005)`, exit status 1. No NOT RELIABLE line: the control arm has 58 conversions and 232 non-conversions, well above the small-count limit of 5.

gap_list_check.py prints the 23 rows in priority and score order and ends with `23 rows: 2 P0, 10 P1, 10 P2, 1 P3; 0 ERROR, 0 NOTE. Nothing was changed.`, exit status 0.

## What the agent does with it

- JC02 and TD01, activation at 0.62: entries split by source show web signups only; API and mobile signups never emit `workspace_created`. JC02 FAIL, confirmed by data. TD01 stays WARN: the trigger leaks rather than over-admits, so its FAIL condition (entries above the source, or failed attempts entering) is not met. The fix is a backend event whose contract comes from tracking-plan-builder. G-09.
- JC03 and TD06, usage decline: the tracking plan shows `weekly_reports_dropped` renamed to `report_volume_dropped` on 2026-09-03. FAIL, confirmed by data. G-10.
- JC09 PASS: Customer.io and HubSpot reports show sends for every live journey with entries, and no message step in a non-sending status (read 2026-10-14).
- JC04 and JC05 on cio-03: seed S02 finished setup during the first delay and still received tip 2. JC04 FAIL, confirmed. The agent read the copy: the tips make no claim about the account's state, so JC05 stays WARN and shares the row. G-06.
- JC07, JC04, JC05 and JC06 on the trial pair: cio-07 exits on subscription; hs-02 has no exit, and the send log shows both reaching 4 sampled profiles within 24 hours, so JC07 becomes FAIL. G-07.
- JC04, JC07, JC05 and JC08 on cio-18: the Customer.io payment reminder has no exit on `invoice_paid`, duplicates the billing platform's dunning and has no owner. Configuration-only: there is no billing send log, and the seed test needs a failed payment in billing test mode that billing ops could not schedule. G-11.
- FC02, FC04, FC05: HubSpot has no frequency limit and no shared budget; all 3 over-cap recipients were within each sender's own cap. G-05.
- PC01 and PC02: the nightly warehouse sync rewrote p017's newsletter opt-out to "on" at 02:00 UTC, and the next newsletter went out 76.8 hours after the opt-out. A send after a stale write is PC02 FAIL at any delay. Seed S11 in the test workspace reproduced it. P0, G-02. The agent told the user as soon as S11 confirmed it, with CR-2 drafted for the privacy owner, then carried on.
- PC07: cio-21 is a feature announcement sent as transactional "to reach everyone". FAIL, configuration-only with the setting and copy quoted; no seed test because it was a one-off send. G-03. In the send log its class is marketing, by purpose, so its sends would count toward PC02 and MH03; it went out on 2026-10-02, before the sample week.
- MH03, MH05, MH08 and MH02: HubSpot reads holdout_group nowhere, so p023 got the newsletter (seed S08 confirmed). The holdout list carried no membership dates, so the agent checked the flagged send against the assignment: holdout_group is set at workspace creation and never reshuffled (MH10), and p023's workspace predates the window, so the send counts. The quarterly claim "onboarding lifts activation by 2.5 points" comes from a HubSpot export that dropped control workspaces without a marketing contact after assignment. That analyses a subset (MH08) and explains the sample ratio mismatch (MH05). The lift is not proven and the split is invalid. G-08.
- MH01 and MH09: "lifecycle drove USD 84,000" is last-touch click credit. Only HubSpot reports attributed revenue, so nothing is double counted across senders. The 10% holdout cannot detect less than about 6.3 points in 3 months; the owner chooses between a 50/50 onboarding test for about 5 months and a coarse readout. G-20.
- DL04, DL05, DL07, DL09 and DL16: the newsletter fails alignment, its one-click header is unsigned, and Postmaster shows Needs work. HubSpot bounce counts by provider and reply code (no recipient addresses) show 310 Gmail deferrals with 421 4.7.32 (From not aligned) on the 2026-10-12 send, so Gmail has started enforcing. About 7,000 personal Gmail recipients per send day make example.com a permanent Gmail bulk sender, so `--volume bulk` is right. Confirmed: the source came from seed-01's inbox. P0, G-01; the agent raised it as soon as the header check and the bounce counts agreed. The body WARN (preferences link only) is G-13. Spam rate 0.18% on send days is DL10 WARN, G-04.
- DL17: product tips (cio-03) and security alerts (cio-20) go out from the same From address. Gmail's sender guidelines ask for one From address per message category. WARN, G-22.
- Reading the raw source beyond the script: newsletter links and the open pixel sit on example.net, the provider's shared domain (DL13 WARN, G-14).

## Every other rule

The report gives every rule a status. The ones not discussed above:

- Triggers and event data: TD02 PASS (the TD02 query found no copies of one occurrence in 30 days). TD03 PASS (the CDP sends `messageId`, which becomes the Customer.io event `id`). TD04 PASS (no import or backfill touched trigger events or filter fields in 90 days). TD05 PASS (trial_end_date syncs nightly; the shortest delay that reads it is 48 hours). TD07 WARN (1.4% duplicate HubSpot contacts, no mixed-consent clusters; G-21). TD08 WARN (31% of HubSpot contacts have no timezone; G-18).
- Segments: SG01 PASS (all 4 segments in use have written definitions). SG02 and SG03 WARN (G-16). SG04 PASS (internal and seed profiles are flagged at creation and excluded). SG05 PASS (active_workspaces is within 4% of the warehouse count). SG06 NOT CHECKED (no random bucket tests are running).
- Caps and suppression: FC01 PASS (the 2026-06-01 policy names identity, channel, counted class, rolling windows, limits, exemptions and blocked-message treatment). FC03 PASS (Customer.io's relative windows match the rolling policy). FC06 PASS (auto-retry is off on the Customer.io limit; HubSpot does not resend skipped emails). FC07 WARN (G-18). FC08 WARN (G-17). FC09 NOT CHECKED (HubSpot showed no block reasons for the window).
- Preferences and consent: PC03 PASS for the product tips scope (seed S04: suppressed within 2 minutes in Customer.io, the only sender of that topic; message_pressure.py found no send between 1 and 48 hours after a change) and NOT CHECKED for the newsletter until CR-1 ships, because Gmail cannot offer one-click on unsigned headers. PC04 PASS (seed S03: turning product tips off stopped tips while the newsletter continued). PC05 PASS (the PC05 query found no marketing sends to default-state UK or EU profiles in 30 days). PC06 PASS (consent history is kept and nobody treats it as enforcement). PC08 NOT CHECKED (no SMS sender). PC09 PASS (no mixed-consent clusters; seed S06 confirmed nothing reached the shared address). PC10 PASS (the 2026-03-10 decision is signed by the privacy owner).
- Deliverability: DL01 PASS (DMARC aggregate reports show only the three inventoried senders). DL02 INFO (Gmail bulk and permanent; about 3,100 Microsoft consumer recipients per send day, below its 5,000 a day threshold; Yahoo publishes no number). DL03 PASS (one SPF record, 6 DNS-querying mechanisms, every Return-Path domain authorised). DL04 PASS for the lifecycle stream (the cio1 key is 2,048 bits; its p= value is 392 characters). DL06 and DL08 PASS on both streams. DL11 PASS (newsletter hard bounces 0.4% of attempted deliveries on 2026-10-12). DL12 WARN (G-15). DL16 blocklist lookups on Spamhaus's reputation checker (2026-10-14): example.com is not listed; the sending IPs are shared provider IPs and example.net is the provider's shared domain, and none of them is listed, so there is nothing to raise with either ESP. DL14 and DL15 NOT CHECKED (no SMS or push sender).
- Measurement and holdouts: MH04 PASS (p022 still received a security alert; seed S08 received service messages). MH06 PASS (assignment per workspace for a workspace outcome). MH07 PASS (the outcome and its 14-day window were fixed in the 2026-07-01 decision). MH10 PASS (assigned at creation and never reshuffled).

## Gap list

Scores use `reach x weight x confidence / effort` ([gap-list-and-change-requests.md](gap-list-and-change-requests.md)); every row's evidence, owner, change and test are in the CSV.

- P0 G-01 DL04, DL05, DL07, DL09, DL16: newsletter fails DMARC alignment, one-click is unsigned and Gmail defers part of it. Reach 48,000, FAIL, confirmed, M. Score 72,000. CR-1.
- P0 G-02 PC01, PC02: nightly sync resubscribes opt-outs. Reach 650, FAIL, confirmed, S. Score 1,950. CR-2.
- P1 G-03 PC07: feature announcement sent as transactional. Reach 3,100, FAIL, configuration-only, S. Score 5,580. CR-3.
- P1 G-04 DL10: Gmail spam rate 0.18% on send days. Reach 7,000, WARN, confirmed, M. Score 3,500. CR-4.
- P1 G-05 FC02, FC04, FC05: HubSpot sends outside the shared cap. Reach 1,300 (14% of the 9,400 people a month who get optional email from both senders), FAIL, confirmed, M. Score 1,950. CR-5.
- P1 G-06 JC04, JC05: onboarding tips keep sending after setup. Reach 410, FAIL, confirmed, S. Score 1,230. CR-6.
- P1 G-07 JC07, JC04, JC05, JC06: trial expiry runs in both tools. Reach 240, FAIL, confirmed, S. Score 720. CR-7.
- P1 G-08 MH03, MH05, MH08, MH02: HubSpot ignores the holdout and the readout drops control workspaces. Reach 120, FAIL, confirmed, S. Score 360. CR-8.
- P1 G-09 JC02, TD01: activation misses API and mobile signups. Reach 114, FAIL, confirmed, M. Score 171. CR-9.
- P1 G-10 JC03, TD06: usage decline journey dead since the rename. Reach 57, FAIL, confirmed, S. Score 171. CR-10.
- P1 G-11 JC04, JC07, JC05, JC08: payment reminder has no exit and duplicates dunning. Reach 38, FAIL, configuration-only, S. Score 68. CR-11.
- P1 G-12 JC01: no renewal notice for annual plans. Reach 40, WARN, configuration-only, M. Score 12. CR-12.
- P2, in score order: G-13 DL07 body link (48,000), G-14 DL13 tracking domain (24,000), G-15 DL12 feedback programmes (9,000), G-16 SG02 and SG03 engagement and sunset (6,500), G-17 FC08 suppression sync (2,820), G-18 FC07 and TD08 send windows and timezones (1,400), G-19 JC05 on hs-06 (840), G-20 MH01 and MH09 claims and power (600), G-21 TD07 duplicates (335), G-22 DL17 one From address for tips and alerts (57).
- P3: G-23 JC06 and JC08 on cio-09 (85).
- Not scored: the five recommended journey types (INFO), listed in the report's coverage map.

CR-1 and CR-2 are written out in [gap-list-and-change-requests.md](gap-list-and-change-requests.md). CR-3 to CR-12 follow the same template.

## Regression fixtures

After changing any script, rerun the six commands above from the skill folder and compare the summary lines and exit codes with this file. Then run the fixtures below, which hold the cases reviewers found the scripts getting wrong:

```bash
python3 scripts/check_message_headers.py assets/regression-microsoft-headers-only.txt \
  assets/regression-split-results-source.txt --class marketing
python3 scripts/check_message_headers.py assets/regression-prefs-url-source.txt \
  assets/regression-non-english-source.txt --class marketing
python3 scripts/check_message_headers.py assets/regression-no-dkim-source.txt --class marketing \
  --volume low --microsoft-volume low
python3 scripts/message_pressure.py assets/regression-pressure-sends.csv \
  --prefs assets/regression-pressure-prefs.csv --holdout assets/regression-holdout.csv \
  --classes marketing,service
python3 scripts/message_pressure.py assets/regression-pressure-no-topic.csv \
  --prefs assets/regression-pressure-prefs.csv --holdout assets/regression-holdout.csv
python3 scripts/message_pressure.py assets/regression-pressure-unknown-class.csv
python3 scripts/message_pressure.py assets/regression-sms-phone-key.csv
python3 scripts/holdout_math.py result --treat-n 380 --treat-conv 30 --control-n 20 \
  --control-conv 0 --expected-control-share 0.05
python3 scripts/inventory_audit.py assets/regression-inventory-counts.csv --model saas \
  --global-holdout yes --policy-caps-all-optional yes --as-of 2026-10-14
python3 scripts/check_message_headers.py assets/regression-dkim-i-subdomain-source.txt \
  assets/regression-word-no-link-source.txt --class marketing
```

Expected:

- Headers: `2 files: 0 FAIL, 0 WARN, 11 PASS. Nothing was changed.`, exit status 0.
  - The Microsoft-format headers-only save prints "authserv-id missing (Microsoft writes none)" with spf, dkim and dmarc passing, and "NOT CHECKED DL07 body" instead of a FAIL: `Result: 0 FAIL, 0 WARN, 5 PASS`.
  - The receiver that writes one header per method prints "4 headers merged from bimi.example.org, dmarc.example.org, dkim-verifier.example.org, spf.example.org", ignores the lower header from mta.example.net, and shows the DKIM domain from header.i: `Result: 0 FAIL, 0 WARN, 6 PASS`.
- Body links: `2 files: 0 FAIL, 1 WARN, 11 PASS. Nothing was changed.`, exit status 0.
  - The preferences fixture has "unsubscribe" only inside its preference-centre URL, in both parts. The script reads the HTML part, where the link says "Manage preferences": `WARN DL07 ... the HTML part has a preferences link but no unsubscribe wording`.
  - The Swedish newsletter's body link "Avsluta prenumerationen" reuses its HTTPS List-Unsubscribe URL: `PASS DL07 ... a link in the HTML part reuses the HTTPS List-Unsubscribe URL`. Its text is quoted-printable, so the decoder runs too.
- Unsigned message at low volume: `1 file: 1 FAIL, 2 WARN, 3 PASS. Nothing was changed.`, exit status 1. `FAIL DL04 dkim: no DKIM-Signature on a marketing message`; DL06 and the DL07 headers are WARN because of the low-volume flags.
- Pressure: `4 FAIL, 1 WARN, 2 PASS. Nothing was changed.`, exit status 1.
  - PC02 FAIL for r04 (50.0 hours after a global opt-out) and r05 (2.0 hours after its opt-out, after a crm_sync stale write).
  - PC03 WARN for r03 (3.0 hours, a second sender waiting on its sync). Its preference change is in Snowflake text and its send in BigQuery text.
  - INFO in flight for r01 (2 minutes after one-click) and r02 (4 minutes after a link).
  - PC08 FAIL for r06: an SMS 30 minutes after STOP (texting gets no grace) and a service text 47.0 hours after it. The service text 2 minutes after STOP is read as the opt-out confirmation and not counted.
  - PC01 FAIL for r05's stale write.
  - MH03 FAIL for r08's send while assigned only. r08's earlier send and r09's send after release are not counted, and r07's service alert never is, even with `--classes marketing,service`.
  - Keys: 6 of 6 preference-log profiles and 3 of 3 holdout members appear in the send log.
- Missing topics and keys: `1 FAIL, 0 WARN, 3 PASS, 2 NOT CHECKED. Nothing was changed.`, exit status 1.
  - The send log has no topic column, so r02's newsletter send 11 days after its topic opt-out cannot be matched: `NOT CHECKED PC02 topic:newsletter`, then `PASS PC02 sends-after-opt-out: for global and channel scopes only`.
  - `Keys: 0 of 3 holdout members appear in the send log.` and `NOT CHECKED MH03 contamination`, not PASS.
  - PC01 FAIL is r05's stale write from the shared preference log.
- Unknown class: exit status 2 with "Found: 'promotional' (2 rows, first row 2)". No check runs on a send log with an unknown class.
- Phone key guard: exit status 2 with "row 3 ... profile_key on an SMS or WhatsApp row is 10 to 15 digits" and no value printed. The 11-digit CRM ID on the email row in row 2 passes.
- Small-count holdout: the unpooled interval reads +5.18 to +10.61 points, then `NOT RELIABLE: an arm has fewer than 5 conversions or 5 non-conversions`, the score interval -8.38 to +11.05 points, `Fisher exact test, two-sided: p = 0.3842` and `Not proven`. MH05 PASS, exit status 0.
- Inventory counts: `0 FAIL, 5 WARN, 5 PASS, 8 INFO. Nothing was changed.`, exit status 0. cio-31 is live with 0 entries and a blank expected_window: `WARN JC03 ... no source count (expected_window blank)`, never a claim about the source. cio-32's dashboard counts with thousands separators are read: `entered 1240 of 1300 expected (ratio 0.95)`.
- DKIM i= and body wording: `2 files: 0 FAIL, 1 WARN, 11 PASS. Nothing was changed.`, exit status 0.
  - Gmail reports `header.i=@news.example.com` for a signature with d=example.com, and the DL07 headers still PASS.
  - The other message has "opt out" in a paragraph of its own and an unrelated link: `WARN DL07 ... not on or within 80 characters of a link in the same block`.
- Bad input: an `.xlsx`, a Windows-1252 or UTF-16 CSV, or a file with NUL bytes exits 2 on inventory_audit.py, message_pressure.py, gap_list_check.py and check_message_headers.py, with no traceback. `nan` or `inf` in a count is a bad value, not a number. Exit 1 always means a FAIL or an ERROR finding.

A different count means the change altered a rule; update this file only when the new result is the intended one.
