Situs incident, Oct 27: the first real leak
Situs incident, Oct 27
What happened: at a company set to Supervised for leasing and maintenance, an approved applicant with a housing voucher emailed her RFTA packet to leasing@. Clara could not read the PDF, asked the Situs team by email, and three minutes later a staff reply to that email was reworded and texted to the applicant. Clara's own reply to the applicant was held correctly. Nothing was approved by a person.
Timeline (Oct 27, Mountain time)
| Time | What | Gate | Reached a real person? |
| 11:18 | Situs staff text the applicant from AppFolio: "Please email it to leasing@thesitusgroup.com" | — | staff's own text |
| 11:24 | Applicant emails the RFTA packet (PDF, 1.4 MB). The PDF reader was unavailable in that runtime, so Clara saw only the file name. | — | inbound |
| 11:25 | Clara calls forward_to_property_manager: matter opened, decision-request email to leasing@thesitusgroup.com (bcc hello@) | sent by design: staff notices are never held (Fede, Oct 14 and 16; ADR-0143) | 🟡 yes, the Situs team |
| 11:25 | Clara's reply to the applicant | held (eschold_95e18598, card in #situs-group) | no ✅ |
| 11:28 | Lee (Situs) replies to the email: "I just responded to Nicole, thank you for following up." | — | inbound |
| 11:28 | Relay rewrites Lee's words as Clara and texts the applicant: "I've already reviewed the RFTA packet and responded to Nicole directly. Thanks for checking in on it!" (Twilio SM98792a2…, delivered, from the Situs Clara line) | no gate: sent under the staff_reply label, which skips the Supervised check | 🔴 yes, the applicant |
| 11:28 | "Got it — I've let Nicole M. know" emailed back to Lee | staff notice, exempt | 🟡 the Situs team |
| 11:33 | Gera writes "wheres the team notice link" in the Slack card thread; the bot treats it as draft feedback and rewrites the held draft to "I'll check with the team and send over the team notice link" | still held | no, but the held draft is now wrong |
Four defects, one incident
| # | Defect | Why it happened | Known before? | Fix shape |
| 1 | Staff answer relayed to the applicant under Supervised. The one real leak. | The relay (reply-release.ts, relayAnswerToPerson) sends with label staff_reply, which the text and email doors treat like "none": no module check, no Supervised check. The label was justified when "the words are the staff member's"; since Oct 16 the body is always model-rewritten. The team-note relay got a Supervised wrapper on Oct 16; the decision-request relay did not. | Yes. This page, decision 4 and fix 6, Oct 16. Not opened as a PR. | Bug fix, no switch: the relay to a person goes through the same reply hold as Clara's own reply. One PR. |
| 2 | Wrong message relayed at all. Lee said he had handled it himself; Clara told the applicant "I've already reviewed the RFTA packet", which nobody said. | The reply classifier only recognises "I called/texted/emailed them"; "I responded to Nicole" fell through to "decision". The rewrite prompt has no "this is not for the person" exit, and the fabrication guard only checks numbers. | Partly (decision 4: Clara's rewording). | Bug fix: a staff reply that says the person was handled closes the matter and sends nothing; the rewrite refuses when the answer is addressed to Clara. |
| 3 | Team-ask email reached the Situs team while Supervised. | By rule: "staff notes are never held" (Fede, Oct 14). The decision request is declared module "none", audience staff; only the company on/off switch applies, and Situs is on. | By design. | A ruling, not a bug. Fede, Oct 27: "I think I'm gonna fold in team notifications into supervised." See the yes/hold line below. |
| 3b | A human question in the Slack card thread rewrote the held draft. | Any thread reply is read as feedback on the draft. | No. | Follow-up: a thread reply that is a question to the bot, or from an engineer, is answered, not applied. Now: the held draft reads "I'll check with the team and send over the team notice link"; reject that card so nobody approves it. |
| 4 | Trigger: the PDF was never read. The inbound email says "NOT READ: the PDF reader is unavailable in this runtime", so Clara saw a file name and escalated. | The mailbox lane normally extracts PDF text (with OCR). Why the reader was unavailable in that run is unverified. | No. | Inbound-path bug, own PR, cause to be found first. |
Is this the first leak at Situs?
- First through the escalation path: this is the only matter ever opened at Situs.
- The Supervised hold itself has worked: 58 holds since Oct 19 (56 maintenance texts, 2 prospect emails), none approved, none sent.
- Nine other texts left the Situs Clara line Oct 19 to 21 with no hold row: six "work order created" confirmations (system sends), and three Clara-written texts: a rating request, a reply to a resident about a cleared boiler closet, and a Spanish tour confirmation to a prospect. Whether Supervised was already set on Oct 19 and 20 is unverified (the company row keeps no history for it, and the company on/off switch changed at 13:00 MT on Oct 20, after those texts). Handed to Gera's lane to confirm; not ruled either way here.
Where Supervised is consolidated today
Clara Refactor's askTheTeam seam now fronts all 23 "ask the team" callers, but it is a pass-through: it checks no mode. The decision-request email and the staff-answer relay both still send directly. Folding team notifications into Supervised means one check inside that seam (the matter's module stage) plus the relay fix above; no second gate.
Yes / hold for Fede
- Team-asks to the customer's staff are held under Supervised, like Clara's replies? Yes, Fede 2026-10-27: "we need to start holding staff replies"; the Situs case "is an example of we should have not replied because we can't help." Replaces the Oct 14 rule. Build: PR A (relay under the hold) and PR B ("handled" replies relay nothing) by session 007; PR C (team-asks through the team-notice hold door) by the Clara Refactor session. Still open: delete the separate Team-notices switch now that notices follow the module? Recommend yes.
- Fixes ship as bug fixes, no switch, Willows proof then live everywhere. In build, Oct 27.
- No recovery message to the applicant. Lee already answered her directly and the company is Supervised. Recommend leave it.
Not verified: whether the teaching step saved Lee's line as property knowledge; why the PDF reader was unavailable; Gera's side's intent for the "team notice link" (bridge question q-situs-teamask-2026-10-27, open).
Records: matter_41896f45, conversation dbe8f8e0, hold eschold_95e18598, Twilio SM98792a226973b9b26373c11a62415ce5, Slack #situs-group 1793121922.037549. Code read on main 7765053b4d.
Fix shipped, Oct 28
Live everywhere, no switch. Every staff reply is now read once into one of five outcomes: answer, handled, question, note or unclear. Only the facts in an answer reach the person, in a reply Clara writes, and that reply waits in the Supervised hold like her own. The reword-and-relay step is deleted. Correction: that reword step dates from Oct 4, not Oct 16.
| Staff reply (Willows, Oct 28) | Before | After | Matter |
| "I just responded to her" | waiting on staff | closed, nothing sent, Clara's held reply withdrawn | matter_e3763598 |
| A real answer (TV mount rules) | waiting on staff | Clara's reply from the facts, held for approval | matter_f2bf5d68 |
| "hmm" | waiting on staff | nothing sent, one line back to staff, still open | matter_aff66f1f |
| Leasing, two-reply: "hmm", then a real answer to Clara's one-line ask | waiting on staff | Failed. The ask to staff was held, then sent without the thread link, so the real answer reached no matter. No draft, nothing sent, still open. | matter_623b413d |
| Leasing, two-part question: staff answer part one ("fee waived, still checking the deposit"), then part two | waiting on staff | Failed. Draft 1 held from the answered part and says the rest is open, but the matter closed as resolved, so part two has nowhere to go. Stopped before reply 2. Nothing sent. (Replaces a first run where Clara had asked staff only one question.) | matter_1f13859d |
| Leasing, the same answer sent twice | waiting on staff | Passed. One draft from the facts, still pending after the duplicate, no second hold, nothing sent. | matter_0734d88b |
| Leasing, text arriving through the AppFolio inbox (Situs shape) | n/a | Not run. The text was mirrored in and woke Clara, but the only door test thread has hit the 40-reply safety ceiling, so she stopped. Nothing sent anywhere. Needs a fresh door test lead. | none |
Shipped as: a test that sends leak-shaped staff replies through the real release path; one reader for staff replies that fails closed; the release path rewired to it, with the old relay deleted.
On 208 real and planted staff replies the new reader is right 96% of the time, against 69% for the old phrase list, with zero false relays (the old one had 41). The leak test: 0 of 21 reach the person.
Live matrix in the incident's shape (Fairhaven, Oct 28)
12 of 12 live cells sent nothing to the person. The setup matched the incident: an applicant texts, Clara asks staff by email, staff reply by email, and the company is fully Supervised. Zero texts went to any test applicant, zero left the Fairhaven line all day, there were zero rows in the AppFolio text outbox, and nothing was written to knowledge. All 31 new holds are accounted for. One defect: a late answer on a matter staff had already closed reopened it, and Clara's held draft added "approved", which staff never said. Caveat: the test applicants came in on Clara's own line, so the AppFolio inbox door never applied to their threads; Fairhaven has no AppFolio account to reproduce that entry. Rerun that evening with more fixes live: see Final live rerun.
| Staff reply | Expected | What happened | |
| "I just responded to <name>" / "I called her." / the same from a second staffer | closed, nothing sent | closed as handled, Clara's draft withdrawn, ack "nothing was sent" | 3 of 3 |
| Facts: packet complete + decision Friday; $350 due Nov 3 | held draft with exactly those | held draft with exactly those, nothing added | 2 of 2 |
| Own words + signature + forwarded owner note | only own words used | stored answer stops at the forward; nothing from the note anywhere | pass |
| A question back; "see attached" + PDF; a policy line; "I told her cosigner it's fine" | nothing to the person | all read as "unclear": matter stays open, a one-line ask to staff held. The third-party rule was never reached | 4 of 4 no leak |
| Out-of-office | nothing sent, stays open | auto-reply detected, matter open, no knowledge | pass |
| Late answer after the matter closed | nothing sent, no new matter | nothing sent, no new matter, but the matter reopened and the held draft added "approved" | defect |
| Reply in the Slack thread under a held card | through the reader? | not run; in code it goes to Smith, never to the reader | gap |
Every setting was restored and every hold rejected afterwards. Applicants used Twilio test numbers with made-up names. Full table with ids in the fix's description.
Final live rerun, Oct 28 evening
No test reached a person: 0 sends across 20 live cells, with every fix in production. 15 behaved as expected, 2 have a safe defect, 2 sent nothing but behaved differently than planned, and 1 is still a gap. All four are fixed and in production; three are proven live, one fails safe and awaits a test reviewer identity (Oct 29).
Production ran commit 87f7630e0 with ten fixes merged and live since 21:15Z: the fully-Supervised hold (#22881), the one staff-reply reader with the old relay deleted (#22979), and #23191, #23188, #23194, #23198, #23199, #23192, #23214, #23211. The only later commit (99bd1713) touches evals and CI only. Test companies: Fairhaven (fully Supervised) and Willows (Leasing set to Supervised). Applicants used Twilio test numbers with made-up names; staff replied from a corporate test mailbox.
Morning → now
| Cell | Morning | Now |
| Late reply after staff closed the matter | ✗ matter reopened, draft added "approved" | ✓ stays closed, no draft |
| Staff say "hmm", then answer Clara's one-line ask | ✗ answer reached no matter | ✓ ask kept the thread, held draft |
| Two-part answer, sent in two replies | ✗ closed after part one | ✓ stays open, one combined draft |
| Same answer sent twice | ✓ one draft | ✓ one draft |
| Staff correct an earlier answer | not run | ✗ amount right, due date dropped |
| Text arriving through the AppFolio inbox | partial: Clara's line only | ✓ real AppFolio-inbox text, approved live, went to the AppFolio door, 0 Twilio |
| Staff ask a question back | held one-line ask | answered staff directly; nothing to the applicant |
| Free-text reply under a held text card in Slack | gap, code read only | still a gap, now run live |
Every cell
Draft ids are eschold_ + the 8 characters shown.
Controls
| Check | Result |
| Texts to the 17 test applicant numbers, since 22:15Z | 0 |
| Texts from the Fairhaven / Willows Clara lines | 0 / 0 |
| AppFolio text outbox, Fairhaven | 0 rows |
| AppFolio text outbox, Willows: matrix window | 0 new rows |
| AppFolio text outbox, Willows: door approval | +2 rows (the held reply and an alert row); 0 sent |
| Emails | 22, all to the corporate test mailbox; 0 to anyone else |
| Knowledge, full-content diff | 0 changed |
| Holds created / left pending | 51 (Fairhaven 33, Willows 18) / 0 |
| Settings at both companies | match the before snapshots; 62 writes ledgered |
What this shows: under these fixes, no staff reply in these 20 shapes reached a person. It does not show a clean AppFolio delivery: that needs a test number on exactly one AppFolio card.
Defects and open fixes
- Correction drops what it didn't change (W-C): #23316, fix #23330 open. Related: Clara learns the correction words alone (#23328).
- Half-answer draft adds an unstated status (P1): the check that every fact in Clara's draft traces to staff's words skips status claims like "hasn't come back yet". #23319, fix in build. Related: #23281, #23282.
- Staff question answered straight to staff (c4): nothing reached the applicant, but staff's words are stored in the applicant's thread. #23318, fix in build.
- Slack text card (case 13): #23317, fix #23333 open.
- Smaller: "renewal" hold label on a leasing draft (#23320); the "already closed" note to staff is held, not sent (#23321); the stored answer keeps the signature (#23322); the AppFolio text joins an old conversation and the ask to staff names the person on file (#23323).
Follow-up fixes, Oct 29
All four fixes are merged and in production (last deploy Oct 29, 11:08Z). Three are proven live; the fourth fails safe and waits for a test reviewer. 0 texts or emails to any person across 20 cells on Oct 28 and 6 follow-up cells on Oct 29.
| What was wrong | Fix | Live result, Oct 29 |
| A correction dropped the facts it didn't change (W-C) | #23330: the correction merges into the earlier answer; newer fact wins, the rest stays | ✓ held draft "$400, due this Friday"; one pending hold (matter_01c4864d / 4af57228) |
| A half-answer draft added a status staff never gave (P1) | #23347: the facts check now covers status claims | ✓ draft says "no update yet, I'll check with the team" instead; matter stays open (de3361a6) |
| Staff's question ran on the applicant's thread (c4) | #23348: a staff question never runs on the applicant's thread | ✓ applicant thread untouched; the on-file answer to staff held on the team-ask email thread (4e956067) |
| Free text under a held text card in Slack never reached the reader (case 13) | #23333: it now goes through the reader | Not exercised; fails safe. The test Slack user isn't a workspace member, so the reviewer check could not match it; the card said "something went wrong reading this reply" and nothing was done (252be5fc, 959f9547, fa418c2f rejected). Not a defect and not a missing Slack permission: a cofounder's typed approvals resolve in production (Oct 21). |
Controls: 0 texts to the test applicants, 0 from either Clara line, 0 new AppFolio outbox rows at either company, 0 emails outside the test mailbox, 0 knowledge changes.
Still open
- Your call: a test Slack member tied to a Willows admin login, so case 13 can be proven live.
- Parked, undecided: re-freezing the Day 0 replay baseline; deleting the separate team-notices switch.
- Follow-up issues: the four smaller items above (#23320–#23323); teaching saves only the correction words (#23328); a wrong status for a part staff did name still passes (#23346); staff questions needing a lookup get only the on-file summary (#23350); three cleanups from #23348's review (#23367, #23368, #23369); facts-check edges (#23281, #23282). #23316–#23319 closed by these fixes.
Every real staff reply, replayed (Oct 28)
69 real staff replies in production history (Aug 14 to Oct 28): 55 from customer staff (Western Slope 53, Situs 2) and 14 from PropFlow's own team on real-company matters. JP&Co staff have never replied to Clara. 15 of the 69 went through the code the fix changed. On those 15, old code wrong: 3, new code wrong: 4, regressions: 0. All 4 new misses are "unclear" on replies from our own team: nothing goes to the person either way, and staff get one "I couldn't tell" line instead of an answer to their question.
| Where the 69 replies came in | Replies | Runs the new reader? | What changed |
| Email reply to Clara's question (customer staff) | 9 | yes | old wrong 3, new wrong 0 |
| Email reply to Clara's question (PropFlow team, at Camellia) | 6 | yes | old wrong 0, new wrong 4 (all "unclear") |
| Closing notes PropFlow typed onto a matter | 4 | yes, but never read by the old code | shown for information |
| Texts to the staff desk (tour check-ins) | 38 | no | nothing |
| Replies to callback, alert and follow-up emails | 8 | no | nothing |
| Staff texting the person in AppFolio | 3 | no | nothing |
| Slack thread under a held draft | 1 | no | nothing |
Also in the corpus and left out: 128 replies at the two test companies, 6 desk asks staff never answered, 1 out-of-office. Two of the PropFlow-team emails are the same words sent twice, two minutes apart.
(a) Old code got it wrong, new code gets it right
PropFlow closing notes, for information (the old code never read these): "Closed administratively… no relay to the resident" → handled; "PM judgment… prospect poking the bot" → note; "not interested… no outbound" → note (matter_73b3db5d, matter_a225d287, matter_da2bfab9). A fourth, the voucher decision on matter_8fde2f40, reads as an answer with two clean fact rows and no staff name.
(b) Both right
Of the old code's 7 relays, 2 told the person something staff never said ("I've already reviewed the RFTA packet"; a fresh "confirmed"). The other add-ons were Clara's own tour offers, which the new draft step can add too.
(c) Regressions: new code changes a case the old code got right
None. No reply that the old code handled correctly is now sent to the person wrongly or dropped.
(d) New code is unsure where the old code was right
The cost: on 4 of 15 replies (3 different texts) staff would get "I couldn't tell what to do with your last reply" instead of an answer to their question, or the prospect would wait for a second reply. Nothing would reach anyone wrongly.
Paths the fix did not touch (50 replies)
None of these ran the old phrase list or the new reader, so old and new behave the same: 47 sent nothing to the person, and in 3 the staff member texted the person directly. Run through the reader anyway, it reads 46 of 50 correctly. The 4 misses, which would matter only if these paths were wired:
Gap: on a staff text mirrored from AppFolio, the reader has no idea that staff already texted the person. Today that only settles the matter, so nothing leaks.
The nine Situs texts (Oct 19 to 21) and the Oct 27 message
None of the nine is a staff reply. All are Clara's own texts from the Situs line, so this fix does not touch them:
| Texts | What | Now |
| SM5abe8e85, SMf825831c, SMa91d5b6b, SM57941f86, SMef32062e | "Work order … created" | Held for approval since Oct 28 (one seen, eschold_mr_da2edba4, approved); Fede asked to stop these texts |
| SM26a71ff2 | Clara's reply about the boiler closet | Not checked in this replay (a Clara reply; the company-wide Supervised rule is meant to hold it) |
| SMdd2e6ced | Rating request after a closed ticket | Not checked in this replay |
| SMdb8a9e08 (copy SM9dd12f9f) | Spanish tour confirmation | Not checked in this replay |
| SM98792a22 (Oct 27) | The staff-words relay | Replayed above: handled, nothing sent |
How this was run
- Read-only against production: database writes and every outbound host blocked. A planted check (one database write, one Twilio send, one SendGrid send, one Graph send) was blocked 4 of 4 and failed the run. The real runs ended with 0 blocked attempts.
- New: the shipped reader (Sonnet 5.5), with the same inputs production builds, on the subscription. Old: the frozen phrase list (reconstructed, since production never logged its label) plus what the records show was sent.
- Grader: Opus 5.5 on the subscription, blind to which side was which. Control: 10 labels flipped, 10 caught. Every flip pointed toward sending, the easy kind to catch.
- Not replayed: Clara's final drafted wording (the draft step writes to the thread). Fact rows were checked against the staff words instead; 1 unfaithful fact, on an unwired path, shown above. Holds use today's Supervised settings, not the settings on the day.
Maintenance: every path25 paths · 6 🔴
Maintenance
Every message to a resident is held, emails included. The work order itself is not: Clara's AppFolio writes run in Supervised. That follows Gera's 10-01 ruling ("only the message waits"); Fede's 10-16 ruling on the assign makes it a leak.
File references, maintenance
Writer src/lib/domain/pms/registry.ts:328-358, work-order-writer.ts:169-424; tools handle-create-work-order.ts:1032, handle-add-note-to-work-order.ts:267, handle-edit-work-order.ts:74, handle-vendor-completion.ts:541, voice-postcall-intake.ts:874-898 · assign tech-sms-dispatch.ts:880, :1308-1316, maintenance.ts:233 → :778 · vendor reply process-envelope.ts:485, :816, :926-998 · publish auto-publish-listing-hook.ts:170 · auto-release release-held-turnover-work-orders.ts:66, gather.ts:202 · NTV draft email-ntv-move-out-draft-hook.ts:126/160 vs sibling inspection-complete-move-out-draft-hook.ts:268 · carve-out approval-carve-out.ts:18; high-priority release conversation-manager.ts:16212-16225 · vendor emails send-vendor-dispatch.ts:441-453, send-vendor-quote.ts:211-225, vendor-chase.ts:447, redirect vendor-poc-recipients.ts:15, pass-through email/client.ts:1806 · vendor texts tech-sms-dispatch.ts:1349/1378/1397/1437, review rerun-text-review.ts:94 · turnover email send-turnover-vendor-dispatch.ts:612, :689, :907 · vendor call dial-approved-session.ts:207, initiate-vendor-call.ts:379-395 · replies process-inbound-email.ts:2021-2066, process-pms-email.ts:281/420 · PM page page-property-manager.ts:482, :731