CRE P4 Rails Audit — every rail/deny rule vs its failing-path test
Date: 2026-08-17
Lane: cre-p4-rails (session that built P1, 0a6d36de)
Scope: cells 38 + 39 compliance rails and deny rules. Additive test
files only — no rail code changed, no other lane's files edited.
Gate satisfied: plan-of-record P4 gate — "every deny-list rule has a
test that fails when the rule is removed."
Method
Grep-driven matrix: every failure reason emitted by
src/cells/cell39/outreach_cell/passport.py, every term in
outreach_cell/voice.py, every key in the Cell 38 deny/PII frozensets
(cre_mappers.DENY_LISTED_FIELDS, RAW_PERSON_FIELDS,
prospecting_cell/profiles.BLOCKED_TARGETING_FEATURES), mapped against
tests/cre_outreach/** and src/cells/cell38/tests/**. Commands and
counts are reproducible from the tree at this commit's parent.
Findings (pre-fix, measured)
| Rail surface | Rules | Seeded failing-path tests | Verdict |
|---|---|---|---|
| Passport check 1 (quality) reasons | 8 | 8 | covered |
| Passport check 2 (compliance) reasons | 9 | 5 | 4 branches untested: tcpa_consent_basis_missing, license_disclosure_unconfigured, sender_postal_address_unconfigured (+recipient_bounce_suppressed in check 5) |
| Passport check 3 (market freshness) | 1 | 1 | covered |
| Passport check 4 (claims / O2 "80%") | 2 | 2 | covered |
| Passport check 5 (opt-out) reasons | 2 | 1 | recipient_bounce_suppressed untested |
FORBIDDEN_TERMS_BASE |
13 | 2 | 11 terms unseeded |
FORBIDDEN_TERMS_RESIDENTIAL (Fair-Housing steering) |
12 | 2 | 10 terms unseeded |
Bounce classification (classify_bounce) |
hard/soft/none/conservative | 0 direct | untested |
Cell 38 DENY_LISTED_FIELDS |
31 keys | 7 | 24 keys unseeded |
Cell 38 BLOCKED_TARGETING_FEATURES |
32 keys | 12 | 20 keys unseeded |
Cell 38 RAW_PERSON_FIELDS |
15 keys | 5 | 10 keys unseeded |
| Freshness gate (cell 38 submarket) | 4 failing paths | 4 | covered (P1) |
| Consent gates (parse fail-closed; person-resolution) | 2 | 2 | covered |
| AUTO_SEND / holdout preconditions | 2 | 2 | covered (P3, AST-asserted) |
The exposure class: a rule silently REMOVED from a list (one line of diff) would not have turned any test red for the uncovered entries — precisely the failure mode rule 01 names ("a gate that does not cover a surface cannot defend it").
Fix (this landing)
Two additive batteries, each using two mechanisms per rail:
- Literal ratchet lists asserted
⊆the shipped set — removing a shipped rule turns red; adding one never requires editing the test (floor, not equality — the TRUTH 5.1.1 trap avoided). - Seeded behaviour — every entry driven through the real refusal path (full passport evaluation / mapper / feature gate) with the specific check and reason asserted.
| File | Tests | Covers |
|---|---|---|
tests/cre_outreach/test_p4_rails_coverage.py |
42 | all 13+12 forbidden terms through the passport (both profiles), steering-term residential scoping pinned in both directions, the 4 untested passport branches, bounce threshold both directions (1 bounce ≠ suppressed; 2 = suppressed), classify_bounce 6 cases incl. the conservative default, erasure-keeps-suppression, residential control draft passes (non-vacuity) |
src/cells/cell38/tests/test_p4_deny_coverage.py |
161 | all 31 deny keys × both roster mappers, all 32 blocked features (alone + hidden among legal features), all 15 raw-person fields × both mappers, case-insensitivity, intake↔scoring rail-divergence guard |
The TCPA passport branch is reached by dataclasses.replace on a parsed
match — parse_prospect_match itself refuses an empty basis (fail-closed,
already tested), so the passport re-check is exercised as designed: a
basis stripped AFTER intake.
Measured results (fresh run, this tree)
tests/cre_outreach/: 119 passed (77 existing + 42 new)src/cells/cell38/tests/: 244 passed (83 existing + 161 new)src/shared/virtuoso_models/tests/: 76 passed, 2 skipped (untouched)
Honest limits
- This audit covers rails that EXIST in the tree. P4's residential-profile build scope beyond rails (jurisdiction config surfaces, residential voice exemplars) is not audited here — nothing to audit until built.
STATE_COMMERCIAL_EMAIL_RULESships empty marker lists (CA/UT) — the mechanism is tested only via the absent-jurisdiction path; per-state markers land with counsel review, and tests must land WITH them.- The DNC checker itself is an interface; fail-closed-when-absent and hit-blocks are tested, but no real DNC provider is wired (P2/P5 work, operator-gated).