The Ungated Lanes

Why Clara emailed "hope you loved the community — apply now!" to a prospect ten minutes after his fair-housing complaint was escalated to humans, and the escalation-crucible extension that makes this class of send impossible to ship again.

2026-08-28 · Camellia incident, same day · Status: Proposed — pending Fede's review · Investigation: agents/008 session

TL;DR. Clara escalated Daniel's fair-housing complaint correctly — then a timer emailed him "hope you loved the community, apply now!" nine minutes later. The "human owns this thread, Clara sends nothing" rule exists, but only at the door texts leave through; emails and every timer-fired template skip it. Fix = one guarded door for every message, on every channel (D1), plus two product calls (D2, D3). The test that keeps it fixed lives in the existing escalation crucible. Daniel is quieted; a human owes him a reply by tomorrow.

Decisions — pick one per row (recommendation first)

D1 · Where does the email lane get gated?

A (recommended) Route every email send that carries a conversationId through applyOutboundPolicy — the same single chokepoint SMS/voice already use (#5400's original design, finished). One gate, one set of exemptions, R131 enforces nobody builds around it.
B Add a standalone escalated-status check inside the two email senders. Faster, but mints a second policy authority that can drift from the dispatcher's.
C Per-caller checks at each send site. Rejected by history — per-site checks are exactly how the lanes diverged.

D2 · Is the post-tour application link an "earned receipt"?

Context: the Aug 18–21 ruling ("only the held topic goes silent") lets receipts the person earned by their own action — tour confirmations, cancellations — through the mute. The question is whether the apply link qualifies.

A (recommended) No. It is a next-step nudge, not a receipt of the person's own action. Muted on escalated threads; it can go out after a human releases the thread. Today's email is the argument: nobody wants "hope you loved it, apply now!" to be the exempt send.
B Conditionally — earned unless the escalation post-dates the tour (i.e., something went wrong at the tour). Closer to intent, but needs ordering logic and creates a third behavior to test.
C Yes, always exempt. Would have sent today's email even with D1 fixed. Listed for completeness.

D3 · Fair-housing backstop — "we should have caught this on outbound"

A (recommended) Deterministic inbound lexicon (voucher, Section 8, housing assistance, discrimination, service/support animal, accessibility, retaliation…) — when an escalation's topic matches, the thread mutes fully: no topic-scoping, no earned-receipt exemption, team notice tagged fair-housing. No LLM in any send path (consistent with the no-blocking-guards doctrine); the lexicon runs once, on inbound, like the life-safety classifier already does.
B Async post-send auditor that reviews every outbound against recent thread sentiment and pages the team on a mismatch. Catches more classes, but catches them after the send.
C Neither for now; rely on D1+D2. The next sensitive escalation gets whatever sends are still classified exempt.

Where things stand right now (ops)

Full detail for engineers — incident timeline, guard-by-guard analysis, harness spec, guard-vision assessment

The escalation mute has holes by lane, not by rule. The "thread is human-owned, Clara sends nothing" gate exists — but only inside the SMS/voice dispatcher. The email senders never consult it. Every timer-fired email (post-tour application links, chase nudges, cadence touches on email threads) bypasses the mute entirely. Today one of them landed on a fair-housing-sensitive escalation.

The incident (Aug 28, all times Denver)

TimeWhat happened
11:45 AMDaniel B. tours Camellia (Zillow lead, booked by Clara Wednesday). Texts "I'm here"; PM notified.
12:33 PMTexts "I'm highly disappointed""Was told you don't take vouchers." Source-of-income is a protected class in Colorado — this is a fair-housing-sensitive complaint.
12:34 PMClara escalates correctly: ack sent, conversation status flips to escalated (human-owned).
12:35 PMDaniel: "It was not a question to be answered." Clara replies anyway — through the topic-scoped release — and the reply speaks about the escalated matter and invents two claims: "I've checked with them on this" (false; the team had only just been emailed) and "I'll follow up with you tomorrow either way" (no mechanism exists that will keep that promise).
12:36 PMEscalation email lands in the Camellia inbox ("this needs a person").
12:45 PMThe tour workflow's 60-minutes-after-tour-start timer fires. sendApplicationLink → Graph email: "How was your tour? We hope you loved the community! If you're ready to take the next step…" Plus the "Application sent" PM notification that surfaced the incident.

Evidence: conversation a05c3c99, tour e8f1ac88, Camellia (prop 1773625953462), prod DDB reads 2026-08-28. Tour row stamps applicationLinkSentAt = 18:45:07Z; conversation status escalated at send time.

Why every guard missed — verified against the code

GuardWhere it livesWhy it didn't stop this send
Escalated-thread outbound gate (policy:thread_escalated, #5400)applyOutboundPolicy in dispatch-envelope.tsThe send never passed through it. The gate is consulted by the SMS/voice envelope dispatcher. The application-link email goes out via sendPropertyEmail (Graph, SendGrid fallback) — neither email sender references the gate. Verified: zero references to the escalated gate in property-graph-sender.ts or email/client.ts. The 2026-08-21 sweep already saw the smoke: a cancellation SMS delivered 6 min post-escalation, noted as "suggesting that send site isn't routed through the gated path" — un-chased then.
Pre-send guard stack of the tour activitysendApplicationLink in activities/tour.tsChecks: tour exists, not already sent, not cancelled/declined, not stale schedule, property not in shadow mode. Never reads conversation status.
The one canonical stop-checkprospectOutreachStopReasonKnows exactly three stop reasons: applied, tour booked, closed. "Escalated / human-owned" is not in its vocabulary — so every cadence that consults it (chase, re-engagement, mint-engagement) shares the blindness.
Suppression (ADR-0092)checkSuppression at the email doorWas consulted — but application_link_email is classified transactional, which suppression allows-and-logs. And no suppression existed for Daniel anyway.
Topic-scoped reply gate ("only the held topic goes silent", Fede 2026-08-09)escalated-topic.ts + release pathReleased the 12:35 reply — which then spoke about the held topic and fabricated a team check-in and a follow-up promise. The release decided whether to reply; nothing constrains a released reply from making claims about the escalation itself.

The SMS leg of the same sender routes through the gated dispatcher — had Daniel's thread anchored to SMS, the gate would plausibly have caught it (inference, not tested; that asymmetry is itself the finding).

Harness: extend escalation-crucible (no new rig)

Per the Harness Atlas, this belongs in scripts/escalation-crucible — the permanent ship-gate for the escalation system (R1–R130, LOGIC/LIVE/JUDGMENT tiers, guard-wired bench). New category K — outbound-lane escalation coverage, R131–R135:

ReqTierProvesToday
R131 Lane inventoryLOGIC (static)Every module that can emit a tenant/prospect-facing send (callers of dispatch(), sendPropertyEmail, the SendGrid leg, voice dispatch) either routes through applyOutboundPolicy or appears in a registry with a named exemption + reason — the spine-runtime-watch drift-test pattern applied to outbound lanes. A new ungated sender fails the build.RED — email lanes ungated
R132 The Daniel fixtureLOGIC (behavioral)Incident replay: post-tour application link fired against a tour whose conversation is escalated → no send, durable skip reason logged. Fixture per Atlas §3.1, provenance a05c3c99 (Camellia 2026-08-28), scrubbed, regressionStatus: known-gap-not-fixed until the fix PR flips it.RED — it sends
R133 Chase & cadence email legsLOGIC (behavioral)Post-tour "did you apply?" chase and cadence touches with an escalated anchored conversation → skipped on the email leg exactly as the SMS leg skips.RED (email leg)
R134 Earned-receipt boundary pinnedLOGICTour confirm/cancel/reschedule receipts still deliver on an escalated thread — the 2026-08-21 lesson (dropped tour texts) must not regress when the lanes close.GREEN — keep it so
R135 Released replies claim nothingJUDGMENTA released reply on an escalated thread never asserts team actions that didn't happen ("I've checked with them") or promises follow-ups no system will keep ("I'll follow up tomorrow").RED — 12:35 reply is the counterexample

Ship discipline per /harness: red runs captured on main before any fix; mutation-control registration for the new drift test; evidence rows (fixture id → red SHA → green SHA) in the suite's results log; atlas row updated. Subscription runner only for R135's judge.

Against the guard-layer long-term vision (10k-ft doc, 2026-08-25)

Read side by side with Clara's Guard Layer — the 10,000-ft Investigation — commissioned three days ago off this same prospect's first email (the dropped application link). Same thread, opposite failure. The vision holds; today exposes three places it is scoped too narrowly.

1 · The vision's chokepoint is a reply chokepoint, not a send chokepoint

The vision's central finding: "the centralized layer that runs before Clara responds already exists — one call site, sixteen mechanisms, fixed order." True — for replies the agent loop generates. The 12:45 email was not a reply. It was a template composed and sent by a Temporal timer. It passed through zero of the sixteen mechanisms: no fair-housing classifier, no promise-backing, no policy gate, and no trace row at all (the 12:35 reply has a full TRACE# with nine safety steps; the 12:45 send has only a message row). Every timer-fired send in the product — application links, chase nudges, cadence touches, reminders, renewal nudges, collections — lives outside the layer the vision is about to arm.

2 · The guards grade the text; the incident was the situation

The 12:35 reply's trace, verbatim: fair-housing Compliant; promise-backing Claim backed — thread was already escalated before this turn; hallucination guard No findings; policy gate absent (disarmed). Each verdict is locally correct and globally wrong:

3 · Ownership needs a reply-side rule, not just an inbound-side one

The topic-scoped gate (Fede, 2026-08-09) decides whether Clara answers an inbound on an escalated thread. Nothing decides what a released reply may say: the 12:35 reply spoke entirely about the held matter. The vision's own line — "SPEAKING ABOUT THE ESCALATED MATTER is the incident" — is enforced only on the way in.

Where the harness slots into the vision's staged ladder

Vision stageThis work
Stage 1 — guard-framework ADR + registry contractR131 lane-inventory drift test is the build-time enforcement of "one chokepoint"; the lane axis + trace obligation go into the ADR.
Stage 2 — judge input enrichmentThread-state input (Amendment 2); R135 commitment/claim check joins the completeness check the head-to-head PoC already proved.
Stage 3 — arm, replay-gatedR132/R133 are the labeled would-alter rows for the timer-send lane; they cannot exist until templated sends trace (Amendment 1).
Stage 5 — golden setDaniel's thread now yields three labeled turns from one conversation (8/25 omission, 8/28 released reply, 8/28 timer send) — first seeds of the regression manifest.

D3 (fair-housing lexicon → full mute) is not a new guard under this framing: it is a thread-state flag the dispatcher gate already reads, widened. Consistent with the vision's "no greenfield rebuild."

PropFlow Docs