What went wrong on Trina H.'s leak call on October 18, and the plan to fix it properly: emergencies always escalate, one address lookup for every channel, and every message to the team shows on the conversation.
Written 2026-10-19 from four read-only investigations of the code and production records. Gera's direction: "real good fix now, no quick fix. Design and plan now, we want best solution." Revised the same day after an independent review by Fable, which corrected the after-call diagnosis, reordered the work, and added the guardrails in section 7. Updated again on the evening of 10-19 after Gera and Fede reviewed the call together: their three stacked failures, the new slices, the decisions they settled, and the PR status are in section 2.
The short version. The emergency alert never fired. MCC got the leak only because Clara forwarded the call. The team got a plain "callback" email, our page never showed that email, the team's replies went missing, and one automated note told Jay "no leak report appears in this thread." Four fixes, planned as small changes, below.
After Gera and Fede's review (10-19): three things stacked up: we had only one of the tenant's four phone numbers, we only knew the building's address and not the unit's, and Clara wouldn't escalate without an address. Fede agreed to drop "no home, no page." The fixes for all three are in progress (section 2).
What to tell Jay: Clara forwarded the call to MCC, which is how Ryan got it. Because of an address bug (fixed that evening), she didn't treat it as an emergency, so our urgent alert and email never went out. We're changing it so an emergency always alerts, even when the address isn't confirmed. Sent 10-19 Gera emailed Jay this correction.
| What | Western Slope | Situs | Note |
|---|---|---|---|
| Units whose own street differs from their building's | 57 units / 4 buildings | 246 units / 13 buildings | JPCO: unknown, because its unit streets are never saved |
| Text messages that can find those units today | 6 of 63 | 2 of 264 | Texts don't look at units at all |
| Prospect calls that can find them | 6 of 63 | 3 of 264 | The street is thrown away before matching |
| Real emergencies that fell to a plain callback, last 30 days | 1 (this one) | 0 | 2 other leak calls gave an address we don't hold |
| Team emails missing from their conversation | 21 | 0 | JPCO 3. All 24 can be re-filed from our own records |
| Maintenance calls where we already know the caller's unit | 11 of 39 (28%) | — | Situs has no maintenance calls in the data yet |
Gera and Fede went through the call together on the afternoon of 10-19. They turned down a quick patch and agreed on a layered fix. Emergencies get zero tolerance, because a few misses lose a customer's trust.
A fourth, from our logs: the after-call check also didn't mark the call as an emergency, and we still don't know why (slice A0). The server-side content check that catches it whatever the cause is in review as #21152.
Gera's side unless noted.
| # | Change | Owner | Status |
|---|---|---|---|
| M1 | Match callers on every phone number a tenant has on file. A match on a secondary number still counts, but Clara confirms the unit | Gera | In progress |
| M2 | The unit detail page shows the unit's own address, not just the building's | Gera | In progress |
| M3 | An emergency is never blocked by not knowing the property; on calls the property becomes nearly optional. Remove the gate in the call script, fix the places that break when there's no property, and send an urgent "call back to confirm the location" note | Gera | In progress |
| M4 | Why the emergency Slack channel stayed silent | Gera | Investigating |
| M5 | Trina H.'s lease reads as both ended in 2024 and active. Sort that out, and bring back the tenant marker on the conversation page | Gera | Investigating |
| M6 | Check Fede's #20946, which merged without review, by replaying "1456 Bunting" | Gera | To do |
| M7 | The unit-address fix ships for all three customers now, including fixing JPCO's unit-street sync. Situs has 246 units with their own street; JPCO has 0 of 232 synced (slice B7) | Gera | To do |
| M8 | A test call: once the address is found, does Clara go on to ask troubleshooting questions? | Gera | To do |
| M9 | A live end-to-end test on Western Slope's test unit after the fixes land. It rings a test on-call list of robot numbers, never the real techs or anyone's personal phone | Gera | To do, after M1 to M3 |
| PR | What | Status |
|---|---|---|
| #21131 | ADR-0145: an emergency always escalates | Merged |
| #21133 | The emergency list read reports what it loaded and alarms when it fails; the check's verdict is recorded (A0b) | Merged |
| #21132 | Backfill: re-file the missing team emails onto their conversations (C2) | Set to merge |
| #21134 | ADR-0146: team messages and replies live on the conversation | In review |
| #21135 | ADR-0147: one address index for every channel | In review |
| #21152 | Emergency content check on calls (A2, part 1) | In review |
The rule (Gera, 10-19): an emergency always goes out, even if we can't confirm the location. We use what we know (the caller's unit on file, the address they said, the line's building) and mark the location "not confirmed."
| # | Change | Proof it worked |
|---|---|---|
| A0 | Done 10-19, cause unknown Why the after-call check stayed silent. All 24 production log lines for the call exist: no switch-read failure, no "list unreadable" line, and the on-call start without a work order was never entered, because the check didn't return "emergency". Replaying the exact production text (1,266 characters) with Western Slope's list gives "emergency" 20 of 20, and loading the list against production with the same arguments returns all 11 emergency types. Both of Fable's explanations are ruled out. Retracted An earlier version of this row blamed the list not reaching the check; that was wrong. What's left is model variance or something unobserved, and it can't be settled because production never recorded the verdict. Since the cause is unknown, the fix that doesn't depend on it, the server-side content check (A2), moves up to right after A1. | Answered with the log lines and replays |
| A0b | Merged 10-19 #21133 records the check's verdict on the dispatch log, logs whether the company's emergency list loaded, and alerts ops when that read fails, so the next miss can be explained | A replayed call shows the verdict on the dispatch log; a forced failed read raises the alert |
| A1 | After the call, an emergency verdict starts the on-call calls on the line's company, carrying the address as spoken and "location not confirmed". Starts from the company and line phone with no building id. Runs without Clara having to cooperate. | Replay the 10-18 call through the after-call sweep into a test company: the on-call list rings |
| A2 | Part 1 (calls) in review as #21152. Server-side content check with the classifier, on calls, texts and emails (extends #19816). Golden case: the 10-18 call, plus two false-alarm controls (a contained drip, a water-bill question) | A "medium" note saying "a lot of water" pages; both controls don't |
| A3 | Every fail-closed read on the chain path alarms; a missed-emergency detector (an emergency verdict or urgent note with no chain start within a few minutes alarms) | A forced read failure on a test company raises the alarm |
| A4 | During the call: remove the "no home, no page" rule from the call script and the address tool, and the server refusal for calls with no building; ask once, then best guess | Bench call with an unknown address and a leak: page sent, marked not confirmed |
| A5 | Confirm, don't ask, for recognized callers; a different stated place becomes a note on the work order and both places go on the alert; a two-home guess never files a work order | Bench: a recognized caller names another street, and the alert shows both |
| A6 | Plain urgent subject and body on team emails, with actions already taken | The 10-18 replay produces the emergency subject above |
agents/clara/lib/voice-agents/maintenance-tenant.ts:163,191,218,223,232; skipped severity question :170-173; recognized callers already skip the address question :234handle-escalate-to-human.ts:371-389; after-call refusal voice-postcall-intake.ts:503-510; after-call fallback that already labels the address unconfirmed :448-470,819-835match-home-answer.ts:38-46handle-create-work-order.ts:965-973 (life-safety already proceeds with a "Unit check" note)src/lib/team-notify/compose.ts:89-98; callback hard-coded at voice-handoff.ts:147; the note tool's urgent email is skipped when the newer notice system is on (handle-capture-unknown-caller-note.ts:546)emergency-alert-notices.ts:122-140; the chain's "off" check runs before the duplicate-protection key is written (emergency-alert-chain.ts)Today there are five separate address lookups (phone repair calls, prospect calls, texts, email and leads, staff tools). They split each address into words three different ways and load the company's homes five times. Only two of them read a unit's own street. Fede's #20946 fixed one more. A fix to one never reaches the others.
| # | Change | Proof it worked |
|---|---|---|
| B1 | Create the index and move phone repair calls onto it first, the path that missed this emergency; delete #20946's separate patch | Replay the 10-18 call and every address attempt since 10-09 |
| B2 | Build check against side lookups | A deliberate side lookup fails the build |
| B3 | Texts onto the index | A Lincoln test that fails first; Western Slope goes from 6 to about 37 of 63 units |
| B4 | Prospect calls onto the index (keep the unit street) | Lincoln goes from "several" to the right unit |
| B5 | Email onto the index (it already reads unit streets, so this is consolidation) | Western Slope and Situs email replays give identical results |
| B6 | Ranges, names in the address field, "street - door" format; confidence and next step; alias tiers and the map fallback | The five Situs buildings with a name or list in their address field resolve; a nearby-street alias resolves with "likely" |
| B7 | Duplicate-record rule (the building's unit wins; AppFolio-inactive records drop out); unit streets saved for every company through the adapter, which fills JPCO's (0 of 232 today). Best-guess auto-filing waits for this | JPCO shows unit streets; Ember units resolve to the building's unit |
| ING1-2 | propflow-ingest: alias step, plus onboarding conflict flags | A Western Slope draft lists Bunting for Lincoln, with learned aliases marked "suggested" |
tools/maintenance.ts:732 → handle-match-home.ts:114 → mid-call-home-match.ts:113 → maintenance/spoken-home-resolver.ts:255 (unit street fallback since #20946 at :289-311)postcall-prospect-capture.ts:658-677 drops the unit street at :675, although leasing/spoken-home-resolver.ts:72-77,482 supports itresolve-turn-home.ts:168 → home-match.ts:109 (one row per building only)home-address-key.ts:332,456; org-mailbox-inbound.ts:174-180; front-door-unit-home.tsorg-listings-sync.ts:665-684 via listing-unit-ids.ts:341 (10-second budget). The comment at rent-roll/utils.ts:442 claiming AppFolio has no unit street is wrongdata/dynamo/property-aliases.ts:26-45 (kinds street and nearbyStreet; sources pms, geo, learned, manual; statuses active, suggested, retired)propflow-ingest/ingest/address-aliases-step.ts, beside prefill-step.ts, run by runner.ts, output as a bridge draftThe rule (Gera): every email, text or Slack message we send the team, and every reply they send back, appears on the conversation it's about, with a link to it.
Why it's missing today: Western Slope and JPCO run on the newer team-notification system. It sends the email and keeps its record somewhere the conversation page never reads. Gera's 10-14 fix (#19577) covered only the older path. About 40 kinds of team message exist, and most of them, including several work order, move-out and collections emails, leave nothing on a conversation.
| # | Change | Proof it worked |
|---|---|---|
| C1 | Notices from the newer system go onto the source conversation with the link; the older duplicate path is removed. Small and independent, so it ships early. It does nothing for Situs, which isn't on the newer system; Situs notices are covered when the older path folds into the same door | The next Western Slope callback shows "Clara emailed the team" on the conversation |
| C2 | Backfill: re-file the 24 missing emails (Western Slope 21, JPCO 3) from our own records. Gera approved Dry run first; only adds, never deletes | Dry-run report, then the 10-18 call shows the email |
| C3 | Staff email replies saved on the source conversation; one row even when two copies arrive. One slice, one owner: this is the same change as the staff-reply investigation's first follow-up | A reply to a test notice shows once on its conversation |
| C4 | Staff texts and their replies on the source conversation | A test text shows up |
| C5 | "Posted in #channel" line for Slack | The 10-18 call shows the #western-slope post |
| C6 | Link check across every other team message (work order, move-out, collections and others) | Every team message has a link that opens the right thread |
src/lib/team-notify/live-deps.ts:48 (noConversationRecord); engine send engine.ts:205handle-capture-unknown-caller-note.ts:546,568 vs :755-779pm-internal-kinds.ts:63, ConversationThread.tsx:3974team-notify/ingest.ts:74-77; door staff-reply/door.ts:187, inbound-email-reply.ts:173email/client.ts:943| # | Change | Proof it worked |
|---|---|---|
| D1 | Tag the forward-to-manager email so its replies come back through the staff door | A reply to a forwarded test email matches its conversation |
| D2 | Clara's staff-answer turn, for replies to questions only, runs with the source conversation as context (after C3) | Replay of Jay's question: Clara describes the call and the MCC forward correctly |
| D3 | Prompt rule plus an outgoing check against "no record" claims without a lookup | The 10-18 "no leak report" draft is blocked |
| D4 | Questions Clara can't answer go to a person in Slack | A test question she can't answer lands in the company's channel |
| D5 | Stop the company's own addresses opening leads on Clara's inbox (left open by #20949; needs Fede's view) | A staff address can't create a prospect |
| Data | Remove the test record's claim on the leasing inbox address and close its stale item. Gera approved | The leasing inbox no longer resolves to a renter |
inbound-email-reply.ts:111-176), passed the own-outbound gate (process-inbound-message.ts:1062) and the "addressed to Clara" exception (:1419-1513), then the matcher tied the address to a placeholder from the 9-13 rent-roll backfillundeliverable-recipient.ts:234,261handle-forward-to-property-manager.ts:1700; pattern to copy page-property-manager.ts:726-800:1140-1160, :1505-1513, staff-notice-thread.ts, thread-context-bypass.ts:207clara-leasing.ts:924-927Each change is branched from main, kept to about 300 lines or fewer, and works on its own. At most three are open at once per builder. Already merged and live: #20946 (unit street for phone repair calls) and #20949 (staff mail no longer treated as customer mail).
The decision records: ADR-0145 merged in #21131, ADR-0146 in #21134 and ADR-0147 in #21135 are in review.
| Who | Decision | Status |
|---|---|---|
| Fede | ADR-0145: emergencies always escalate, dropping "no home, no page" | Agreed 10-19; merged as #21131 |
| Fede | Clara answering team replies: follows Fede's shared-inbox gating design (three layers, about 95% confidence, supervised first). ADR-0146 only records replies on the conversation | Agreed 10-19; Fede designing |
| Gera | Fallback when we can't place the caller: the last unit we know for them, flagged. Tenants treated as active get checked as really active | Decided 10-19 |
| Gera | Duplicate records (Ember Estates, DU portfolio): the building's unit wins, inactive records drop out, flagged at onboarding | Decided 10-19 |
| Gera | Any active leak is an emergency; only a contained or shut-off slow drip is routine. #20742 must be reconciled with this | Decided 10-19 |
| Gera | Unconfirmed-location alerts go to the same on-call chain as any emergency, marked "location not confirmed" with Clara's best guess | Decided 10-19 |
| Gera | Non-emergency call with an unconfirmed unit: file on the caller's known unit at normal priority with a "confirm location" note; no hold for staff | Decided 10-19 |
| Gera | The live end-to-end test rings a test on-call list (robot numbers, no personal phones), not the real techs | Decided 10-19 |
| Gera | The unit-address fix ships for all three customers now, including JPCO's unit-street sync | Decided 10-19 |
| Fede | D5: company addresses can't open leads on Clara's inbox | Open |
| Gera | Re-file the 24 team emails (after C1); remove the leasing inbox's fake owner | Approved 10-19; the fake owner was removed 10-19 |
| Gera | Learned aliases need confirmation first | Decided 10-19 |
| Gera | Correction to Jay | Sent 10-19 |