This page shows how Clara works in production today, what we will rebuild and in what order. It then lays out the new-hire lifecycle that rebuild serves, the calls waiting on Fede, and the numbers behind it all.
Proposed, pending review2026-10-25 · design only: no code, no PRs, nothing sent · ledgers, bench results and reviews live as working notes on the tracker page
Decided, Nov 4 2026 (Fede, evening, session 005): "if a teammate is saying take this action take it" · "regardless of the human hold" · "its not clara autonomous. its a command from a teammate" · "we cant do a quick fix" · 18:06: "regardless of a human is in the thread. If it's supervised that still applies." Owner: 005. All five open sessions confirmed nobody else holds this area (Nov 4, 18:40 MT).
What happened. Western Slope prospect Heather Wolff toured Oct 24 and applied. Nov 4, 10:31 AM she texted the office for a second showing so her co-applicant could see the house. Bev answered by hand and emailed Fede to schedule it. Fede texted Clara's Western Slope line: "Text Heather Wolff at Morningside: Kat can show the house any day next week between 2 and 4 PM, which day works best?" The staff desk found her and called the text tool. The outbound door refused it: the thread carries the human-replied hold, stamped Oct 27 when Kat answered Heather by hand from the AppFolio texting inbox (#20993). Nothing ever clears that hold. Clara told Fede "the message was blocked from going out to her." Heather is still waiting.
The signal names who asked. Every staff-desk write carries an origin on the message envelope: teammateCommand: { personId, channel, at }. Not a boolean. One field settles four things at once: the hold bypass at the outbound door; the "operator send with no trigger source" critical log (that is the missing source); the follow-through engine's "a human touched this thread" mark (001's constraint, no PMS literal); and the feed card label ("on Kat's ask", not a stale thread name).
Holds are read, never cleared. The autonomous path keeps autoReplyHold as its signal (cadence stop, follow-through brief, reply-clock silence). The command passes the hold on its own door and leaves the stamp in place.
What opens the path. The model's tool call on a verified staff turn: roles looked up at the company, any staff tier, as the desk does today. Never a phrase or punctuation (Nov 2 ruling). It is the same reasoned "did staff ask Clara to act" decision as the email-thread re-evaluation above, and its cases go in the staff-reaction harness, not a new one.
Shared with the autonomous path, not re-done: tour commands call the tour domain functions and the host rule (002's lane); staff recognition is roles; the sender rule (staff hear from Clara at Propflow, outside people from the customer's mailbox) is untouched.
| Reader of the hold today | Kind | Under the command |
|---|---|---|
| Outbound policy at the chokepoint (text, AppFolio texting door) | send | passes, logged "delivered on a held thread: teammate command" |
| PMS email lane refusal, bake gate "teammate took the thread" (#16608) | send | passes: both mean a person is in the thread |
| Supervised hold for approval, Off, Preview (Gera's four modes) | mode | unchanged. Supervised holds the command for approval like any message. Today's text tool skips that hold on a staff ask (the "staff-asked approval id"); under this ruling that skip comes out, Gera's chain |
| STOP / consent on file; a test property's fence; a conversation a teammate called "a test" (Gera, Nov 2: draft-only on every line) | law / fence | unchanged, refused |
| Held-text set, AppFolio text-door drain re-check | send | the stored copy carries the origin, so a retry passes too |
| Cadence yield, follow-through brief, reply-clock silence, inbound escalated gate | signal | unchanged; they keep reading the stamp |
Chain, cut by proof (fix to live behavior, no switch). PR 1: the origin on the envelope, honored at the chokepoint; the two text tools and the application text leg carry it (the closed #25284 recut). PR 2: every other desk write carries it; the email leg and the bake gate honor it. PR 3: the AppFolio text-door drain honors the stored origin; the feed card and the trigger-source log read it. The command passes the hold only when the teammate works for the company that owns both the line and the person being texted. Each PR ships the same control: the command goes out on a held thread; Clara's own reply on that thread stays held.
Proof. Harness rows → a staff text to the Willows line commanding a text on a held test-prospect thread → the real re-send of Fede's Heather Wolff ask at Western Slope, end to end, with the message id. Text only; no voice minutes.
Out of this chain, named: Bev's original email to Fede is the case the ruling implies should also work by email. Staff email to Clara is not routed to a company today; the one-pipeline design (Nov 3) is the dependency.
Decided Nov 3 2026: option A, under Fede's "retire this helper" (the standing grant; A keeps what renters get today, an immediate cancel). Fede may override. Fede: "lets look at the history and put this in a test harness - test what happens if we remove it", then "retire this helper".
Four small PRs, no switch. After the first two, the replay shows tour changes better (worse 7 → 4), no tour booked that nobody picked (2 → 0), and every "I need to cancel" cancelled (84 of 84).
| PR | What | State |
|---|---|---|
| #24971 | Clara's tour tools carry the helper's rules: cancel text to email leads, the team-offered / phone / name checks on a yes, the offered day on a bare time | merged Nov 3 |
| #24979 | Prompt: cancel at once, offer to rebook in the same reply; "reschedule" with no time takes the tour off; a yes that names no time is not a pick | merged Nov 3 |
| #24997 | Two gaps the review found (checks skipped on a video switch; the name ask pointed at a field the tool lacks), plus tool-level tests | merged Nov 3 |
| #25005 | Remove the helper from the router; the open-homes list loads on every tour turn instead; Fairhaven live run | merged Nov 3 23:12Z; live run passed (below) |
| #25029 | Fix found by the live run: the offered-day pin moved two Thursday bookings to Friday (it took the day from a Friday calendar read, not from Clara's sentence) | merged Nov 4 00:38Z; live re-check passed; no customer tour was affected |
| Cases | Before the chain: same / better / worse | After: same / better / worse |
|---|---|---|
| Helper changed a tour (29) | 16 / 6 / 7 | 14 / 11 / 4 |
| Helper only added advice (69) | 43 / 12 / 13 | 42 / 11 / 15 (reply wording) |
| Clara booked a time nobody picked, or an extra tour | 2 | 0 |
| The 5 JP&Co "I need to cancel" cases + 2 more, 12 runs each | tour left on | cancelled, 84 of 84 |
| "I added it to my calendar" after two offered times (Amy) | booked 10 AM, 8 of 12 | asked which time, 12 of 12 |
The 4 still worse: one already worse before the chain; one asks for a phone under today's phone-first rule (the judge graded by the old date); one asks for a phone because the rebuilt prompt borrowed another lead's "no phone on file" line (books 3 of 6 runs). Reply-only gaps go to #24945. Planted mistakes: 4 of 4 caught on every run.
| Step | Result |
|---|---|
| Book three homes | 3 tours, each carries its home (before: tours booked with no home) |
| "Cancel two, keep one" | exactly two cancelled; no rebook offer (#25030) |
| "Sounds good" after two offered times | asked which time, booked nothing; booked once a time was named |
| "Friday at 3 instead?" | same tour moved, no duplicate |
| "I need to cancel" | cancelled at once, rebook offered |
The old helper never ran (0 log lines). One defect: two Thursday tours first written on Friday, then corrected by Clara; fixed in #25029. Record ids are on #25005.
What the helper is: a step that runs before Clara reads a renter's reply. It confirms, cancels or moves a tour on its own, then tells Clara what it did. On Nov 3 it cancelled the one tour of three the renter wanted to keep. The goal is that Clara alone acts on tours, through her tour tools.
Every real renter message the helper acted on, at every customer, replayed twice on Opus 5.5: once as production ran it (helper on), once with the helper removed (helper off). A judge compared the two calendars and replies. Four planted mistakes were all caught. Nothing was written to production.
| Cases | Same | Better without it | Worse without it |
|---|---|---|---|
| Helper changed a tour (30) | 16 | 6 | 8 |
| Helper only added advice (68) | 43 | 12 (2 change the calendar) | 13 (3 change the calendar) |
| Renter said | Helper on | Helper off |
|---|---|---|
| Cancel two of my three tours, keep one (the Nov 3 case) | Cancelled the one she kept | Right: cancelled the two, moved the kept one to her new time |
| Picked a time; or answered on a stacked day | Booked a time she never picked, or added an extra tour (2 cases) | Right: asked which one |
| "I need to cancel" (one tour; 5 cases, JP&Co) | Cancelled | Wrong: offered a new time, tour left on |
| "Reschedule" with no new time | Took the tour off and asked for a time | Wrong: kept it |
| Picked a time, no phone on file | Asked for the phone | Wrong: booked anyway (1 case) |
Why the cancels go wrong: Clara's prompt says to offer a reschedule before cancelling, unless the renter insists. The helper never followed that line; it cancelled at once. Also lost by removing the helper: the text that goes to an email lead when a tour is cancelled (Fede, Oct 22). Clara's cancel tool sends no text.
| PR | What |
|---|---|
| 1 | Carry over what only the helper does: the available-homes list, the offered-times stamp, the cancel text, the phone check when moving a proposed tour |
| 2 | Prompt line per the decision, proven on this replay (every protected case at least as good) |
| 3 | Remove the helper and its tool stripping; live rung at Fairhaven |
Replay code and ids-only results: evals/replay-harness/tour-journey/helper-off/ and results/before-2026-11-03-tour-helper-off.md on branch fede/amy-reid-tour-harness (pushed, no PR). Listed on the harness atlas, Tour journey row.
Decided, Nov 2 2026: Option A. Fede: "yes, this should always be our default." Building as a fix, no switch; the stay-quiet PR #24533 is closed after the inspection below; A ships as its own small chain.
Fede's premise: "we should be reasoning on whether they asked us a question or not, not having hardcoded characters." Every punctuation, name-position or regex rule on this path is debt and goes.
Threading, as live today (006): a staff reply to any notice Clara sent lands on the conversation the notice came from, from any company staff member, reply-all included; from fix three, the same for every one-address staff email she sends. The one reader then sorts it: an answer is relayed to the renter (held under Supervised), "handled" closes the notice silently, a question or note runs her coworker turn there with Cc kept, unclear is held. None of the three options touches that; they only decide whether the question-or-note branch may speak.
| Today's Situs cases | What happened | Should be |
|---|---|---|
| Eileen: "Hi Deiby, How did this go?" | Clara answered in a fresh thread | Quiet. A question to a colleague. |
| "Team, This is a good example of someone messing with the ai agent…" | Clara replied "Noted…" | Quiet. A remark about her, not to her. |
| "Yeah this is PropFlow testing it themselves… Sent via Superhuman <link>" | Clara answered; the "?" in the tracking link read as a question | Quiet. Staff talking to each other. |
| "Hi Clara, No, that is not possible." (Oct 31) | Handled | Still handled: an answer to her. |
| "Thanks! Can you resend it?" | Answered | Still answered: a request to her. |
Options for the one question, "did the team ask Clara something?"
What each option changes in that branch: A, the reader's question/note verdict gains one more judgment, "is it to Clara?", before the coworker turn; B, the same judgment made from the To line and her name, so a group question with a real ask ("what did this caller want?") goes unanswered; C, the branch is removed and a note or question is only recorded, so the Fairhaven proof case gets no reply unless staff mail her alone.
Inspection (007, read-only, production logs, Nov 2 afternoon): the regex was not even the cause. Main's model reader already labelled the "Team," note and the 18:26Z note as a note; since this morning's change every note starts a reply turn, and Clara's staff-email reply prompts have no way to stay silent (only the video prompt does). Eileen's email took a second door with no "should Clara answer" step at all. The stay-quiet PR added word lists at both doors and would still have answered the 18:26Z note, so it is closed, not merged. New chain (fixes, no switch): 1. a note, or a message for someone else, never starts a reply turn, and the email prompts get a silence outcome. 2. The reader sees To/Cc with roles, Clara's own message and the recent turns, gets a "for someone else" outcome, and is the one decision at both doors. 3. Threads are picked by what the reply answers, not by the sender, with no building required.
Proof before any merge: the overnight replay set (14 real Situs threads) plus today's three misfires and the two controls, the judgment run three times to show it is stable, no sends. Also running: Fede asked for a deep re-inspection of this architecture ("I don't trust it"); 007 owns it, with read access to the replay cases.
Two independent studies of Situs's year of email and all guest-card messages (Fable, Opus), a harness of real VA-to-team exchanges, local POCs, and the landscape. Nothing below is on; each row is one yes/hold.
| # | What the team does today | How often | Proposal | Size | Recommend |
|---|---|---|---|---|---|
| 1 | Renter names a time nobody has open; the VA emails the team "anyone available at X? I'll coordinate" and waits (median 3.6 h for a decision reply) | 31 of 91 time-naming texts; both studies' top email pattern | Clara asks the hosts by email with a deadline and one nudge, books the first yes, tells the renter; falls back to the two nearest slots at the deadline. Harness: 0 of 10 real cases today, 9 of 10 on the branch; cover-for-me 1 of 3 → 3 of 3; hosts asked on their own channel. | Feature, 2–3 PRs on the tour move/reassign design | yes |
| 2 | Staff move a tour; the prospect is told "confirmed", never asked. Reassigning a tour to another agent has no command | 6 of 77 tours moved in 60 days; 39 showings handed between agents this year | Propose-and-confirm + reassign; POC 23 of 23 cases on the branch (2 of 23 today), 0 sends | 4 PRs (state, ask+answers, timers, reassign) | yes; plus 2 sub-asks: email reminder for email-only prospects (yes), still ask when the renter asked to move but the agent picked the time (yes) |
| 3 | Clara and the VA desk answer the same prospect texts; the VA wins the race | staff first 36 of 47, Clara 1, nobody 10 | Decide who goes first at Situs; Clara claims the card visibly after her message | Decision + 1 PR | Clara first on new leads, staff keep threads they already own Fede, Nov 2: expected, we just launched. No action. |
| 4 | Team email copied to leasing@ never reaches Clara or the renter's thread | 72 skipped in 8 days; 234 this year | Log teammates' replies on the thread (1 PR); the group-thread rule (speak / act / stay quiet) scores 6 of 14 real threads today, 14 of 14 as a decision rule, not wired yet (3 PRs) | 1 PR + design | yes to the log; design for the rest |
| 5 | Clara's notes go to leasing@ with no named owner; the VA names a person | VA 95 of 105; Clara 0 of 20 | Address the host or the card owner by name, shared inbox in copy | 1 PR | yes |
| 6 | "I'm here" texts from the curb | 68 of 884 cards | Clara stops saying "they'll be ready for you"; she names the host she told, repeats the property's meeting spot, and after an unconfirmed showing warns the agent may not be there and offers another time. Harness: 0 of 3 real cases today, 3 of 3 on the branch; planted defect red. | 1 PR (~100 lines), fix, no switch | yes Fede, Nov 2: the host notification already exists (tour-day update email + host text). This row is only the renter-facing wording; fold it into row 7, not a PR of its own. |
| 7 | Situs skips unconfirmed showings (they travel); confirm one hour out | 66 staff cancels, 53 prospect cancels | The one-hour reminder asks the renter to reply to confirm (Fede's wording). Silent by 30 min out: the host gets one email that they haven't confirmed; the tour stays booked, nothing is cancelled or written to AppFolio. A confirmation sends the host nothing (today's "confirmed" note folds in); a late yes sends "confirmed after all". Row 6's "I'm here" words ride in the same PR. Harness: 9 of 23 today, 22 of 23 on the branch, 11 planted defects caught, 0 sends. Company setting tour.askToConfirm, default off. Design + POC | 3 PRs (~600 lines): new capability + fixes | yes to build; turn on at Situs after a Willows run, with AppFolio's own confirmation text off for Clara's showings; hold auto-cancel Fede, Nov 2: no release or cancel at launch; email the host 30 min out instead. |
| 8 | Staff email Clara delegations, loop-closers, status pings | 27 / 14 / 8 in the mailbox | Shipped tonight: verified staff email gets the desk (#24426), "Got it" back (#24428), after-tour ask by email (#24435). Owed: Willows end-to-end proof Monday | done | prove Monday |
| 9 | From the landscape: remember a correction; reply-all etiquette; never resend a follow-up a person already sent | n/a | Fold ideas 1, 2 and 6 into the planned coworker work | in existing items | yes |
| 10 | Back-office work (resident notices 16%, applications-to-move-in 15%) has no Clara tool | ~31% of the VA's day | Design first; new capability, default off | design | hold until 1–7 are live |
| 11 | Staff and the VA seats book showings by hand in AppFolio; Clara never hears about them, and misses AppFolio-side cancels and confirmations of the showings she booked | 507 showings Jan–Sep (383 staff, 124 VA seat) | A 10-minute read of AppFolio’s showing calendar makes each one a Clara tour: intro, host note, reminders, after-tour asks, moves and cancels both ways. POC: main 3 of 19, branch 19 of 19, 8 of 8 planted bugs, 0 sends. Design | 4 PRs: a fix for her own showings (no switch) + the import (new capability, default off) | yes; turn on at Situs with AppFolio’s renter showing messages off the same day |
Curb-arrival and confirm-or-cancel POC: branch fede/poc-va-arrival-confirm (local only), 0 sends in every run; Clara's actual replies were not run, only what she is handed; today's "confirmed" note to the host is text-only, so Situs hosts (no phones on file) never get it; nothing was sent to anyone. Design and results: tour confirm-or-cancel.
Proposed — pending review · read-only: nothing sent, nothing changed · built from production records, code on main, and the PR and session history (sources at the end)
Deiby's reply named the tour in its subject line. The reply step is built to match such a reply to its check-in by the email's headers, and for check-ins it drops that match on purpose. Four more links then broke in a row. Had the match been kept and used, both replies could have gone to Deiby's own thread at the tour's building, where sending was on, with Clara's question in view.
BEFORE Nov 3, 4:53 PM MT. Deiby answers Clara's email "Stephany M.'s 3:00 PM tour at Lampliter":
"She actually ended up not showing up. Can you check in with her to reschedule please?"
1 Reply step built to match the reply to Clara's check-in by its headers;
for a check-in it tells the next step "nothing matched" MATCH DROPPED
2 Clara's turn gets Deiby's new lines only; subject and quoted question cut "SHE" HAS NO NAME
3 Filing the company's first building by id, the no-building fallback:
Situs CFC One, a placeholder marked "not in PropFlow"
──► Deiby's Lampliter thread skipped ──► a NEW conversation WRONG PLACE
4 Clara lists Deiby's open check-ins: Stephany, Patricia
──► "Which one didn't show: Stephany … or Patricia …?" ASKS ABOUT HER OWN QUESTION
5 Send gate team notices are off at Situs CFC One ──► blocked;
the inbound-email log says only "suppressed" DEIBY GETS NOTHING
AFTER with the fixes below
Reply step keeps the match ──► Deiby's Lampliter thread, Clara's question in view
Clara's turn sees the subject and the quoted question
Clara records Deiby's note on that tour and follows up with that prospect
Send gate Lampliter, sending on ──► the reply reaches Deiby
any block is logged with its reason
| # | What broke | What it did on Nov 3 | Introduced |
|---|---|---|---|
| 1 | The match was thrown away, on purpose | The reply step matches the reply to Clara's check-in by its headers, then reports "nothing matched" so tour notes never reach the prospect. A unit test asserts the drop. | Nov 2 (#24435) |
| 2 | Clara never saw the subject or her own question | She read only Deiby's new lines ("She actually ended up not showing up…"). Her lookup listed both open check-ins with prospect and building; nothing said which one Deiby answered. | Nov 2 (#24426); subject a row label only since Aug 3 (#5333) |
| 3 | A fallback filed it under a switched-off placeholder | Filed under the company's first building by id, the no-building fallback: Situs CFC One, a placeholder marked "not in PropFlow" on Oct 6. On email Clara keeps only threads at that building, so the threads holding her questions were skipped. Every module is off there, so even "which one?" was blocked. At the tour's building both would have sent. | Nov 2 (#24426); line-based thread rule text-only (#20110) |
| 4 | The Nov 2 fix for this class missed this sender | The Nov 2 fixes (Clara remembers her own notices, the reply tag on staff emails) land a reply on its notice's thread. The check-in uses a different sender, which gets no tag and no notice row. | Nov 2 (#24489, #24494, #24534) |
| 5 | No real staff reply was ever run through it | The two PRs that built this path were unit-only; their live proof was "owed". The replays graded "replied or stayed quiet", with sends switched off. The first end-to-end run was Deiby's. | Nov 2 (#24426, #24435, #24543, #24565) |
"why does this keep happening on Clara as a coworker" … "do we not have previous context on what was sent? embarrassing"
Fede, Oct 16, on the same symptom by text: Kat wrote "She never showed", and Clara answered three times "I couldn't find that tour — who's this for?"
| Pattern | Evidence | What it cost |
|---|---|---|
| 1. We fix the door a bug came through, not the mechanism | "Ask and reply on two threads" has hit four paths, each fixed alone: text check-ins (#14100, Sep 30), staff texts (#20110, Oct 15: "SMS only, on purpose"), plain staff emails (#24534, Nov 2), now the email check-in. | It reached a customer the day after the last fix. |
| 2. Each hand-off drops what the last step knew | The reply step drops the matched check-in; the turn drops the subject and the quote. Since Oct 17 Clara's lookup lists her open asks with ids (#20603), never which one a reply answers. | Clara asks "which one?" about her own question. |
| 3. A missing building changes behavior | "First building by id" was added twice on Nov 2 (#24426, #24494), removed from one door on Nov 3 (#24746), and left on this one as "a separate change". Fede, Nov 2: "this should not matter if there's a property or not in the logic". | Clara's only message was blocked without a word. |
| 4. "Tested" stops short of the real path | Tests pin the wrong behavior: the drop, and the fallback with the real building finder stubbed out. A replay graded Clara's "I'm missing who 'him' is" to Hugo a pass. The check-in's live proof sat "owed for Monday", blocked by the no-sends rule, while a sibling fix was proven at Fairhaven that day. | The first end-to-end run was a Situs agent's real reply. |
| 5. Simplifications and known gaps are never revisited | Oct 4 simplification: "one open texted question per leasing agent and no reply matching". Oct 7, a review: "a staff reply can land on the wrong prospect when two asks are open" (#17333); deferred, owner unknown. The same fallback was filed twice on sibling doors (#24496; #24835, ten hours before Deiby's first reply); both open. | Deiby had two open asks by email, the case flagged a month earlier; nobody revisited the premise (inference). |
| 6. Speed on one seam, no sweep of siblings | The email check-in was one PR after five weeks and 22 PRs on the text one, and skipped their thread and which-prospect fixes. The staff email turn changed 5 times and team notices 15+ in two days. Staff-reply work ran in six sessions, each on its own door (inference). | Each fix closes its own door; the next sender reopens the hole. |
All are fixes to live behavior: no switch, live everywhere on merge (Sep 30 rule). Each is proven by a real staff email reply at Fairhaven, from a staff test mailbox in the Microsoft tenant, with record ids (testing playbook; Willows is closed since Nov 2). Unit tests are build hygiene, not proof.
| # | Change | Owner | Size | Proof |
|---|---|---|---|---|
| 1 | Keep the match. A reply to Clara's check-in runs on that check-in's thread and building, told which tour and prospect. Tour notes still never reach the prospect. Covers check-ins already open. | Clara Refactor (001) | 1 PR, urgent | Replay the three Nov 3 replies: right thread, right tour, send allowed. Fairhaven: a staff test mailbox with two open check-ins answers one "no-show"; Clara notes that tour, follows up with that test prospect, and her reply reaches the mailbox. |
| 2 | No placeholder fallback. A staff email takes the building of the notice it answers; with none, it runs at the company (Nov 2 rule) and Clara works out the building from what she reads. Never a not-in-PropFlow building. | Scope resolver | 1 PR | Replay the Nov 3 three and Hugo's Nov 2 reply. Fairhaven: a staff email naming no building gets a delivered answer. |
| 3 | One reply door for every notice (Fede, Nov 2: "any Clara communication like people should be able to reply to it…"). Register the check-in and the two virtual-tour emails to the host, then the rest. A build check, with a planted control, fails any staff email sender that skips it. A check-in answer becomes tour notes, never a relay. | Clara Refactor (001) | 2 PRs | Fairhaven: a staff test mailbox answers one notice of each kind; each lands on its own thread. |
| 4 | Context on every staff turn: the subject, the quoted question, and what the reply's headers matched. The model reads them and decides (Nov 2 rule: reasoning, never patterns). | Clara Refactor (001) | 1 PR | Replay, including a forward and a reply with no headers; Fairhaven live. |
| 5 | Standing fixture set, "staff replies to Clara's own notices", from real records: the Nov 3 three, Hugo, Kat on Alicia J., and Eileen's note to Deiby (right move: silence), plus reply-all, forward and no-building variants. Graded on "right tour, right thread, reply left", never "replied or stayed quiet". Session-run on the subscription for every PR on this path; not in CI. | Testing | 1 local harness | Red on today's main, green after 1 to 4; putting the drop back turns it red. |
| 6 | Say why a reply was blocked: the inbound-email log names the gate and building, the reply step logs a set-aside match, the app marks an unsent reply "not delivered", and a blocked staff reply raises an alert in the alerts channel. Refusal rows stop saying "local". | Clara Refactor (001) | 1 PR | Fairhaven: a reply blocked on purpose shows its reason in the log, the app and the alert. |
| 7 | Sweep the about 271 "no building, do nothing" guards, starting with this path: staff email turn, reply step, notice door, send gate. | Scope resolver | Chain, one concern each | Read-only replay of real records per PR; only the fixed cases change. |
| Link | Where | What it does |
|---|---|---|
| 1 | src/lib/domain/escalation/reply-release.ts:653-663; test __tests__/reply-release-message-id-routing.test.ts:165-189 | resolveMatterIdForReply matches In-Reply-To / References to the Message-ID stamped on the check-in's matter (minted at integrations/email/client.ts:314, stored by tour-checkin-ask.ts:575-591). For kind === 'tour_checkin' it returns not_a_matter_reply, the same value as "no match"; process-inbound-email.ts (~645) reads that value as "no open matter at all matched". RunStaffEmailTurnInput has no matter field. The step-aside logs nothing, and the one ingestion row checked (318bfa8c, Nov 2) stored no In-Reply-To, so production cannot show the match fired on Nov 3; this link is read from the code and its test. |
| 2 | agents/clara/lib/email/staff-email-turn.ts:136-140, :222 | staffEmailTurnBody strips the quote; the subject is used only when the body is empty. Otherwise the subject feeds the building guess and the emailSubject row stamp, "never branched on by the writer" (#5333). thread_context returned both open check-ins with matter, tour and person ids and summaries; nothing says which one the reply answers. |
| 3 | staff-email-turn.ts:107-128, :191; identity/find-property-in-text.ts; identity/resolve-by-ref.ts:245-283; agent/conversation-manager.ts:5040-5044; modules/building-module-chain | The anchor runs findPropertyInText on the subject and the RAW body (quote and signature included; Clara's turn gets the stripped one). Strict matching hits a building by name or by address; two or more hits return null, then the first company building by id. appfolio-situsgroup-property-101 sorts first of Situs's 53. On email the staff-thread filter keeps only threads at the anchor; the line-based rule is channel === 'sms' only. A building marked not in PropFlow resolves every module off (building_off). |
| 4 | src/lib/integrations/email/client.ts:1029-1037; escalation/tour-checkin-email.ts; team-notify/inbound-email-reply.ts:186-205 | The #24534 tag applies only when record.mode === 'none'. The check-in declares outboundRecordOnTarget(...), so no rk_ key and no notice row; the door finds only notices in its store. leasing/tour/tour-room-made-notices.ts and tour-room-waiting-notice.ts (virtual-tour emails to the host) declare their record the same way. The door's "answer" branch relays to the person, so registering check-ins (fix 3) needs the tour-notes rule. The check-in file's header assumes "The subject names the home, so that turn files under the tour's building." |
| 5 | agents/clara/lib/email/__tests__/staff-email-turn.test.ts:69-87 | The anchor test uses a substring stub, never the real matcher, always with an empty body, and pins "falls back to the company's first building by id". |
The subject is built from the building's name, and Situs has one Lampliter, so the subject alone should match. Eight Situs buildings carry the Bannock office address (property-101, 113, 50, 61, 90, 91, 93, 96), and the company's front-door entry is named "Situs Group". If the signature or the quoted header in the raw body carries either, Lampliter plus that hit is a tie, and the fallback picks property-101. The PR history names a second possibility: the turn-home step moved the building. Hugo's Nov 2 reply, also through the staff email turn, was filed at Alton (property-112), so the fallback does not fire on every Situs email. Confirm from the raw stored bodies of ingestions fb23bc1d… and b1e9b65d… and the "staff email routed to the staff turn" log line; fix 2 removes the cause either way.
| What | Ids |
|---|---|
| Staff replies (ingestion log) | fb23bc1d… (20:56:47Z, Barcelona); b1e9b65d… (23:53:21Z, Lampliter); Miriam 3c4a6d1e… (22:22:36Z, conversation 4abd32d7…) |
| New conversations | b46a96f8-786a-4287-9927-37ead4a47e5d, 8cc7c241-4138-4597-b5a6-f4bae62771ef; both filed at property-101, tag source backfill, confidence guessed |
| Blocked sends | delivery log edl_78baca75… (20:56:57Z), edl_5fc2ff24… (23:53:33Z); refusals mref_f35106cd…, mref_674339aa… (Miriam, 22:22:46Z), mref_6681c2ca…: team.notices, module_off, env "local" |
| Clara's check-ins (both still open) | matter_tourcheckin_64dfd201… (Lampliter, sent Nov 2 22:45Z, Deiby's thread 7e00314f…); matter_tourcheckin_ff5ef864… (Barcelona, 18:45Z, thread e0663b1f…) |
| Prospects | Stephany pers_605fc468… (conversation e379ef3e…); Patricia pers_cbb66879… (d657e923…, conv_voice_21916806…) |
| Buildings | Lampliter property-8 and Barcelona property-41, sending on (inherited); Situs CFC One property-101, on the company's hidden list, not in PropFlow since Oct 6 (Buildings settings page) |
staff-email-turn.ts:457-462), so the app shows a question that never left.Sources: prod-facts, code-trace, pr-mining, session-mining and prior-decisions notes (Nov 3, session 012 scratchpad); code read on main e1d7fe14; the Situs building list (53 rows), the ingestion log, the delivery log and module-refusal rows pulled Nov 3; PR bodies #24426, #24435, #24534, #24746, #20110; PR and issue dates from GitHub; the open issues above. No message was sent. Prospect names appear as Clara's own notices print them.
Proposed — pending review · design only; nothing sent or changed · builds on the Nov 3 root cause
Scope: a human on the customer's own team (anyone with a PersonRole at the company) reacting to anything Clara sent them, on any channel. Prospects, renters, vendors are out.
"deep. not individual bug fixes. systematic refactor."
Fede, Nov 3
TODAY six doors; each one matches, decides who, picks a building and reads the words on its own
email answering a tagged notice ──► 1 notice door ─┐ 5 ways to match the reply
email answering a check-in ──► 2 matter door │ 5 ways to decide "is this staff?"
any other email to Clara ──► 3 staff email turn │ 3 ways to pick the building
email to the shared inbox ──► 4 shared inbox │ 4+ ways to decide "is this for Clara?"
a text ──► 5 text door │ a refused reply is logged "suppressed",
a call ──► 6 staff phone line ─┘ with no reason
PROPOSED one pipeline, the same six steps on every channel
email ─┐
text ─┼─► 1 WHO ─────► 2 MATCH ──────► 3 SCOPE ──────► 4 READ ───────► 5 ACT ────────► 6 DELIVER
call ─┘ roles, one what Clara the company, one model on the from Clara at
resolver sent, from one then the read; may matched Propflow; every
register record's stay silent thread refusal keeps
building its reason
Recommended: option A. Five PRs, three lanes, no switch. Each goes live on merge, after a replay of all 74 exchanges and a real staff reply at Fairhaven.
An exchange is a staff reaction plus its follow-ups, graded by its worst moment. The eight "staff wrote first" exchanges enter the pipeline as "nothing matched".
| Notice | Channel | Customer | Exch. | Right | Wrong | What went wrong |
|---|---|---|---|---|---|---|
| tour check-in | text | Western Slope | 19 | 16 | 3 | wrong thread, wrong action, silent |
| tour check-in | Situs | 3 | 0 | 3 | wrong building (placeholder), all 3 blocked | |
| escalation ask | Western Slope | 10 | 10 | 0 | ||
| escalation ask | text | Western Slope | 1 | 1 | 0 | |
| escalation ask | Situs | 2 | 0 | 2 | relayed Lee's note to an applicant; Somvang's answer never relayed | |
| call summary / callback | Situs | 10 | 6 | 4 | argued with Hugo's and Eileen's notes to their team, wrong thread (Hugo's David reply), silent | |
| call summary / callback | Western Slope | 4 | 1 | 3 | wrong thread (2, shared inbox taken for a test prospect), wrong building | |
| emergency alert | Situs | 7 | 7 | 0 | ||
| emergency alert | Western Slope | 1 | 0 | 1 | wrong action (banter saved as a company note) | |
| tour confirmed / update | text | Western Slope | 4 | 3 | 1 | wrong action |
| tour confirmed | Situs | 1 | 1 | 0 | ||
| virtual-tours intro | Situs | 3 | 2 | 1 | answered Eileen's question to Deiby | |
| welcome email | Situs | 1 | 1 | 0 | ||
| staff wrote first | text | Western Slope | 3 | 0 | 3 | wrong action (2), wrong building ("not set up under your access") |
| staff wrote first | Western Slope | 1 | 0 | 1 | silent (Jason Fish's showing request) | |
| staff wrote first | call | Western Slope | 1 | 0 | 1 | wrong action (refused a lookup) |
| staff wrote first | call | Situs | 1 | 0 | 1 | wrong action (Hugo taken for a renter; techs paged) |
| staff wrote first | call | JP&Co | 2 | 2 | 0 | |
| Total | 74 | 50 | 24 | wrong action 7, wrong building 5, wrong thread 4, spoke when quiet was right 4, silent when she should speak 4 |
Situs breaks on email: replies miss their thread, and Clara talks over staff-to-staff notes. Western Slope breaks on what Clara does next, and its shared inbox is taken for a test prospect.
CLARA SENT REPLY DOOR MATCHES THE REPLY BY LANDS ON
tagged notice email ────────► 1 NOTICE DOOR its reply tag ───────────► the notice's thread
check-in or matter email ───► 2 MATTER DOOR its message id ──────────► the matter's thread
check-in: match DROPPED ─┐
any other email to Clara ───► 3 STAFF EMAIL TURN ◄────────────────────────────┘
nothing ─────────────────► a NEW conversation
email to the shared inbox ──► 4 SHARED INBOX nothing ─────────────────► whoever claims the address
(leasing@ = a test prospect)
a text ─────────────────────► 5 TEXT DOOR one open ask, 24 hours ──► the line's staff thread
a call ─────────────────────► 6 STAFF PHONE LINE nothing ─────────────────► the call record
some staff email ───────────► none of the six (lead router, "data only") ─────► dropped, no reply
DECIDED AGAIN AT EVERY DOOR
WHO IS STAFF WHICH BUILDING FOR CLARA? WHAT IS IT?
door 1 role, else the mailbox domain notice's; files 1st by id model read; approvals by "?"
door 2 owner, @propflowai.co, role or matter's model read
domain, current owner
door 3 role named, else FIRST BY ID read skipped if only to Clara
door 4 address claim; role OR domain the mailbox's none: handled like a prospect
door 5 the phone's person and role the line's one-open-ask rule; "which tour?"
door 6 the phone's person (Hugo: none) the line's model; lookups only
SEND every door: the module switch at "its" building; a refusal is logged "suppressed", no reason
TOTAL 5 ways to match · 5 ways to decide "staff" · 3 building pickers · 4+ readers of the words
A TEAMMATE REACTS: email, text or call; same steps on every channel
│
1 WHO one resolver: the address decides the company, then the person, then their roles
│ no role at the company ──► not this pipeline (prospect, renter and vendor paths)
2 MATCH the register of everything Clara sent a teammate: reply tag, message id, quoted text,
│ the line's open asks ──► the notice, matter, tour, prospect and thread it answers,
│ or the open asks it could answer; never a time window, never a guess
3 SCOPE the company, narrowed to the matched record's building, else a building the words name
│ never a hidden or placeholder building, never "first building by id"
4 READ one model read: words, subject, quoted text, To/Cc with roles, thread, the match
│ ──► answer · handled · question · note · for someone else · unclear
│ staff talking to staff ──► silent
5 ACT Clara's turn on the matched thread, the match in her context:
│ tour notes, relay to the prospect, close, look up, do what she was asked
6 DELIVER teammates hear from Clara at Propflow; one send gate at the scope from step 3
every refusal stores its reason and scope; failures go to the alerts channel
Steps 1 to 3 are lookups with one right answer (the thin code layer, Oct 25); judgment is step 4.
| # | Principle | Fede's ruling |
|---|---|---|
| 1 | Every Clara message to a teammate registers with the one reply door; a build check with a planted control fails any sender that skips it. | Nov 2: "any Clara communication like people should be able to reply to it natural language" |
| 2 | What the reply answers rides into Clara's turn as context; never dropped, never worked out again. | Oct 16: "do we not have previous context on what was sent? embarrassing"; Nov 2: "it's pretty embarrassing to say 'what was this for?'" |
| 3 | Scope starts at the company and narrows to the matched record's building when known; never a hidden, not-in-Propflow or placeholder building, never first by id. | Nov 2: "this should not matter if theres a property or not in the logic" |
| 4 | Who someone is comes from roles through one resolver (Gera's model); never a mailbox, domain or address shortcut. | Nov 2: "always check roles" |
| 5 | The model reads subject, quoted text, headers, thread and roles, decides what the message is and whether Clara speaks; staff talking to staff gets silence. | Nov 2: "we should be reasoning on whether they asked us a question or not, not having hardcoded characters" |
| 6 | Every refusal to send stores its reason and the scope it was judged at; a failed relay is told apart from one that ran. | Oct 25: internal failures go to the alerts channel and the record |
| 7 | Teammates hear from Clara at Propflow; outside people hear from the customer's mailbox. | Nov 3: "It should be from Clara at Propflow for internal team notifications. And it should be leasing outside to prospects, not internally." |
| 8 | Email, text and calls take the same steps. | Oct 26: "channel should not matter" |
| Deleted | Where | Replaced by | PR |
|---|---|---|---|
| Check-in match dropped | src/lib/domain/escalation/reply-release.ts:663; pinned by __tests__/reply-release-message-id-routing.test.ts:165-189 | the match carried into the turn | 1 |
| Subject used only when the body is empty; quote stripped before the turn | agents/clara/lib/email/staff-email-turn.ts:136-140 | subject and quoted ask in the turn's context | 1 |
| Staff email turn takes no match | staff-email-turn.ts:142-152 (RunStaffEmailTurnInput) | the door's match | 1 |
| Tag only when the send declares no record (check-ins get none) | src/lib/integrations/email/client.ts:998-1060 (staffNoticeTagFor, record.mode === 'none'); src/lib/domain/escalation/tour-checkin-email.ts:95 | every staff send writes a notice row (the store from #notice-context) | 2 |
| One open ask per teammate, 24-hour window | src/lib/team-notify/sms-reply-match.ts; sms-ask-gate.ts (SMS_ASK_OPEN_WINDOW_MS) | the line's open asks from the register, read in step 4 | 2 |
| "The server picks which tour" by person, then "which_tour" | agents/clara/lib/agent/tools/staff-leasing-desk.ts:2040-2081 (findOpenTourCheckinsForStaff) | the register's match | 2 |
| Texts-only staff-thread rule | agents/clara/lib/agent/conversation-manager.ts:5041-5046 | the matched thread, every channel | 2 |
| First building by id as the anchor | staff-email-turn.ts:107-128 | company scope, narrowed by the match or the words | 3 |
| First building by id as the filing key for company-wide holds | src/lib/team-notify/notice-thread-turn.ts:165 (companyFilingBuilding) | holds filed at the company | 3 |
| Same-mail-domain arm | agents/clara/lib/email/internal-recipient.ts:45-64; direct uses process-email-record.ts:642, 993, process-inbound-message.ts:1114, 1561, 2082; the Lambda caller lambda/inbound-processor/handler.ts:978 goes in the shared-inbox fix in flight tonight | the resolver's roles | 4 |
| @propflowai.co, owner snapshot, current owner and building-mailbox checks | reply-release.ts:561 (isStaffOnMatterThread) and ~700-745; src/lib/team-notify/inbound-email-reply.ts:93-119 | the resolver's roles | 4 |
| "?" and word lists on approval replies | src/lib/domain/staff-reply/door.ts:132 (reviewerIntent) | the one read | 5 |
| Read skipped when mail is to Clara alone | staff-email-turn.ts:346 (readStaffEmail) | the one read, always | 5 |
| Prompt-only silence rule | agents/clara/lib/agent/staff-desk-persona.ts:125 (STAFF_EMAIL_SILENCE_RULE) | the read's silent outcome | 5 |
clara_turn returned when the relay failed and when it ran | src/lib/team-notify/ingest.ts ~215-300 | sent / refused / failed, each with a reason | 5 |
| Reply saved before the send | staff-email-turn.ts:457-462 | saved with its delivery state | 5 |
56 "no building, stop" exits found by pattern search (a floor) in the doors' code and Clara's staff tools. My read of each:
| Exit | What it does with no building | Verdict | PR |
|---|---|---|---|
staff-email-turn.ts:194 | "not a staff turn" | changes behavior | 3 |
notice-thread-turn.ts:250, 274 | reply not sent, hold not filed | changes behavior | 3 |
inbound-email-reply.ts:113 | mailbox check says "not staff" | changes behavior | 4 |
reply-release.ts:3167 | relay email not sent, a warning only | changes behavior (silent drop) | 5 |
staff-leasing-desk.ts:2044 | tour notes: "nothing to attach" | changes behavior | 3 |
staff-leasing-desk-team-sms.ts:97 | held drafts not withdrawn | changes behavior | 3 |
conversation-manager.ts:7409 | the turn loads no building or company context | changes behavior | 3 |
conversation-manager.ts:5076 | staff-thread filter off | changes behavior | 2 |
process-envelope.ts:1071, 1115, 1177, 1238, 1296; staff-line-thread.ts:54; open-matter.ts:1636, 2587; sms-recipients.ts:40; sms-sender.ts:33, 36; voice-handoff.ts:146 | staff holds, threads and team texts need a building | likely; checked in PR 3 | 3 |
inbound-dispatcher.ts ×6 | a line with no building is refused | line routing; check against Gera's address map | — |
29 others (conversation-manager.ts ×7, process-inbound-email.ts:1611, 1771, handler.ts:599, open-matter.ts:451, process-envelope.ts:219, tour-checkin-ask.ts:403, staff-tool lookups ×16) | the record's own building was deleted, or the work is about one building | fine under the Nov 2 rule | — |
| Option | What it is | What it fixes | What it leaves |
|---|---|---|---|
| A. One new pipeline (recommended) | Five PRs; each moves doors onto the pipeline and deletes their old code in the same PR. | All six doors, every channel. | Nothing old running. Five PRs is the ceiling of the "cut by proof" rule. |
| B. One front door over the six | A single entry that hands each reply to its old door; old code deleted later. | One place to log and test. | Five matchers and five "staff" checks still run behind it; the front door becomes door seven. |
| C. Keep the six; pass the match along | Each door hands what it matched to Clara's turn. | "Which one?" on matched replies. | Everything else; the next new sender reopens the hole. |
Why A. The same failure hit four paths, each fixed alone; the fourth reached a customer the day after the third fix. Only deleting each door in the PR that replaces it stops the next sender routing around the fix; "delete later" is the root cause's pattern 5.
Risk. Live behavior changes at three customers; Western Slope's check-in texts (16 of 19 right) could regress when the text door moves. Every PR replays all 74 first and may change only the cases it names.
| # | What changes | Lane | Proof: fixtures, then live | Deletes |
|---|---|---|---|---|
| 1 | Keep the match and carry it (email, urgent). A matched reply runs on the matched record's thread and building, with the notice, matter, tour and prospect it answers, the subject and Clara's quoted question in her context. A check-in answer becomes tour notes on that tour, never a relay. | Clara Refactor 001 | Deiby's two replies, Miriam's check-in reply, Hugo's David reply. Fairhaven: a test mailbox with two open check-ins answers one "no-show"; Clara notes that tour and her reply arrives. Record ids. | the check-in drop and the test that pins it; subject and quote stripping; the match-less staff turn |
| 2 | One register for everything Clara sends a teammate. Every staff send (notices, check-ins, matter emails, virtual-tour host emails, texts) records what it was about; replies on every channel look there first. A build check over the sender audit's inventory, with a planted direct send as its control, fails any sender that skips it. | Clara Refactor 001 (inventory from 011) | Kat's "She never showed" (three rounds), the other Western Slope check-in text cases, virtual-tour replies. Fairhaven: a test mailbox answers one notice of each kind, each lands on its own thread; one texted check-in answered. | tag only when no record; one open ask in 24 hours; tour picked by person; texts-only thread rule |
| 3 | Scope at the company. The company, narrowed to the matched record's building, else a building the words name; holds filed at the company; the turn loads company context when no building is known. | Scope resolver | The Situs check-in replies with the match removed, Hugo's David reply, Kat's "not set up under your access". Fairhaven: a staff email naming no building gets a delivered answer. | both first-by-id pickers; six no-building exits; the 12 likely ones once checked |
| 4 | Who is writing, from roles, every channel. One resolver call: the address decides the company, then the person, then roles; no role means not this pipeline. Calls also get the caller's open asks from PR 2's register. Data: the shared-inbox address comes off the test prospect at Western Slope; Hugo's phone moves to his own person (both on your go). | Sender identity (011), on Gera's resolver | Jay's shared-inbox email (taken for a test prospect), Hugo's test call as a simulated conversation (no call minutes), a same-domain address with no role. Fairhaven: that address is treated as outside; a teammate hears from Clara at Propflow. | mail-domain arm (callers left after tonight's shared-inbox fix); @propflowai.co shortcut; owner and current-owner checks; building-mailbox check |
| 5 | One read, one send record. Every reaction gets the one model read (step 4) and speaks or stays silent. Every refusal stores reason and scope; a blocked or failed reply alerts the alerts channel and shows "not delivered" in the app; refusal rows stop saying "local". With A on decision 4, Team notices off stops notices, not answers. | Clara Refactor 001 | The five quiet cases stay quiet; the four "spoke when quiet was right" go quiet, including Lee's note that leaked to an applicant; Somvang's answer relays. Fairhaven: a staff-to-staff reply-all gets no answer; a reply blocked on purpose shows its reason in the log, the app and the alert. | the "?" rule; read skipped when mail is to Clara alone; prompt-only silence rule; two-meaning relay result; save before send |
Order. Now: PR 1, the shared-inbox fix in flight, the harness's first run. Then PRs 2, 3 and 4 in parallel (4 after the shared-inbox fix; its call part after 2). PR 5 last. Each: replay green, merge, Fairhaven run with record ids.
No switches: all five fix live behavior. Call actions and Jason Fish's showing notice would be new: outside this chain, default-off setting, your turn-on.
| # | Files (main a4eef6ba) |
|---|---|
| 1 | src/lib/domain/escalation/reply-release.ts, agents/clara/lib/email/process-inbound-email.ts, agents/clara/lib/email/staff-email-turn.ts, Clara Refactor 001's turn-context loader |
| 2 | src/lib/integrations/email/client.ts, src/lib/domain/escalation/tour-checkin-email.ts, src/lib/domain/leasing/tour/tour-room-made-notices.ts, tour-room-waiting-notice.ts, src/lib/domain/escalation/staff-line-thread.ts (the 13 text sends), src/lib/team-notify/sms-reply-match.ts, sms-ask-gate.ts, agents/clara/lib/agent/tools/staff-leasing-desk.ts, agents/clara/lib/agent/conversation-manager.ts; new build check plus its control under scripts/mutation-controls/, enumerating the 79 staff email and 13 text send sites in 011's inventory |
| 3 | staff-email-turn.ts, src/lib/team-notify/notice-thread-turn.ts, staff-leasing-desk.ts, staff-leasing-desk-team-sms.ts, conversation-manager.ts, agents/clara/lib/messaging/process-envelope.ts, src/lib/domain/escalation/open-matter.ts, src/lib/team-notify/sms-recipients.ts, sms-sender.ts |
| 4 | agents/clara/lib/email/internal-recipient.ts, reply-release.ts, src/lib/team-notify/inbound-email-reply.ts, agents/clara/lib/email/process-email-record.ts, process-inbound-message.ts, the call's caller lookup; uses src/lib/integrations/email/team-recipient.ts (the role read the sender fix added) |
| 5 | src/lib/domain/staff-reply/door.ts, staff-email-turn.ts, agents/clara/lib/agent/staff-desk-persona.ts, src/lib/team-notify/ingest.ts, src/lib/domain/escalation/staff-loop/reply-meaning.ts, the refusal rows |
The fixture set: the 74 exchanges from real records, plus reply-all, a forward, no headers, no building named, and two open asks to one teammate (deferred Oct 7). A case passes only on the right record, thread, action and delivery, or on silence when silence is right; "replied or stayed quiet" never passes.
| PR | Green means | Planted defect that must turn it red |
|---|---|---|
harness red run, Nov 3, main 8a0148fe: 22 pass / 19 fail / 2 skipped (43 fixtures: 13 from real incidents, 5 controls where Clara was right, 25 synthetic). A grader that only checks "replied or stayed quiet" passes 17 of the 19 failures. Failures on main: reply to a check-in lands on a new thread (9), handled at the hidden office building then blocked (6), "which one didn't show?" (3), the expected action never happens (4), an operator note relayed to a renter (1), self-contradiction about the building (1), silence after an owner's instruction (2, expectation pending Fede). Suite: scripts/coworker-harness/staff-reaction-regression/, branch fede/staff-reaction-regression-harness, run on the subscription, wire cut at the socket; calls skipped (voice minutes). Full table: results/before-2026-11-03.md. | ||
| 1 | The three Situs check-in replies and Hugo's David reply land on the matched thread, right tour, reply delivered. | Put the check-in drop back. |
| 2 | Every send site in the inventory registers; Kat's three rounds resolve on the first; Western Slope's 16 right check-in texts stay right. | A staff send that skips the register: the build check goes red. |
| 3 | No case is filed under a hidden or placeholder building; no-building cases get a delivered answer at company scope. | Put "first building by id" back. |
| 4 | Shared-inbox and same-domain cases resolve by role; no outside person is taken for staff. | Put the mail-domain arm back. |
| 5 | The five quiet cases and the four staff-to-staff cases stay quiet; Somvang's answer relays; every refusal row has a reason and a scope. | Put the "?" rule back; drop the refusal reason. |
Standing rule. Every PR replays the whole set on the subscription, never in CI, posts the before/after grid, and may change only the cases it names (the Oct 25 gate, replay harness). Then a real staff reply at Fairhaven, from a corporate test mailbox, proves it with record ids. Calls: simulated; one real call at most.
scripts/coworker-harness/staff-reaction-regression/ (world, wiretap, local DynamoDB), built tonight on its own worktree: no PR, no CI. Input: staff-reaction-corpus.json. Separate from evals/replay-harness/ (day-0 byte diff); both run per PR.
| Who | What Clara should have done | Recovery | State |
|---|---|---|---|
| Somvang Keomanee, prospect, Barcelona | Pass on Miriam's 4:33 PM answer on fees | A text with her answer and an offer to book a tour | preview ready |
| Stephany M., prospect, Lampliter | Follow up on her cancel-and-reschedule text; Lee's offer is unanswered | A text picking up the reschedule, including a late showing | preview ready |
| Patricia R., prospect, Barcelona | Follow up on a true no-show | A text asking her to pick a new time | preview ready |
| Deiby, Situs | Answer her two check-in replies | Short email: both are being followed up; Stephany cancelled rather than missed | intent only |
| Miriam, Situs | Pass on her fee answer (her 3:22 PM question was answered by hand at 5:28 PM) | Short email once Somvang's text goes: her answer reached him | intent only |
| Jay, Western Slope (Oct 18) | Answer whether maintenance still goes to their call center; he got a reply in a test prospect's thread | Short email with the current routing | intent only; needs the routing fact |
| Jason Fish, Western Slope (Oct 31) | Tell the 239 S Elm tenants about the Nov 2 showing; nothing happened and he got no reply | The showing has passed: tell Jason it was missed. Whether Clara can send that notice is unverified; if not, it is new, not a recovery | intent only |
| Kat, Western Slope (Oct 14) | Save Scott Jacob's number; Clara refused it | Save it on his record; no message | three weeks old; check it is still wanted |
| Identity records | Western Slope's shared-inbox address sits on a test prospect; Hugo's phone on a person with no role | Remove the claim (our test record, in Western Slope's live data); move Hugo's phone (customer data) | ready; both need your go |
Previews are in the session, not on this page; nothing goes without your OK on the exact words.
Sources: staff-reaction corpus (88 message rows, 74 exchanges, Oct 5 to Nov 3, read-only from production), prod-facts, code-trace, pr-mining, session-mining and prior-decisions notes (Nov 3, session 012 scratchpad); code read on main a4eef6ba0e; the sender audit (011, Nov 3) and its merged fix #25042; the shared-inbox sender fix in flight (plan #25040); Gera's portfolio architecture page (model, wall, people, change); this page's #notice-context, #dropped-ball, #architecture, #core and #replay-harness sections. No message was sent.
Local POC on branch fede/staff-reply-pipeline-poc, run on the subscription with the wire cut, both model columns complete runs on the same code; the overnight bar (net better, zero regressions) is met, so the fix chain (4 PRs, no switches) is being cut from it; the real Fairhaven staff reply is PR 1's proof.
A local proof of concept of the one-pipeline staff-reply design (who by roles, match from one register, company-first scope, one read, act on the matched thread), run against the 43-case staff-reaction harness on the local test world. One full run per column on the final POC code (branch fede/staff-reply-pipeline-poc, code at 484558bd1c). Nothing was sent; subscription only. Opus column via an env override used only for this run, proven by a control with a non-existent model name (8 not-found errors). The default model is unchanged.
Bottom line: 22 → 32 passing on today's models, 33 on Opus 5.5, zero regressions. The model gap (32 vs 33) is within run-to-run noise.
| Run | PASS / FAIL / SKIP | Median sec per fixture | Total min |
|---|---|---|---|
| main (Nov 3) | 22 / 19 / 2 | – | – |
| POC, current models | 32 / 9 / 2 | 11.7 | 8.1 |
| POC, Opus 5.5 | 33 / 8 / 2 | 16.7 | 11.1 |
Cost: the runner reports no token counts; seconds per fixture (whole fixture: world seed, notice, reaction, grade, judge) are in the grid. On the subscription there is no metered cost.
| Fixture | Before (main) | POC, current models | POC, Opus 5.5 | sec current / Opus |
|---|---|---|---|---|
control-01-recap-to-coworker-cc-clara | PASS | PASS | PASS | 4 / 6 |
control-02-thanks-on-intro | PASS | PASS | PASS | 2 / 3 |
control-03-ive-got-this-one | PASS | PASS | PASS | 3 / 5 |
control-04-praise-on-tour-confirmed | PASS | PASS | PASS | 2 / 3 |
control-05-text-thanks-after-note | PASS | PASS | PASS | 14 / 24 |
seed-01-checkin-noshow-followup | FAIL (thread, action, scope, delivery) | PASS | PASS | 27 / 27 |
seed-02-checkin-two-open-asks | FAIL (thread, subject, action, scope, delivery) | PASS | PASS | 30 / 32 |
seed-03-checkin-unrelated-question | FAIL (thread, scope, delivery) | PASS | PASS | 17 / 23 |
seed-04-escalation-answer-relay | PASS | PASS | PASS | 16 / 17 |
seed-05-call-summary-complaint-office-claim | FAIL (delivery) | FAIL (delivery) | FAIL (delivery) | 5 / 6 |
seed-06-call-summary-no-building | FAIL (delivery) | PASS | PASS | 12 / 14 |
seed-07-text-checkin-never-showed | PASS | PASS | PASS | 11 / 15 |
seed-08a-staff-to-team-remark | PASS | PASS | PASS | 3 / 4 |
seed-08b-owner-remark-about-test-caller | PASS | PASS | PASS | 5 / 12 |
seed-09-coworker-question-on-clara-thread | FAIL (scope) | PASS | PASS | 2 / 2 |
seed-10-owner-test-call | SKIP | SKIP | SKIP | 0 / 0 |
seed-11-new-email-notify-tenants | PASS | PASS | PASS | 13 / 20 |
seed-12-text-contact-number | PASS | PASS | PASS | 14 / 17 |
syn-01-checkin-reply-plain-signature | FAIL (thread, action) | PASS | PASS | 15 / 26 |
syn-02-checkin-forward | FAIL (thread, action) | FAIL (thread) | FAIL (thread) | 14 / 21 |
syn-03-checkin-new-email-no-headers | FAIL (action, scope, delivery) | FAIL (scope) | FAIL (scope) | 15 / 28 |
syn-04-checkin-reply-platform-staff | PASS | PASS | PASS | 12 / 17 |
syn-05-checkin-reply-cc-renter | FAIL (action) | FAIL (action) | FAIL (action) | 26 / 36 |
syn-06-checkin-staff-to-staff-cc-clara | PASS | PASS | PASS | 10 / 12 |
syn-07-call-summary-reply-instruct | PASS | PASS | PASS | 17 / 21 |
syn-08-call-summary-forward | FAIL (thread, scope, delivery) | FAIL (thread, scope) | FAIL (thread, action, scope) | 20 / 25 |
syn-09-call-summary-no-building-question | FAIL (action) | PASS | PASS | 10 / 17 |
syn-10-call-summary-reply-platform | FAIL (action) | FAIL (action) | FAIL (action) | 13 / 16 |
syn-11-escalation-answer-plain | PASS | PASS | PASS | 19 / 18 |
syn-12-escalation-answer-owner-company-sig | PASS | PASS | PASS | 19 / 35 |
syn-13-escalation-two-open-asks | PASS | PASS | PASS | 16 / 19 |
syn-14-escalation-reply-cc-renter | PASS | PASS | PASS | 5 / 8 |
syn-15-tour-confirmed-move-time | FAIL (action) | FAIL (action) | FAIL (action) | 9 / 15 |
syn-16-tour-rescheduled-ack | PASS | PASS | PASS | 2 / 4 |
syn-17-tour-cancelled-rebook | FAIL (action) | PASS | PASS | 11 / 20 |
syn-18-intro-question | FAIL (thread, action, scope, delivery) | FAIL (action) | FAIL (action) | 12 / 14 |
syn-19-application-invite-already-applied | FAIL (thread, action) | FAIL (subject) | PASS | 10 / 13 |
syn-20-digest-close-out | FAIL (thread, action) | PASS | PASS | 11 / 20 |
syn-21-text-ask-renter-question | PASS | PASS | PASS | 16 / 20 |
syn-22-new-email-metrics | FAIL (scope, delivery) | PASS | PASS | 10 / 14 |
syn-23-text-new-note | PASS | PASS | PASS | 10 / 14 |
syn-24-platform-new-email | PASS | PASS | PASS | 3 / 3 |
syn-25-checkin-call-reply | SKIP | SKIP | SKIP | 0 / 0 |
seed-01, seed-02, seed-03 (check-in replies: run on the check-in's own thread at the tour's building, the tour and prospect carried, no "(by text)" on an email note, sent from clara@); seed-06 (call summary with no building: company scope, Clara texts Dale); seed-09 (coworker question on a company thread: quiet, company scope, never the hidden office); syn-01; syn-09 (no-building call question answered right this run; it flips, see variance); syn-17 (tour-cancelled notice now reaches the desk: rebook follow-up); syn-20 (digest reply finds its notice, Paula noted, Ben left alone); syn-22 (new email naming Birch: scoped to Birch, not the hidden office).
Breaks: none. Every fixture green on main is green on the POC, both models.
PR 1 green targets: 6 of 9 fully green (seed-01/02/03, seed-06, syn-01, syn-20). syn-18 now passes thread, scope and delivery (the PR 1 part: its reply finds the intro notice through the new register) and fails only the judge on wording (it passed in the earlier run). syn-02 and syn-19 stay red, below.
| Fixture | Why (current models) |
|---|---|
syn-02 checkin forward | A forward carries no In-Reply-To/References, so nothing matches; the turn runs at Birch (named in the words) and records the no-show, but on a new thread. Needs a match source for forwards (design delta 3). |
syn-19 already applied | Clara notes it on Paula's card with Paula's own tour id; the grader only allows tour ids that a notice names, and this notice names none. Likely a grader over-strictness (Opus passes by omitting the tour id). Raise with the harness owner. |
seed-05 complaint, office claim | The read says "for someone else" ("Hey guys…", Clara in the third person). The fixture wants Clara to answer. Same on both models: a real judgment question for the design (should Clara answer a question the owner asks the team about her?). |
syn-03 new email, no headers | "at Birch" is not found by the building-in-text matcher, so it runs company-wide. PR 3's (allowed red). |
syn-05 reply cc renter | Model quality: with Paula copied, the reply exposes "marked a no-show". Both models. |
syn-08 call summary forward | Forward, no match (as syn-02); company scope. PR 3's building check (allowed red). |
syn-18 intro question | Matched, scoped and delivered right; the judge fails the wording (offers to text the link to prospects, a capability the notice never named). Both models this run; current passed it in the earlier run. Model quality, not routing. |
syn-10 platform reply | Sender is a Propflow operator with no role: not staff, so the old notice door runs it, unchanged from main. Out of the POC's "who" step by design. |
syn-15 move the tour | The harness world has no readable calendar, so reschedule_tour refuses (calendar_unavailable); Clara then asks staff instead of checking 4pm with Paula. Harness world gap plus model choice. |
Opus vs current (this run): Opus also passes syn-19 (it leaves the tour id off the note) and fails nothing current passes: 33 vs 32, about 40% slower per fixture (median 16.7 s vs 11.7 s).
Two full pairs were run (the earlier one before the last scope fix). Rows that changed verdict between them on the same model:
| Fixture | Current: run 1 → run 2 | Opus: run 1 → run 2 | Check that flips |
|---|---|---|---|
syn-01 | PASS → PASS | FAIL → PASS | action (follow-up to Paula started or not) |
syn-09 | FAIL → PASS | PASS → PASS | action (judge on the reply wording) |
syn-18 | PASS → FAIL | FAIL → FAIL | action (judge: invented capability) |
So the honest read is: current 32 both times, Opus 32 then 33; the model gap is within the noise of one or two judge-graded rows. Routing checks (thread, scope, subject, delivery) did not flip on any row.
src/lib/domain/escalation/staff-loop/reply-meaning.ts classifies by "what should the renter hear" (answer / handled / question / note / unclear / for someone else). On a teammate's reply, "she no-showed, can you mark it", "close out Paula", "call him and explain" read as note or handled, and Clara went silent (POC run 1: 11 of 23 on the red set plus controls). The POC adds its own read, src/lib/domain/staff-reply/pipeline-read.ts: act / relay_to_person / remark / for_someone_else, with what Clara sent, the quote and everyone's roles. That flipped seed-06, syn-20, syn-22 and kept all controls quiet. The design's "one read" needs a new staff-reply taxonomy, not the renter-relay reader.src/lib/domain/escalation/reply-release.ts) and a renter-ask answer (src/lib/team-notify/ingest.ts) each read again. The POC enters them from the pipeline with the match made (two reads on those paths). Merging means the relay takes the pipeline's reading as input.syn-02, syn-08. Needs the forwarded Message-ID from the body or a token in Clara's own footer; the design says header/SMS only.sendEmail (src/lib/integrations/email/client.ts) only tagged record-less staff sends. The POC tags and registers every single-address staff send whose record names a conversation (staffRecordRegisterTagFor). That is what made syn-18/syn-20 match. Side effect to check: the check-in's Message-ID is now an rk_ id; the matter stores the real one, so the matter match still works.syn-18). Production needs a real marker on the company-line building (role: 'company_line_umbrella' is not set in the fixture world). File: src/lib/domain/staff-reply/pipeline-match.ts.inboundOnThread), the pattern notice-thread-turn.ts already uses, instead of writing the staff words as the renter's inbound.staff-desk-writes.ts, staff-leasing-desk.ts); the POC labels by the channel the teammate used. The tool log still says actor=clara-sms on an email turn.The first slice of the chain, #25097 (a teammate's reply carries its match into the turn), merged at 09:58Z and was in production by 11:59Z. Two review rounds: the org fence was skipped for matter matches, and a notice row registered on the same Message-ID let the notice door take an owner's reply before the matter's own release ran. The door now steps aside for a matter, in one place, on both lanes.
| Harness (43 cases) | Pass | Fail | Skipped (phone) |
|---|---|---|---|
| main before the chain | 22 | 19 | 2 |
| #25097 | 33 | 8 | 2 |
Live proof at Fairhaven. A leasing-agent test mailbox replied to Clara's Nov 2 check-in on Tobias W.'s tour ("It went well… wants to apply this week. Please add that to his card."). The reply matched the check-in, stayed on its thread and building, the tour was marked attended with the note queued to the card, the check-in went from waiting on staff to resolved, and Clara's answer reached the mailbox from clara@propflowai.co 58 seconds later. Ids: PR comment. Two cosmetic follow-ups filed: the "Property:" line appears twice (#25137) and the sender name reads "Clara" instead of "Clara at Propflow" (#25138).
Next in the chain: PR 2 (#25120, armed) extends the read; PR 3 forwards, PR 4 the text lane, PR 5 the Message-ID claim across lanes, each opened only once its base is in main.
Twelve "staff wrote first" dialogues, built from the job map (morning queue, walk-in card plus showing, notes from the car, application chase, no-show then mark inactive, pest notice in two languages, resident ledger, open a work order, owner question, notify two units, driving catch-up, staff email cc'ing Clara) were added to the regression harness and run on main and on the proof-of-concept branch. Both pass 2 of 12. The failures are missing capabilities, not bad judgment:
| Gap | Dialogues it blocks | Where |
|---|---|---|
| No tool to set a follow-up on a card | 03 | staff desk tools |
| No tool to mark a card inactive | 05 | staff desk tools |
| Work orders blocked on staff turns | 08 | staff-write-gate |
| No application-status read | 04 | staff-portfolio |
| Thread history omits Clara's own earlier free-text answers, so a bare "Yes." points at nothing | 04, 11 | thread-context |
| Resident messaging only as a manager's text; the mass-text handler takes the building from the staff thread | 06, 10 | mass-comms, handle-send-mass-message |
| No owner role; the spam classifier drops the owner's email | 09 (allowed to fail) | types, process-inbound-email |
| Showing booked at the card's building, not the one staff named | 02 | fixed in flight: PR #25150 |
This is the before-number for the superpowers work in the architecture re-pass above. Full 57-row grid: harness branch fede/staff-reaction-regression-harness, results/gold-2026-11-04.md. One run per side; single-row flips between main and the POC are noise until they repeat.
What happened. Nov 4, 8:57 AM MT: Miriam Gallegos (Situs PM) started a group text with the leasing team and Clara's Situs line: "just want to test if we can have a group text with the leasing team. Any showings scheduled today?" The carrier delivered it to Clara's line as a plain one-to-one MMS (Twilio sid MMd6e95ed17c…, no other recipients exposed). Clara answered Miriam alone, correctly ("no showings today", confirmed by Fede), and told her replies come back on the same thread. Her teammates saw the question but never Clara's answer.
Why it cannot work today. Clara's lines run on Twilio Programmable Messaging: one number, one webhook, one recipient per reply. A group MMS has no "reply all" on that path, and the inbound webhook does not list the other members.
| Option | How | What the team gets | Cost / risk | Verdict |
|---|---|---|---|---|
| A. Twilio Conversations group texting on the existing line | Put Clara's company line on a Conversations address configuration with autocreation on. An inbound group MMS creates a Conversation with every member as a participant; Clara replies into it and everyone sees the answer. The number's own webhook still fires, so one-to-one traffic keeps its current path; the new handler acts only on group conversations. | A real group thread: any teammate can ask, everyone sees Clara's reply. Members identified by roles from their numbers, so Clara knows who is in the room. | Only +1 long codes (Situs +1 720 740 4240 and Fairhaven +1 720 807 1724 qualify; toll-free lines do not). 10 participants max. Third-party prod config change on the number (Fede's go). Open mechanics to prove at Fairhaven first: that one-to-one messages do not double-fire once autocreation is on. | Recommended |
| B. Second vendor (Telnyx or Bandwidth) for group threads | Group MMS with explicit cc list on a new number. | Same as A. | New vendor, new number per customer, new A2P registration, split messaging stack. | No |
| C. No group thread; Clara broadcasts | Each teammate texts Clara directly (works today); Clara posts a morning showings summary to the team's phones. | Answers reach everyone, but as separate texts; no shared conversation. | None. | Fallback while A is proven |
Shape of A, if chosen. New capability, so it ships dark behind one company setting ("Clara joins team group texts", default off; Fede turns it on per customer). Three PRs: (1) a Conversations webhook door that recognizes a group conversation and maps its participants to people by their roles at the company (unknown numbers are named "someone on the team", never guessed); (2) the staff desk turn runs on the group as one conversation, with every member's role in context and one reply to all; (3) the per-line Twilio address configuration, written only on Fede's go, Fairhaven first, proven with one real group text from two test phones. Rules that carry: identity by roles, no regex on "did they ask Clara", staff hear from Clara's line (shared with prospects, per the Nov 3 ruling).
Who was in Miriam's group is being read from the team's email signatures (cells listed under each name); result lands here.
Read-only inspection, Opus 5.5, Nov 3 night; vision in Fede's words: Clara knows every teammate on every channel, sees everything they can see in AppFolio, and does what they ask, owners included.
Read-only. Code: origin/main 8e4f4d8227 (Nov 4 03:57 UTC). Data: propflow-prod, read-only. Nothing was sent or written. The vision: Clara knows every teammate on every channel, sees everything we can see, and does what they ask. Roles stay a thin layer for later.
TL;DR
Verdicts: solid / partial / missing. Paths are under ~/code/PropFlow/propflowai.
| Channel | Identity (who is this?) | Reach (what can she look up?) | Act (what can she do?) | Gates (what can silence her?) |
|---|---|---|---|---|
| Text to Clara's line | solid. Role at the line's company, looked up by phone (src/lib/domain/staff/inbound-resolution.ts:420-450). A fresh text gets a turn and a reply. | partial. 23 company lookups including live AppFolio reports, plus people and thread lookups (agents/clara/lib/agent/capabilities/staff-portfolio.ts). Only for desk roles: maintenance, accounting and viewer get one metrics tool (inbound-resolution.ts:136, capabilities/index.ts:393-396). | partial. Book, move, cancel, tour outcome, note, send application, new guest card, lockbox code, contact, text a prospect (capabilities/staff-leasing-desk.ts:34). All but texting reach AppFolio through the neutral PMS layer. | partial. The Supervised hold is keyed on the thread's building (process-envelope.ts:583-611). A STOP on a staff phone blocks every reply (src/lib/integrations/twilio/client.ts:1166). |
| Email to clara@ | partial. Role by email, but only if the sender passes the strict mail check, Clara is on To/Cc, and the lead router doesn't claim the mail first (agents/clara/lib/email/staff-email-turn.ts:86-100, process-inbound-email.ts:1284-1378). Any miss means silence. The lookup isn't fenced to one company (staff-email-turn.ts:171). | partial. Same as text when the mail check passes, otherwise nothing (staff-turn-options.ts:75). | partial. Same as text, same mail-check condition. | missing. The thread is filed under the company's first building by id (staff-email-turn.ts:117-128), and the send gate checks that building's modules. A "not in PropFlow" placeholder turns them all off: this blocked Deiby and Miriam today (company-live-gate.ts:369, building-module-chain.ts:410). |
| Email to the shared inbox (leasing@) | partial. Recognised only when replying to a team notice or a held message (src/lib/team-notify/inbound-email-reply.ts:212, staff-reply/door.ts:218). | closed on purpose for a fresh ask. | closed on purpose. | by design. Anything else is skipped silently as "coworker mail" (process-inbound-message.ts:1593-1636). Under Fede's Nov 3 ruling, staff mail landing in leasing@ is a symptom of an earlier wrong send, not a channel. |
| Call | partial. Role at the dialled line's company; any line that resolves to a company hands staff to the Coworker Line (src/app/api/voice/personalization/route.ts:2413). Its tools find the company only through the line's building, so a company-only line likely answers "not staff" (coworker-voice/call-binding.ts:61-65,145; inferred from code, not tested). | partial. 12 lookups (agents/clara/lib/voice-agents/coworker.ts:75-87). No vendors, no live AppFolio reports, no knowledge base. Maintenance callers don't hear amounts. Model is Sonnet 4.6 (model-paths.ts:127). | missing. Emergency page and a repair pin only; the work order is filed after the call (coworker.ts:151). No tour, card, application or note actions. | partial. No switch since Nov 2. A caller's phone on a person with no role reads as a renter (Hugo, Oct 28). |
| In-app Ask Clara | solid. Signed-in user (src/app/api/dashboard/chat/route.ts:37). | solid. The same 23 reads. | missing. Read-only. | n/a |
| Slack | missing. Only replies to held drafts (src/app/api/slack/events/route.ts:186). No mention or DM chat. | missing | missing | n/a |
run-work-order, wo-action with 20 actions, run-renewal, run-mark-guest-card-inactive, run-guest-card-contact-set, get-vendor-detail). Work orders already go through the neutral writer (src/lib/integrations/appfolio/work-order-writer.ts). Renewal and tenant writes still call AppFolio directly (src/lib/tools/appfolio.ts, src/lib/tools/renewals.ts), so they need adapter work first.pers_ab61f830). This is data, not code.agents/clara/lib/messaging/inbound-router.ts:867-906).src/lib/domain/pms/application-status.ts exists. A company-wide live report covers at most 5 buildings (src/lib/domain/dashboard/chat/tools.ts:83); Situs has 53. Guest-card, application and owner reports aren't in the report list (tools.ts:94-110).src/lib/data/types.ts:19635) or data. The AppFolio owner list is synced read-only (src/lib/integrations/appfolio/pms-adapter.ts:1165). Owners only receive a weekly report by email list. An owner who writes is a stranger.body || audioText keeps only the typed words when both exist (src/app/api/twilio/webhook/route.ts:559).model-tiers.ts:256), calls on Sonnet 4.6, not Opus.inbound-resolution.ts:434). It bites nobody today: every customer staff role is company-wide. It is the opposite of the Nov 2 rule.Evidence from real "staff wrote first" cases:
c9f809af); "what's the code itself?" after "Lockbox code is 6011"; "you'll need to send it yourself" on an application. All three tools exist today, so these were judgment or scope misses.conv_voice_7eb85355).conv_voice_0503c5c0).047f5db5).| Scenario | First thing that breaks |
|---|---|
| Leasing agent texts all day. "What did the renter ask?", "tour went great, add a note", "move Patricia to Thu 2pm", "send Stephany the application". | Works, with a desk role and a phone on file. It fails for 7 of 8 Situs staff (no phone). |
| Same agent: "don't send one to X", "follow up with Y tomorrow". | No tool. Clara can only send one text now (text_prospect). |
| PM emails "what's open on work orders at Lampliter?" | Works only if the mail check passes and Clara is on To. Naming the building saves the gate; a mail that names no building lands on the placeholder and is blocked. Sent to leasing@, it gets silence. |
| Owner: "how is leasing going at Barcelona?" | Not recognised on any channel: no owner role. |
| Voice note to Clara's line: "the tour went really well, here's what you should know". | Transcribed and run as a staff text. Breaks if typed words come with it (the audio is dropped). It's unproven in production: the database can't tell an audio message from a typed one. |
Fixes (no switch, live on merge):
route.ts:559). Proof: a captioned voice note to the Willows line, record id.New capabilities (dark behind ONE company setting, "Clara acts for staff"; Fede turns on per customer):
The text-desk actions that are live today (book, move, cancel, note, send application, guest card, text a prospect) stay live with no switch. Fede ruled them in on Oct 26; the new setting covers only the new actions.
Read-only pull of the AppFolio activity we mirror, plus staff messages and calls; counts are minimums (texts and emails mirrored only since Sept 29 / Oct 2); names anonymised to roles.
This is a read-only pass over production. Nothing was written and nothing was sent. Names are replaced with role labels, and phone numbers would show only the last 4 digits (none are needed here).
How evidence is weighted (per Fede, Nov 3):
Staff messages to Clara are thin (74 exchanges in 30 days). They appear only in section (c).
Coverage and limits
| Customer | Role | People | AppFolio texts / emails to renters (about 5 wks) | Emails between staff | Messages to Clara (30 d) | Calls |
|---|---|---|---|---|---|---|
| Situs | Owner/admin | 2 | 0 / 7 | 56 | 15 | — |
| Situs | Property manager | 1 | 96 / 46 | 32 | 5 | — |
| Situs | Leasing agent | 4 with a role, plus a shared "leasing" login and 1 helper with no role | 534 / 171 (about 141 a week) | 11 | 8 | 28 office-line calls recorded |
| Situs | Maintenance | 1, plus a shared "maintenance coordinator" login | 66 / 0 | 4 | 0 | — |
| W. Slope | Owner/admin | 2 | 6 / 1 | 22 | 9 | — |
| W. Slope | Property manager | 1 | 1 / 0 | 0 | 0 | — |
| W. Slope | Leasing agent | 3, one of whom also uses a shared "assistant PM" login | 207 / 30 (about 52 a week) | 23 | 43 | 77 calls Clara handed to staff |
| W. Slope | Maintenance | 2 | 2 / 0 | 0 | 0 | — |
What the table shows:
How much Clara already carries (before):
Rows 1 to 12 are ordered by count. The cross-cutting job below overlaps rows 2, 4, 6 and 8 to 12, so it is not added to them:
| # | Job | Who | Count, 45 days (per week) | Channel | Real example (anonymized) | What Clara needs | Basis |
|---|---|---|---|---|---|---|---|
| X | Answer what renters ask (the "answer this for me" job) | Everyone who faces renters | 362 staff messages answered a renter question from the last 3 days: prospects 127, residents 114, applicants 85, vendors 35. Overlaps the rows below | Text, email | Q: "Can't see the submit button to pay online" → "Scroll to the bottom and reduce your browser zoom." | Read: units, ledger, work-order status, knowledge base | data |
| # | Job | Who | Count, 45 days (per week) | Channel | Real example (anonymized) | What Clara needs | Basis |
|---|---|---|---|---|---|---|---|
| 1 | Open, assign and close work orders | Maintenance coordinator, PM | 744 opened by hand (S 486, WS 258), about 116 a week; 704 closed; Clara opened only 11 | AppFolio | "Move-out inspection and unit turn"; "Washer turns on but is not draining" | Write in AppFolio: create, assign to vendor or in-house crew, close. Read status | data |
| 2 | Book, move and run showings | Leasing agents, PM at Situs | 353 actions (about 70 a week): • 103 messages scheduling or moving • 72 on day-of logistics • 163 AppFolio showing notices (scheduled 43, confirm 67, confirmed 40, moved 6, cancelled 7) • 15 notes | Text, AppFolio | "No worries, I will schedule you for 3:00 PM today." / "[code] is the code for the lockbox on the stairs, message me when you're done." | Write the showing in AppFolio and the agent's calendar, send access details, day-of confirm, a reminder to the agent | data |
| 3 | Keep the guest-card list clean | Leasing agents | 285 actions: • 129 guest cards typed in by hand (S 109: TurboTenant, affordable-housing lists, walk-ins, referrals, an agent's own social posts) • 54 call attempts logged • 50 cards marked inactive with a reason (WS 43, most often "no response") • 52 follow-ups to quiet leads | AppFolio, text | "I will mark your card as inactive so you stop getting messaged, but reply any time to see a home." | Write: create a guest card, log the call, set status and reason. Run the follow-up schedule | data |
| 4 | Run the application pipeline | Leasing agents, shared leasing login | 280 actions: • 100 application-link templates • 36 helping someone apply • 112 on processing (documents, references, approvals) • 21 "application received" • 11 notes Results: 57 applications, 30 approved, 5 denied, 14 leases signed | Text, email, calls | "I haven't heard back from your references yet. If you could give them a heads-up, that would speed up your application." | Write: send the link. Reminder: chase missing documents and references. Read: screening status. The approve or deny decision stays human | data |
| 5 | Send notices to residents | Leasing agents, shared logins | 118 distinct mass sends reaching 598 threads, plus 83 one-to-one notices. Many in English and Spanish | AppFolio mass text | "Pest Control Notice: treatment Tuesday 8:00 AM to 3:00 PM. Please remove food, open containers…" | Write a mass message by property, building or unit list, in both languages | data |
| 6 | Keep residents and vendors posted on maintenance | Maintenance coordinator, leasing at WS | 119 messages: • 62 to residents (intake, status, "was it fixed?") • 57 to vendors (quotes, approvals, turn status, invoices) | Text | "Just checking in: did the vendor fix your washer already?" / "We have the go signal, this is approved. When can you do the work?" | Read work-order status, a reminder to vendors, write status updates. Invoices: no | data |
| 7 | Owner and admin back office by email | Owners, PM, shared logins | About 100 threads between staff: • 16 on application decisions • 18 on vendor bills and laundry quarters • 15 resident or legal issues • 8 marketing • 7 maintenance • 4 move-outs • 4 financials | Subjects only: "Rental Application Report for [applicant]", "Outstanding invoices, action required", "Annual property assessment" | Read and summarize, draft replies. The decision stays with the owner | data (subjects only) | |
| 8 | Write notes after a showing | Leasing agents | 62 notes (40 on how the showing went, 22 on the household: voucher, pets, move date), plus 19 answers to Clara's tour check-ins at WS | AppFolio note, text | "They liked it. She will talk to her daughter tonight. Didn't love that the washer/dryer hookups are upstairs." | Take a dictated or texted note and write it to the card | data |
| 9 | Renewals, move-outs, keys, deposits | Leasing agents, PM | 47 messages (7 calls) | Text, calls | "How would you like your $1,000 renewal credit applied? Option 1…" / "Do you have a forwarding address for your deposit?" | Read lease and ledger. Write the resident's choice. Reminder on move-out steps | data |
| 10 | Rent, ledger and payment questions | Leasing agents, PM | 35 (11 of them calls) | Text, calls | "Their ACH from 9/18 was not posted to your account; we've notified accounting." | Read the ledger. A payment plan stays human | data |
| 11 | Move-in coordination | Leasing agents, owner at WS | 28 | Text | "Move-in instructions: lockbox on the front door, 3 keys inside…" | Read move-in charges. Send keys and access, send the portal invite | data |
| 12 | Complaints and disputes | PM, leasing | 25 | Text | "We have documented your complaints about smoking from the unit next door; management will address it." | Log it and route it to the PM. The PM decides | data |
| 13 | Collections and late notices | PM | — | — | — | Read delinquency, write notices | domain |
| 14 | Renewal offers and pricing | PM, owner | — (WS gets Clara's "renewals coming up" list) | — | — | Read expirations, draft offers | domain |
| 15 | Move-out inspection and deposit settlement | PM, maintenance | — (Situs has 132 inspection-type work orders) | — | — | A reminder on the deadline. Colorado has a hard deposit deadline (knowledge-base doc security-deposits-move-out-charges) | domain + partial data |
| 16 | Make-ready (unit turn) board | Maintenance, PM | 94 turn-type work orders (S 55, WS 39) | — | — | Read turn status, chase vendors (7-stage turn in knowledge-base doc turnover-make-ready-process) | domain + partial data |
| 17 | Owner report (occupancy, leasing, work orders) | Owner | — | — | — | Weekly summary (knowledge-base doc owner-weekly-report-and-box-score-conventions) | domain |
| 18 | Housing-voucher paperwork | Leasing, PM at Situs | A handful in the data | "RFTA means Request for Tenancy Approval… from Denver Housing" (an RFTA is the form a voucher holder's housing authority needs the landlord to fill in) | Reminder and status tracking | domain + partial data |
What Clara would need, across 765 of the 853 labelled messages and calls (acknowledgements and "other" left out)
Could Clara do them? The labels said:
Clara's notices to staff:
Of the 72 replies, 24 went wrong (33%):
Things staff asked Clara for first (5, all at WS):
Follow-ups that failed:
What staff did by hand that Clara has no path for today:
Each one is built from the hand actions counted above. Numbers inside the dialogues are illustrative; the job counts they cite are real.
Design pass, Opus 5.5 at max effort, Nov 3 night, on Fede's ask: "do we need to rethink the architecture, give Opus 5.5 broader powers, fewer rules, more domain guidance and examples?" Status: Proposed, pending Fede's review. The Opus-vs-Sonnet numbers quoted here are interim; the model-swap bench row supersedes them when it lands.
Proposed, pending review. Nov 3, night. Read-only: nothing was changed.
A teammate's message today. Every step exists in several copies.
ENTRY 6 reply doors (text, email to Clara, shared inbox, notice replies,
matter replies, the staff phone line), plus video, meetings, in-app chat
|
WHO 5 different "is this staff?" checks
|
MATCH 5 different ways to find what a reply answers
|
SCOPE 3 building pickers; email takes the company's first building by id
|
PROMPT 8 prompts. Text and email: the renter prompt with a staff hat
(4,311 and 4,717 words). Calls: their own (1,790 words, older model)
|
TOOLS 5 tool lists: desk 38, maintenance 4, calls 14, video, in-app
4 different ways to find a person
|
GATES 4 gate stacks. Email alone has 7 checks that can silence a teammate
|
SEND each door sends its own way
The copies number: 8 prompts, 5 tool lists, 5 staff checks, 4 gate stacks.
Real use is still narrow. Kat sent 54 of the 55 staff texts in 30 days. Situs staff sent 5 emails and no texts. Clara's lines got 3 voice notes in 200 days.
Work orders opened, 45 days by hand ######################################## 744 by Clara # 11
| # | Gap | Evidence |
|---|---|---|
| 1 | Most of the job has no tool: work orders, card status, follow-ups, withholding an application, vendors | 744 work orders by hand, 11 by Clara. 172 of 765 staff needs are writes. The AppFolio robot already does them all |
| 2 | Every door is its own Clara | 24 of 74 staff replies went wrong since Oct 5. Situs: 4 of 5 after both Nov 2 fixes |
| 3 | The staff prompt is the renter prompt with a hat | 118 rules, 0 examples. Kat was told "you'll need to send it yourself", though the tool existed |
| 4 | Calls are a separate Clara that can't act | Own 12 lookups, older model. Jay was told "I only help prospective renters" |
| 5 | Roles are uneven; property owners don't exist | Maintenance, accounting and viewers get 1 lookup. No owner role. 7 of 8 Situs staff have no phone on file |
| 6 | Reach is capped and differs by channel | Live reports stop at 5 buildings; Situs has 53. Calls have no vendors or knowledge base |
| 7 | Building rules silence teammates | A placeholder building blocked Deiby's and Miriam's replies on Nov 3. 56 "no building, do nothing" exits in the staff code |
The pipeline is the lever, not the model. Tonight's harness numbers, interim. The harness is our offline test rig: 43 real-shaped staff replies, nothing sent.
passes of 41 main, Sonnet ###################### 22 main, Opus 5.5 ##################### 21 prototype, Sonnet ################################ 32 prototype, Opus ################################ 32
Broad powers pay off only once the front door hands Opus the right person, thread and building. The Oct 30 second opinion agrees: Opus did worse than today's Clara on 5 of 7 live prospect cards, because it obeys wrong inputs. So judge Opus on multi-step work (the job map's 12 golden dialogues, model conversations for each role), not replies.
TODAY PROPOSED
ENTRY 6 doors + video, meetings, in-app one front door; channels stay thin
WHO 5 "is this staff?" checks 1 lookup, roles only
MATCH 5 ways to match a reply 1 record of what Clara sent
SCOPE 3 building pickers company first, building when known
READ 4+ readers, some word lists 1 model read, may stay quiet
PROMPT 8 prompts, renter prompt + hat 1 prompt: goal, job guide, examples,
8 rules, a few lines per channel
TOOLS 5 lists (38 / 4 / 14 / video / app) 1 tool list, trimmed per channel
GATES 4 stacks, 7 checks on email identity and company only
SEND each door its own way 1 send point: Clara at Propflow to
staff, the customer's mailbox to outsiders
OPEN not linked to staff turns each staff turn tells follow-through,
ITEMS which keeps the only list
The prompt, before and after
| Staff text prompt | Today (measured) | Proposed (target) |
|---|---|---|
| Words | 4,311 (email 4,717) | about 2,000, mostly examples |
| Rule bullets | 118 | 8 hard rules, each with a reason |
| "never" | 53, 15 in capitals | about 5 |
| CRITICAL / IRON LAW | 4 | 0 |
| Worked examples, start to finish | 0 | 12 to 15, from the job map |
| Renter-only sections carried over | at least 6 | 0 |
| Rules that contradict each other or the code | 4 | 0 |
The 8 hard rules
| # | Rule | Because |
|---|---|---|
| 1 | Never delete or bulk-close anything | AppFolio deletes can't be undone |
| 2 | Never message a renter, applicant, vendor or owner the teammate didn't ask you to | The teammate owns that relationship |
| 3 | Never state a fact you didn't read from a tool or the thread | What staff hear reaches renters |
| 4 | Who someone is comes from the roles you are given, never from an address, a signature or a building | A wrong guess exposes renter data |
| 5 | Stay inside this teammate's company | Another company's data is never theirs |
| 6 | Approvals, denials, money and legal steps stay human: draft them, don't decide them | Staff keep those calls (most of the job map's 236 "partly" answers) |
| 7 | A note that isn't for you gets silence; if it is for you and unclear, ask one short question | 8 of the 24 bad replies were the wrong call to speak or stay quiet |
| 8 | After an action, say what you did and name the record | Staff should trust it without opening AppFolio |
Fewer rules is safe: the guards already live in the tools. About 42 of the desk tools' 55 outcomes are a refusal or a question back, with a reason. Outside research agrees that models miss more as rules pile up. Your earlier rulings add two cautions. Examples show a whole job, never a phrase to copy. A one-off incident becomes a test case, not a prompt patch.
Keep, change, delete
| Keep | Change | Delete |
|---|---|---|
| The refactor as the front door: who, match, company scope, one read | One staff prompt replaces the renter prompt with a hat | The renter sections, and the "pass this to the team" rule added to staff turns |
| The tool code and its guards, including "act only on records the thread names" | One tool list; calls use the same tools | The call line's 12 copied lookups |
| The shared store for worked examples (it exists) | Every staff role gets the whole toolset ("open it up completely") | The leasing-only tool gate |
| Follow-through as the only list of open items | Answers to staff are gated by identity and company only | The Supervised hold on staff replies; the word lists that read approvals |
| ElevenLabs running the calls | Opus on text and email, once the bench says better | The first-building-by-id pickers; the 56 no-building exits; the old video desk prompt |
1. Calls
2. New actions (work orders, card status, follow-ups, withholding an application, vendors)
3. Property owners (the investors, not the company's owner, who is already an admin; the docs use "owner" three ways)
| When | What | First real proof |
|---|---|---|
| Tonight, in flight (fixes, no switch) | The refactor chain, PR 1 first (keep the match) | The 74 real exchanges, replayed |
| Opus on the staff turn, only if the bench says better. Tonight's interim run: a tie on replies | The harness, both models, two runs each | |
| A voice note keeps both the words and the audio | A replay of real texts; then the first real captioned note after deploy | |
| One staff toolset on every channel, for every role | A maintenance teammate's lookup and tour move by email at Fairhaven; one simulated call | |
| Tonight, next (local, no CI) | The harness gains the 12 golden dialogues as "staff wrote first" cases, plus maintenance and property-owner senders. Calls are simulated, so no call minutes | Red on main first |
| Tomorrow, if you pick 2A (one dark slice: merged but off until you turn it on) | Behind "Clara acts for staff", a teammate emails "open a work order for…". Clara opens and assigns it through the work-order adapter, then replies as Clara at Propflow | Live at Fairhaven by email: the work-order id, the reply id, and the staff-turn event (the record follow-through reads). Text waits: Willows is closed, and Fairhaven's line takes no texts |
| This week, after your go | One staff prompt: goal, job guide, examples, 8 rules | The harness, the 12 golden dialogues and the 74 real exchanges, with no regression |
| More actions behind the setting; calls get them too | A record id per action at Fairhaven; one real call at the end | |
| Waits for you | The shape; the three calls above; the turn-on per customer; Situs staff phones and Hugo's record (customer data); property owners |
Acceptance. Every step runs the 12 golden dialogues, the 43 harness cases and the 74 real exchanges through scripts/coworker-harness/staff-reaction-regression/. A case passes on the record written, or on the right silence. A grader that only checks "replied or stayed quiet" passes 17 of today's 19 failures, so it doesn't count. A few golden dialogues have near matches today (notify tenants, never showed, stay quiet); most are new.
The big one: broad reach plus a false staff match. One wrong phone or email on a staff record would let a stranger read every renter's ledger and move tours across 53 buildings. Today's records are messy: Hugo's phone sits on a person with no role, and Western Slope's shared inbox is claimed by a test prospect.
Verified identity must mean all of this, on every turn:
These are your two Oct 9 hard rules made concrete: non-staff never reach it, and nothing crosses customers.
Other risks
| Risk | What we do |
|---|---|
| Opus obeys bad inputs | Broad powers land only after the front door |
| Opus is slower: a median 16 seconds per case tonight vs 13 | Fine for text and email; calls stay on a fast model |
| A shorter prompt can regress | Shrink only with the full replay and a planted defect that must fail |
| Two lists of open items | Follow-through keeps the only one; the staff agent reads it and sends it the staff-turn event |
| Gera's lane tonight: three open PRs make a building's Supervised mode hold or stop Clara's own texts to staff (his late-October rule, "Supervised holds every team message") | Your Nov 3 "no supervised" covers replies. For her own notices, the hub records your Oct 16 "no" and still lists it as your yes/hold. This design assumes answers to teammates never wait on a building's mode. One line between you and Gera settles the notices |
Read-only audit of the staff side: staff asking Clara, Clara asking staff, and team notices. Code read on main 9e502ce48b; usage read from production, Oct 2 to Nov 1. The refactor's state is in the Oct 31 audit.
proven a real customer record · shipped, untested live code, no real use yet · missing · n/a
| Capability | Text | Voice | |
|---|---|---|---|
| Staff ask Clara | |||
| Book or move a tour | provenWS: 3 of 5 worked; tour 4d116c70 | missingthe staff desk is granted on text only | missingthe staff phone line only reads, by design |
| Cancel a tour | shipped#20886; 0 real uses | missing | missing |
| Tour notes | provenWS: 24 of 26 saved; matter_tourcheckin_af61d484 | missing | missing |
| Look up records | provenWS: 1 of 1 | missinga staff email that matches no open ask never reaches Clara (gap 1) | proven, WS only1 of 2 worked; conv_voice_5586a662; off at Situs and Camellia |
| Send application, text a prospect | shippedswitch removed Oct 26 (#22532); the one real try (Oct 24) hit the old switch | missing | missing |
| Answer Clara's question | provenWS lockbox asks 2 of 2 | provenSitus matter_41896f45; WS 7 replies | n/a |
| Clara tells or asks staff | |||
| Ask the team a question | provenWS lockbox asks | provenWS 30 sent, 7 answered by reply | n/a |
| "How did the tour go?" | provenWS 23 of 27; Camellia 0 of 4; Situs since today (#24225), untested | missingFede Nov 1: email by default; no PR yet | n/a |
| Tour booked, moved, cancelled | proven, opt-inWS 36 notices to 2 people; Situs 0 people opted in | inbox onlytour host not emailed; Fede Nov 1, no PR yet | n/a |
| Arrived, running late | proven, opt-inWS 6 | shippedto the tour host since today (#24230); proven live at Fairhaven Nov 1 (tn_770e05d7) | n/a |
| Office note after a call | n/a | provenWS 44, Situs 13, Camellia 10 delivered; 8 refused by the wording check | source |
| Welcome text to a new agent | Willows only#22669; off at all three, Live mode only | n/a | n/a |
| Hand a matter to a named person | missing on every channelhand-off framework not started (Oct 31 audit, row 13) | ||
| Staff to Clara (text / email / voice) | Their requests that worked | Clara's asks answered | Median answer | |
|---|---|---|---|---|
| Western Slope | 55 / 17 / 2 | 31 of 38 | tour check-in 23 of 27; questions 7 of 30 by reply, 7 more closed in AppFolio, 14 open | 6.6 min (check-in) · 24 min (email) |
| Situs | 0 / 5 / 0 | 1 of 5 | 1 of 1 | 3 min |
| Camellia | 0 / 0 / 0 | none asked | 0 of 13 (4 check-ins, 9 questions) | none |
Kat sends 54 of the 55 texts. No customer staff member decided any held message; Fede, Gera or a session decided them all. PropFlow's own traffic is excluded (Fede: 39 calls at Western Slope).
Situs marks the gaps that matter for Situs on Monday.
| # | Gap | Evidence | Next step |
|---|---|---|---|
| 1 | Situs A staff email that matches no open ask falls through both inboxes silently | Miriam's reply to a tour confirmation, Oct 29 (3ba9bea3); Eileen's reply to an office note, Oct 20 (e3bfdc15, before office notes carried a reply tag). The mailbox copy was skipped because "Clara's inbox handles it", and Clara's inbox skipped it as data only. Nobody was told | One fix: route every staff email to Clara through the one staff reader; never skip it silently |
| 2 | Situs Staff cannot book, move, cancel, note or look things up by email | Desk granted only on text (staff-turn-options.ts). The Sept 25 reason was spoofable senders; the email sender check now exists (#12334) | One PR: open the desk to email when that check passes; prove it at Willows from the test mailbox |
| 3 | Situs Tour host not emailed on booked, moved, cancelled; the "how did it go" ask exists only as a text | Situs has 0 text opt-ins; Fede's Nov 1 ruling; no PR found | Build PR 1 (host email) and PR 2 (notes ask by email), as already decided |
| 4 | Clara's questions go unanswered, and Clara never checks whether a person already replied | WS: 14 of 30 still open; 7 closed only because staff wrote the renter in AppFolio (about 26 h). Camellia: 0 of 13 answered | Build the wake, look, act slice (Oct 16): read the card for a staff reply before re-asking |
| 5 | Situs Two of today's changes have no live proof | #24225 check-in at Situs, #23188 text answer (#24230 host email proven at Fairhaven Nov 1) | One Willows run each, with record ids |
| 6 | Text desk misfires on harder requests | WS: 6 of 36 failed (note on the wrong card, wrong time, tour not found, card refused); 4 hand-sent corrections. Most predate the Oct 17 to 19 desk rewrite | Replay those 6 against today's desk; fix only what still fails |
| 7 | Supervised holds staff notices, which Fede ruled out on Oct 16 | Rule from Gera, Oct 29 (team-notify release.ts). Situs leasing is Live, so Situs is not affected | Fede, yes or hold: do staff notices skip the Supervised hold? |
| 8 | Staff phone line: lookups only, on at Western Slope only | 2 real staff calls in 30 days | Leave it; too little use to invest in now |
Not verified: what Clara sent staff by email before about Oct 28 (bodies were not stored), so email notice counts are partial; who in Western Slope's shared inbox wrote each email; staff texting from numbers not on file; whether Lee's Oct 31 dog-limit reply ("duplicate") reached the renter; the content of the two dropped Situs emails, because the SES path keeps metadata only.
Answer. In 30 days, one thing a teammate told Clara was a lasting habit she should have remembered. So build the smallest store there is: a few short "how I work" lines per teammate, read only when that teammate texts or emails Clara. Grow it only if use shows up.
Kat (K.B., Western Slope) sent 54 texts. 46 were the task at hand (tour notes, bookings, "ok"). 8 said something more. Situs adds nothing: its 5 staff emails were either answers to Clara's questions or were dropped with no body kept (gap 1 above).
| Date | What K.B. said | Where it belongs |
|---|---|---|
| Oct 2 | "I will show A.N. after B. It's the same location" (Clara had refused 3:45 over the 30-min gap, booked 4:00, then corrected) | Memory: she stacks tours at one address |
| Oct 2 | "Duplicate" (second notes ask for one visit, two homes) | Engine: one notes ask per visit |
| Oct 14 | "You said you already did" | Engine: same |
| Oct 7 | "I made the guest card already" | Engine: look up the card again |
| Oct 16 | "If you scroll up in this thread you can see you just asked me about her?" | Engine: lost thread context |
| Oct 24 | "Good, please send an application" → "you'll need to send it yourself" | Missing capability |
| Oct 15 | "Lockbox code is 6011 for 585-201" | Already handled: the lockbox ask |
| Oct 14 | "L.J. is a current tenant switching properties" | Prospect card note |
Count: 1 truly needs memory, 1 already handled by a setting or flow, 1 is a card note, 4 are engine bugs, 1 is a missing capability. K.B.'s text-me choice is already a setting (tour alerts by text since Oct 1). The 4 engine bugs are follow-ups, not memory.
Five per-person settings, none free-form, none read when a teammate writes to Clara.
| Setting | Stores | Code | Set by |
|---|---|---|---|
| Text or email | text / email per person; only the welcome text reads it | src/lib/domain/staff/contact-preference.ts | When the person is added |
| Welcome text | One intro text per person | src/lib/domain/staff/staff-welcome-text.ts | Company switch, off |
| Tour alerts | Per event: email or text | src/lib/domain/portfolio/scope/staff-tour-alert-subscription.ts | The person, in settings |
| Who shows tours | Ordered list + gap minutes (company) | src/lib/domain/calendar/tour-showers-join.ts | Settings |
| Working hours | None per person; their calendar's free/busy | src/lib/domain/calendar/check-availability.ts | Calendar connect |
| Language, timezone | Built for renters | PersonPreferences in src/lib/data/types.ts | Inferred |
| Answers saved from staff replies | Property knowledge, live at once | src/lib/domain/escalation/teaching-capture.ts | Staff reply |
| Store | One row per teammate per company, beside the person: PERSON#<id> / MEMORY#<orgId>. Same pattern as the person-facts row (person-facts-item.ts): one write helper, carried on person merge. At most 10 short lines, each with the words, the date said and the message it came from. Not Person.preferences: that is shaped for renters. |
|---|---|
| Capture | Clara notices when a teammate states a lasting habit about themselves during normal work and saves it with one tool call. No "should I remember this?" quiz (Fede, Oct 24). She confirms in one clause only when it changes what she does: "Booked 3:45, and I'll stack tours at the same address for you from now on." |
| Use | Slice 1: added to Clara's staff-desk instructions when that teammate writes in (composeStaffDeskSystemPrompt() in agents/clara/lib/agent/staff-desk-persona.ts). Notices stay templated for now. |
| See / change | "What do you remember about me?" lists it. "Forget that" or "stop doing X" removes it. No settings page, no approve button (autonomy rule). |
| Expiry | A line unused for 90 days drops. Over 10 lines, the oldest drops. |
| Not memory | Property fact → knowledge base (teaching capture, incl. the "remember next time" email case). Company rule → company settings. Prospect fact → their card. Text vs email → the existing tour-alert setting. |
| Remembers | Cost | Proves | |
|---|---|---|---|
| A. "How I work" lines (recommended) | Habits a teammate states, e.g. stacking tours at one address | 1 PR: row, one tool, one prompt block | Willows test-line texts: teammate states a habit, a later booking follows it with no re-ask; record ids |
| B. "Text me / email me" | Nothing new: saying it flips the existing tour-alert setting | Small, no new store | Capture wiring only; 0 real cases in 30 days |
| C. "Homes I cover" | Which homes a teammate shows | Overlaps the tour host field in the tour-move design | Better solved there |
| Oct 2, today | With slice A |
|---|---|
| K.B. asks for 3:45 after B. Clara refuses (30-min gap), books 4:00, sends A.N. a 4:00 confirmation, then a correction to 3:45. 7 texts on the staff thread, 1 wrong confirmation to the prospect. | Next time K.B. asks for a back-to-back slot at the same address, Clara books it first try. 1 text, no correction. |
Related: staff tour moves and reassignment · client onboarding playbook (welcome text and team setup). Evidence: Kat's staff thread c9f809af and 4d116c70, Oct 2 to Nov 1.
Answer. The shared box is a listening post, not a desk: of 5,238 messages this year, 13 left it, all Clara's. Staff answer from their own addresses and hand work around by reply-all (842 of 1,055 hand-offs, 80%). The VA seats do 42% of all human work on guest cards, first human on 56% of them; that work is 72% texting and 8% booking. Since Oct 20 Clara has been a bystander: in 47 prospect text conversations, staff or the VA answered first in 36 (median 9 min), Clara in 1, nobody in 10; Clara wrote 18 of 346 typed prospect messages (5%). Nothing in the mailbox stops Clara from doing the VA's prospect work; nobody has said who goes first.
| Jan | Feb | Mar | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov 1 | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| Received | 216 | 240 | 385 | 308 | 449 | 474 | 840 | 730 | 865 | 712 | 6 |
| Sent from the box | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 12 (Clara) | 1 (Clara) |
| Who writes to the box (3,958 messages read) | Messages | Share |
|---|---|---|
| Situs staff (president 328, PM 180, VP ops 136, leasing 191, others) | 1,294 | 33% |
| Listing sites and website forms (Zillow, TurboTenant, Tenant Turner, Contact Us) | 1,122 | 28% |
| System notices (AppFolio, vendor portals, forms) | 467 | 12% |
| Residents | 338 | 9% |
| Applicants, guarantors, housing connectors, unknown people | 326 | 8% |
| VA seats (C.T. 24, PM Assistant 50, Assistant Lead 30, plus forwards) | 131 | 3% |
| Vendors | 125 | 3% |
| Prospects writing directly | 120 | 3% |
| Clara and PropFlow | 35 | 1% |
Internal reply speed by email: 546 replies, median 49 min, 52% within the hour, 1 in 10 over 22 hours. Staff answering a VA's team email: median 32 min (58 cases). VA answering a staff ask: median 69 min (101 cases).
Three VA seats: C.T. (listings, cards, applications), A.L. (texting, calls, insurance) and P.A. (vendors, turns). Prospects are about 30% of their message volume.
| VA work (share of 3,892 VA guest-card actions) | Clara can do it today | Clara did at Situs, Oct 20 to Nov 1 |
|---|---|---|
| Text prospects, 71.6% | Yes (AppFolio texting seat, live Oct 29) | 18 of 346 typed prospect messages; first to answer in 1 of 47 conversations |
| Book and move showings, 8.3% (VA booked 288: 164 C.T., 124 A.L., onto D.M. 142, L.M. 53, E.O. 32, M.G. 25) | Yes for her own bookings; moves and cancels only when asked by text | not counted this pass |
| Card edits and notes, 8.4% | Notes yes | not counted this pass |
| Phone calls to prospects, 4.9% | Inbound only | not in this study |
| Email prospects, 3.0% | Yes | 12 first-touch emails (Oct 28 to Nov 1) |
| Move-in and application steps, 3.9% | No (application link only) | 0 |
| Resident texting and email (8,845 resident texts Jan to Sep, 338 resident emails in the box) | No (tenant hand-off, Gera's lane) | 0 |
| Team coordination by email: status answers, loop-closing, taking a delegated task | No by email (#24367); yes by text. Nov 2: merged and live on deploy: a verified staff email to clara@ gets the desk (lookup, book, move, cancel, note) and a reply (#24426); staff who answer Clara's question by email get a "Got it" back (#24428). After-tour ask by email for hosts with no phone (#24435, merged 06:45Z). Willows end-to-end proof of all three owed Monday. | 0 emails to staff |
| Maintenance, vendor and turn mail (28 "GREEN, unit ready" mails, work orders) | No | 0 |
Coverage. Clara can do about 80% of the VA's prospect-side actions and about 25% of all VA work. In the first 13 days she did about 5% of the prospect texting.
| # | Gap | Evidence | Clara today | Clara should | Size |
|---|---|---|---|---|---|
| 1 | Who goes first. Clara and the VA desk watch the same cards; the VA wins the race. | VA or staff first in 36 of 47 text conversations, median 9 min; C.T. typed 101 prospect texts to Clara's 18. Aug 4: VA "I have spoken with him already", L.M. 15 min later "I believe D.M. did as well." | Answers when she gets there; no visible claim on the card | One explicit first-responder rule per customer: Clara first for N minutes, a human reply takes the card over, the claim visible in AppFolio | Decision, then 1 PR |
| 2 | The team-time ask (Fede's example): a prospect wants a time nobody has open. | 3 explicit asks all year; Oct 30 took 8 emails, 4 people and 3.5 h with a president nudge. The usual practice is booking straight onto an agent's calendar (288 times), which produced day-of cancels, an agent no-show and a prospect who could not find anyone (4 corpus cases). | Says the time is not open and offers the two nearest slots (verified on the Oct 30 thread) | Ask the agents with a deadline, tell the prospect "checking", book on the first yes and name who meets them, otherwise fall back to the offered slots | Feature, 2 to 4 PRs, on top of the staff move/reassign design |
| 3 | "I'm here" texts from the curb. | 68 of 884 cards hold one (Jan to Sep 1), 8 more in October: "I was here at 350pm and no one is still here?????"; "I'm here. Where would the leasing office be?"; "I am here but not sure where to go" | Not verified this pass | Answer with the meeting spot and the host's ETA, text the host, note the card | 1 PR after verification |
| 4 | Confirm-or-release. Situs skips showings the prospect did not confirm. | D.M., Jul 28: "we do not reside at the property and we need to plan for travel"; CONFIRM/DENY texts one hour before; 66 staff cancels and 53 prospect cancels in 884 cards; a DENY the day after a VA booking (Jun 24) | Reminders exist; whether an unconfirmed Situs showing is released is not verified this pass | Mirror the house rule: ask the day before, cut off one hour out, release the slot, tell the host, re-offer times | Fix or 1 PR after verification |
| 5 | The email shapes #24367 has to handle (already filed; counts added here) | Inbox-wide: status pings 8 ("did we get back to this applicant?", "have you followed up?"), delegations 27 ("can you call the new tenants?", "put this on the issues list"), loop-closers 14 ("I have already contacted the prospect"), decision asks by @mention 4, one never answered. The corpus holds 6/7/5/4 of them. | Dropped unless it answers a question Clara asked | Answer from the record on the same thread, reply-all; take leasing-shaped delegations, decline the rest in one line; close her own loops | #24367 plus the reply-all relay fix |
| · | Residents and maintenance are most of the VA's day. | 8,845 resident texts vs about 3,900 prospect-side (Jan to Sep); in the box the VA's email tasks are maintenance 68 and residents 56 against showings 38 | 0% | Nothing new for Situs this month; the counts go on the tenant hand-off list | Design first (Gera's lane) |
| · | Website Contact Us forms | 53 this year, 18 from prospects, 29 residents or other, 6 vendor pitches | Ignored (no card is minted from listing mail) | Decision: treat the prospect-shaped ones as leads, or leave them to the VA | Decision |
| · | Cover requests ("can you take my showing?") | 0 asks in this mailbox; they travel by phone or personal text. AppFolio shows the footprint: 39 showings handed from one agent to another this year (35 cards), 14 of them for a slot the same or next day; plus an agent with car trouble (prospect rebooked next day) and a 3-property tour moved and reassigned within 68 min | n/a | Harness scenarios come from the reassignment events, not from mail | Covered by the move/reassign design |
harness_set as leasing-shaped replay cases (3 team-time asks including Fede's Oct 30 example, 2 moves, 0 cover requests, the rest status pings, delegations, loop-closers and decision asks). Names are masked to initials in the corpus; the verbatim threads sit beside it, keyed by thread_id. Session scratchpad only (situs-inbox-fable/), never on a page.Answer. The VA does two jobs: the AppFolio front desk (on the guest cards the VA seats booked 57% of showings, made 84% of logged calls, started 55% of move-ins) and an email back office the team hands out by name ("C.T., pre market it now"). Clara does about a third of the VA's typed work (30% fully, 11% partly). The first thing the team will hit is a closed door, not a missing skill: they work by copying leasing@, and Clara skips teammate email copied there unless it answers her.
| Habit (1,340 team and VA emails in the box) | Number |
|---|---|
| leasing@ is only a copy (CC), not the addressee | 73% |
| Two or more colleagues on the email | 73% |
| A task handed to a person or "Team" by name (a floor) | 45 (E.O. 17, H.W. 11) |
| Team answers a VA question | median 40 min, 47% over an hour |
| VA answers a team ask | median 30 min |
Totals match the Fable section (5,238 emails this year).
Clara does it partly not at all unclassified
1,935 typed VA work units (texts, emails, logged calls), Jan to Nov 1, not counting 454 AppFolio template sends; with those, 37% fully and 9% partly. The top five shapes make 80%.
| # | Gap | Evidence | Clara today | Clara should | Size |
|---|---|---|---|---|---|
| 1 | Team mail copied to leasing@ never reaches Clara | Clara's log, Oct 25 to Nov 1: 72 teammate emails skipped as staff-to-staff, e.g. H.W.: "Team can we please notice these units for Wednesday afternoon". This year 234 team emails to renters copied leasing@ (23 since Oct 15, mostly residents); none reaches the renter's thread. | Skipped unless sent to Clara. Not the reply-all bug (replies to her notices). | Log a teammate's reply in the renter's thread; treat asks to "Team", a VA or Clara as hers (#24367 decides what she does); otherwise stay quiet | 1 PR to log; design first for asks |
| 2 | A renter names a time Clara doesn't have | Oct 30, D.S. asks for 2:15. Clara: "2:15 PM … isn't open, but I do have 1:45 PM or 1:30 PM." VA C.T. asks the team; the president chases 3 h 18 min later; C.T. books 3:30 by phone. Clara saw the booking land; she couldn't ask. Guest cards, Jan to Aug: a third of renter texts naming a time ask for one nobody offered (31 of 91 sampled); 16 of the 31 got their time. | Offers open slots only | Ask the showing team with a deadline, tell the renter she is checking, book on a yes, else offer slots | Feature, 2–3 PRs (reuses the ask-the-team path and chaser) |
| 3 | Clara's notes have no owner | The VA names a person in 95 of 105 internal emails; all 20 Clara notes since Oct 19 went to leasing@ alone ("Can someone review…?"). E.O. on a callback note: "Did anyone call her?" | Shared inbox, no addressee (tour notices to the showing agent are being built) | Address each note to its owner by role, copy leasing@ | 1 PR |
| 4 | Back-office tasks have no Clara tool | Resident notices (16%). Listing edits ("VAs - for now can we turn off the leasing for all commercial units", done in 6 min). Outreach ("C.T. can you please contact the Alton team"). Notes and flags. | Card notes only; #24367 opens the door, not these actions | Notes and listing availability first; decline the rest in one line | Design first; new capabilities, default off |
| 5 | Applications to move-in follow-through | 15% of typed VA work plus 140 template status sends. E.O.: "L., Have you followed up?" | Sends the application link | Chase each step (documents, deposit, lease, insurance, keys); flag stalls to the team | Design first; new capability, default off |
Checks, unverified: Oct 30, Clara's slots ended at 1:45 while D.M. had 3:00 free (check Situs rotation and hours); 42 showings staff booked in AppFolio since Oct 20 (do they reach the calendars Clara reads?). Agrees with Fable gap 1: Clara wrote in 10 of 150 staff-touched renter conversations since Oct 20.
situs-inbox-opus/va-team-exchanges.jsonl.C.T., L. and B. are the three VA seats (C.T., Assistant Lead, PM Assistant). Staff: H.W. president, E.O. VP operations, M.G. property manager, D.M. assistant property manager, L.M. leasing.
| Sender | Jan | Feb | Mar | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov 1 | Total |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Listing sites | 36 | 53 | 36 | 94 | 197 | 153 | 291 | 261 | 278 | 207 | 2 | 1,608 |
| Situs team and VAs | 30 | 54 | 73 | 62 | 89 | 109 | 291 | 240 | 256 | 271 | 3 | 1,478 |
| AppFolio | 9 | 9 | 78 | 24 | 39 | 56 | 67 | 49 | 78 | 48 | 1 | 458 |
| Personal addresses (renters, others) | 9 | 9 | 18 | 17 | 13 | 31 | 44 | 41 | 53 | 33 | 0 | 268 |
| Clara | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 21 | 1 | 22 |
| Other (vendors, utilities, banks, newsletters) | 132 | 115 | 180 | 111 | 111 | 125 | 147 | 139 | 200 | 144 | 0 | 1,404 |
| All | 216 | 240 | 385 | 308 | 449 | 474 | 840 | 730 | 865 | 724 | 7 | 5,238 |
Gmail header counts by sender domain, sent and received, spam and trash excluded.
Answer. Three of the VA's habits now work in code: asking the team to cover a time nobody has open, finding cover when a host is out, and knowing when to speak, act or stay quiet on a group email. The first two run on Clara's real booking and staff-reply code; the harness still drives the timer. The third is a decision rule, not wired in yet. Today Clara does not do the first two, and gets the third right in 6 of 14 cases.
| Pattern | Cases | Right today → with the POC | What changes for the team | Proposed PRs (source lines, tests extra) |
|---|---|---|---|---|
| Ask the team for a time nobody has open | 10 + 5 controls real: 4 of 12 tagged | 0 of 10 → 9 of 10 controls 5 of 5 → 5 of 5 | Clara offers the open times and asks each host directly if anyone can take the asked time, with one reminder halfway to hosts who have not answered. The first yes gets the tour on her calendar; Rosa gets one confirmation; a later yes gets one line back. | 3: the ask (~500); booking on a yes (~320); wiring into the booking flow plus the timer for the reminder and the close (~200) |
| Cover for me | 3 real: 2 | 1 of 3 → 3 of 3 | A host who is out tells Clara. Clara asks the other hosts first and moves the tour to the first yes; Rosa keeps her time and hears nothing. Only if nobody can does Clara ask Rosa for a new time. Today Clara moves the time straight away. | 1, on top of the first (~80) |
| Each host on their own channel | 1 | 0 of 1 → 1 of 1 | The ask goes by email by default and by text only to hosts who opted in. Situs has no one on text. | Folded into the first PR (~10) |
| Group threads: speak, act or stay quiet | 14 real: 13 leasing, 138 all topics | 6 of 14 → 14 of 14 decision only, not wired | Clara answers what she is asked, does what she is told (one line back to the sender), stays quiet when the team talks among themselves, and pulls her draft when a teammate already answered. She never repeats a teammate's words back. Also the Opus study's top gap: a teammate's email to a renter with leasing@ copied is skipped today (72 in 8 days). | 3: store who is on each thread (~100); teach the reader "instruction" and "question for Clara" (~80); wire the rule into the reply doors (~250) |
| "I'm here" from the curb; confirm or cancel | 23 real: 6 Situs cases behind 9 | 9 of 23 → 22 of 23 1 known gap; 11 planted defects caught | The one-hour reminder asks the renter to confirm. Silent at 30 minutes: the host gets one email and the tour stays booked. A confirmation sends the host nothing; a late yes sends "confirmed after all". At the curb Clara names the host she told and never promises anyone is ready. Design + POC | 3: setting and ask (~150); heads-up, fold and "I'm here" words (~300); weekend heads-up by email and an emailed yes counts, both fixes (~150) |
fede/poc-va-ways-of-working, pushed, no PR (head d9a6a6e2e0; 58 commits behind main at publish, not rebased; it carries the staff tour-move POC it was built on). Base for the red runs: ded901930b.npx vitest run --config scripts/coworker-harness/va-ways-of-working/vitest.config.ts scripts/coworker-harness/va-ways-of-working/__tests__/cover-ask.harness.ts__tests__/group-thread.harness.ts. Plants: VA_POC_PLANT=ask_when_free | ignore_tour_then | silent_second_yes | drop_host_said_yes | nudge_everyone | relay_on_talk | ignore_cc.scripts/coworker-harness/va-ways-of-working/results/ (a run file per label, before and after notes, attestation.json, the 32-case coverage map).Built from the code on main as of Oct 25. Read it left to right: who writes in, the entry points, the lanes before the model, what a turn gathers, the prompt layers, the model, the guards after it, the output gates, and what gets saved. It is wide on purpose; a big screen helps. Every box carries its plain name and, in grey, the code file or function that proves it.
The full list of facts, one line per box with its file, and the ten items marked unverified, are in the tracker's working notes.
| Concept | Own copies today | Target | Shared core | Leasing | Maintenance | Renewals | Tenant comms | Invented by the module |
|---|---|---|---|---|---|---|---|---|
| Hold decision for a reply | 0 | 0 · shared | shared | shared | shared | shared | shared | urgent-repair carve-out, handyman mode, deferred renewal actions |
| Review door for automated sends | 3 | 0 · shared | shared | own copy | own copy | own copy | none | one review door per module |
| Held draft store | 0 | 0 · shared | shared | shared | shared | shared | shared | renewal actions wait on the hold |
| Ask for help (hand-off) | 2 | 0 · shared | shared | own copy | shared | own copy | shared | tour update email, renewal manager email |
| Topic classification | 4 | 0 · shared | shared | own copy | own copy | own copy | own copy | tour intent, repair triage, renewal decisions, lease questions |
| Knowledge | 0 | 0 · shared | shared | shared | shared | shared | shared | appliance manuals, listing search |
| Person lookup | 3 | 0 · shared | shared | own copy | own copy | own copy | shared | spoken address matching, renewal name match |
| PMS write | 3 | 0 · one adapter | shared | own copy | own copy | own copy | none | guest-card outbox, work orders, renewal offers |
| Reminders and follow-ups | 4 | 0 · shared | shared | own copy | own copy | own copy | own copy | outreach cadence, repair check-ins, renewal ladder, morning digest |
| Quiet hours | 2 | 0 · shared | none | none | own copy | own copy | none | two copies, one mirrors the other; kept as door parameters: the handyman's own window, each lane's wait walk, renewals refusing a building with no time zone |
| Tone and style | 0 | 0 · shared | shared | shared | shared | shared | shared | module rules layered on the shared voice |
| Prompt examples | 4 | 4 · one format | none | own copy | own copy | own copy | own copy | no shared examples file |
| Tool set | 4 | shared + a few per module | shared | own copy | own copy | own copy | own copy | each module brings its own tools |
| Intake question flow | 2 | 0 · shared | none | own copy | own copy | none | none | lead intake, repair intake |
| Scheduling | 3 | 0 · shared | shared | own copy | shared | own copy | own copy | tours, repair visits, key pickup |
| Staff notice sender | 3 | 0 · shared | shared | own copy | own copy | own copy | shared | two 'one doors' in core plus three module senders |
| Module-off refusal | 3 | 0 · shared | shared | own copy | own copy | own copy | none | same refusal written three times |
| Module entitlement | 3 | 0 · shared | shared | own copy | own copy | own copy | none | same check written three times |
| Offer saga and packet | 1 | 1 · keeps its own | none | none | none | own copy | none | Renewals: offer saga, auto-start scanner |
| Tour pipeline and guest card | 1 | 1 · keeps its own | none | own copy | none | none | none | Leasing: tours, guest-card write-back, application link |
| Work order triage and vendor dispatch | 1 | 1 · keeps its own | none | none | own copy | none | none | Maintenance: triage, vendor chase, on-call chain |
| Lease answers and key pickup | 1 | 1 · keeps its own | none | none | none | none | own copy | Tenant comms: read-only resident answers |
About 47 own copies today, 4 of them legitimate. Every other row collapses by the same move as the hold seam: one door, byte-identical on the frozen baseline's 5,515 cells, then delete the copies. One small PR per row, ordered by what breaks most today: first the hold (one door at all 8 callers, PR #22450 merged Oct 25 night; the six backstops stay as call points of that one function, each guards a path the earlier check never sees; PR #22473 folds the first-email check in and adds the first no-duplicates check). Then ask for help and staff notices together (the askTheTeam seam, being built now), then topic classification, then the rest. The 47 to 4 count is implementations, not call points. The tracker carries one row per concept so the own-copies count is visible falling.
Snapshot Oct 27 2026 ~03:00Z; live state stays on the phase tracker.
| Bucket | Items | PRs |
|---|---|---|
| Done: live and proven end to end with record ids | 10 | 47 |
| Merged, not proven: needs a live run | 12 | 48 |
| Merged, not proven: no-change refactors (the frozen replay is their full proof) | 5 | 11 |
| In progress | 13 | 8 |
| Not started | 10 | 0 |
| Decided by Fede | 18 | — |
| Open for Fede | 7 | — |
| What it does | PRs | Proof |
|---|---|---|
| Tenants hear the work order number from the PMS, never our internal id | #22174 | Willows, Oct 24: confirmation and follow-up texts delivered (SM1842cb13, SMfe3c9d3f): "Your work order number is 3319." |
| Clara's intro texts go out from the building line | #22373 | Willows: three texts delivered to Fede's phone (SM6bfa46f5), conversation fcd0c46b Tracker: merged, unproven |
| A listing special is said beside the rent every time the home is named, and never copied into the knowledge base | #22524, #22527 | Willows, Oct 26: draft says "$1,595 a month, and if you move in by November 30 you'll get your first month free"; inquiry 5af0eeb3, draft eschold_cb020a76. Drafts mode, nothing sent Tracker: proven |
| Checks only block; a held first email gets one writer retry before it is held | #22249, #22500 | Willows, Oct 26 3:49 PM MT: first draft failed a check, the retry passed, email delivered; inquiry 2299df14, conversation 60ee2d3f Tracker: proven · merged, unproven |
| A reply Clara sent late no longer shows a false "draft held" card on the thread | #22208 | Production screenshots of the real Western Slope thread, before and after. A truly held draft was not checked live (none existed) |
| Rating buttons on the Slack shadow cards save to our grading records | #22411; bench #26; relay #1472, #1492 | 9 real presses Oct 25 to 26 (8 Fede, 1 Gera), each tied to a message id, on the bench's human-labels page Tracker: merged, unproven · part landed |
| The bench can replay a real recorded turn read-only, and never posts to Slack while it does | #22151, #22152, #22173, #22179, #22201, #22238, #22351, #22386, #22324, #22327, #22329 | Used on production Oct 24 to 25: a recorded Willows turn (tsnap_aa2fa121), 20 repair episodes run through it; the rerun after the 97-card Slack incident posted 0 cards (session notes) |
| Clara Bench app, runner and the frozen baseline | bench #1 to #21 (20 PRs) | Baseline row adopted Oct 25: on tenant texts the office already answered, today's Clara would reply on 82 of 96 (85% noise) Tracker: proven (evidence) |
| Replay harness: every hold decision on the real book frozen and diffed after each change | #22445 | 5,515 decisions frozen from production read-only Oct 26; used as the merge proof on 9 later PRs Tracker: part landed |
| Opus 5.5 shadow cards on Slack for Western Slope and Situs tenant texts, nothing sent | bench #22, #23, #24, #25 | Real cards posting and being rated (row above); review set 3 built from 46 of them Tracker: part landed · part landed |
| Office-opened thread gets no Clara turn (#22509) and one topic classifier (#22519) | #22509, #22519 | Willows live run Oct 26 night, thread 8ffa78e8 Tracker: part landed · merged, unproven |
| What it does | PRs | What was checked, and what is missing |
|---|---|---|
| First email writing: opens like an agent, rent and size line, tour times kept, no dashes anywhere, asked size at another home, a lease phrase is not a tour promise | #22061, #21992, #22175, #22178, #22185, #22236, #22292, #22317, #22485 | Replays of 23 to 95 real cards. No Willows first email on the deployed code for these paths Tracker: part landed |
| First email: a draft that is a note about the renter is held; a held draft goes to the review card | #22314, #22336, #22542 | Willows run proved only "no regression" (the check did not fire); the review card ran at Willows from a laptop before merge, not on the deployed code Tracker: merged, unproven |
| Texts, follow-ups and call notes: checks only block, one retry, then hold | #22296, #22306, #22321 | Replays: 117 text and email turns, 128 follow-ups, 310 calls. No live run |
| Tours and tour times (offered hours, drive gaps, staff blocks, cancel by email, "Saturday at 3", first open day) | #21970, #21991, #22002, #22003, #22018, #22024, #22036, #22233, #22241 | Replays byte-identical. Willows: rescheduling passed, but the same-day case the fix was for was not run; the first-open-day run read the right times, yet the email sent was cut short by another check |
| When Clara stays out: shared numbers, unknown senders, staff notes, held replies, runaway stop, channel rules, no vacancy counts on calls | #21861, #22058, #22218, #22515, #22077, #22080 | Replays of real threads. Every PR names a Willows run after deploy; none is recorded Tracker: part landed |
| Tenant repairs: photos reach the work order; a different problem gets its own order; one professional voice | #22146, #22383, #22380 | Replays (17 real photos, 51 notes) and a voice eval. First proof is the next real tenant photo Tracker: proven · proven |
| Guest card notes written like a leasing agent | #21985, #21986, #21997 | Replay of 24 real cards. The Willows run was promised; no record found (unknown) |
| Staff desk: Clara checks what was already done before acting; the two desk switches removed | #22505, #22532, #22533 | Replay of the Oct 24 card. Live desk text tracked in issue #22514 |
| Alerts: our failures go to #alerts only, never the customer's team; a held first touch counts as the reply; held card on the new card design | #22417, #22482, #22486 | Unit tests and one record replay. The new held card was never posted (the Slack guard refused the sample) Tracker: proven · merged, unproven |
| Lead intake: no hold on a renter whose card already exists, new-lead card for email leads, held texts keep retrying | #21930, #22239, #22240 | Replays of the last 7 days. Willows runs pending |
| Staff welcome: one text per role | #22454 | Copy only; nothing sends it yet (what triggers it is Fede's call) Tracker: part landed |
| A "Why?" box after a rating press | #22526; relay #1529 | Merged in the last hour; proof is the first real press Tracker: part landed |
| What it does | PRs | Proof |
|---|---|---|
| One function decides whether Clara's reply is held | #22334, #22450, #22473 | 0 of 5,515 frozen decisions differ Tracker: proven · proven |
| One list of which model each Clara step uses | #22446 | Same models resolved everywhere; Opus not switched on anywhere Tracker: merged, unproven |
| One "ask the team" entry point over 23 callers | #22455 | 0 of 5,515 differ Tracker: part landed |
| One review door for automated sends | #22522 | 0 of 5,515 differ. Willows logs show the right call, but the text it cleared then did not send (cause unknown) Tracker: merged, unproven |
| Harness recorders and three real-case fixtures (held email with times, staff took the thread, office-opened thread) | #22457, #22426, #22483, #22488, #22496 | Test-only; one real Willows turn recorded and replayed to the same reply Tracker: part landed · part landed · part landed |
| One ask-for-help door (#22592, 0 of 5,515 differ) | #22592 | 0 of 5,515 differ Tracker: proven |
| One quiet-hours door (#22597, 0 differ) | #22597 | 0 differ Tracker: proven (evidence) |
| One staff-notice sender for Leasing (#22595, 0 differ) | #22595 | 0 differ Tracker: merged, unproven |
| What it does | PRs | State |
|---|---|---|
| A thread the office opened gets no Clara turn at all | #22509 | Merged and proven at Willows Oct 26 night Tracker: part landed |
| One topic classifier for every module | #22519 | Merged and proven at Willows Oct 26 night Tracker: merged, unproven |
| "Try again" on a held first email; a 24-hour expiry leaves a clear expired state | #22559 | Merged Tracker: merged, unproven |
| A held renter email never expires silently; the hold names its check | #22538 | Open, not set to merge (tracker says it is) Tracker: merged, unproven |
| The follow-up writer gets the home's bedroom facts (Jessalyn) | #22507 | Open, not set to merge (tracker says it is) Tracker: merged, unproven |
| One person lookup for leasing, maintenance and renewals | #22506 | Set to merge, but has a merge conflict Tracker: proven |
| Rerun scripts write only to test properties | #22504 | Set to merge, waiting on checks Tracker: merged, unproven |
| Staff desk: a queued application is "on its way", never "sent" | #22574 | Merged |
| Shadow week | — | Running; the card does not yet update when the office replies later |
| Rating card polish (why it was held, record links) | — | A local change in the shadow runner, no PR |
| Renewals staff notices fold (building) | — | Being built; no PR |
| Module-off refusal and entitlement folds (building) | — | Being built; no PR |
| Robot phone format fix, appfolio-browser-agent#633 (merged, proven at Willows) | appfolio-browser-agent#633 | Merged; Willows card 49 written as (404) 285-9387 by the deployed robot, read back, restored (Oct 27 02:12Z) |
| No-reply notification emails never become conversations, #22616 (armed, Willows proof after deploy) | #22616 | Armed, Willows proof after deploy Tracker: part landed |
| What it does | Depends on |
|---|---|
| Situs Group onboarding package: Clara's intro to their team, what she does, who owns what | Needs only the baseline, which is done |
| Hand-off fixes to live behavior, and a "did a person already reply" signal from AppFolio | Waits on the ask-for-help fold |
| Tenant replies, dark: maintenance first, renewals second | Waits on the hand-off fixes and the full-context bench |
| PMS inbox behind one interface, then the review with numbers | Last in the chain |
| The rest of the cleanup: 9 more one-door folds (PMS writes, reminders, quiet hours, intake questions, scheduling, module off, entitlement, tool set, prompt examples) | Waits on the ask-for-help and topic folds |
| A text with two repair issues can lose one | Not started |
| Repeat messages on one guest card (7 sync bugs) | Waits on Fede's pick of design A |
| Opus 5.5 as a bench row against the frozen baseline | The model list is ready |
| Smith's Slack cards on the one card design | The shadow card part is done |
| Replay harness gaps: the tour loop calls the model outside the recorder; phone and clock not pinned | Filed as #22458 |
Snapshot Oct 27 2026 ~04:00Z. We read every message Fede typed to Clara lanes 001 to 006 from Oct 23 00:00 UTC to Oct 27 02:02 UTC. There were 1,438 messages, 57 of them status nudges. We found 136 distinct pieces of feedback. Live state for each item is on the phase tracker (rows 58 to 70).
| Status of the 136 items | Count |
|---|---|
| Done: a standing rule, decision or page | 41 |
| Merged and proven | 7 |
| Merged, not proven | 20 |
| Designed or PR open | 16 |
| Recorded only (memory or page line, nothing built) | 34 |
| Not applied | 16 |
| Built, but not reaching him | 2 |
These 18 items have nothing in place yet. Two of them have code merged, but Fede's own words after the merge say it does not work for him. Items 9, 10, 14 and 16 to 18 are questions or grading work, not builds. All six sit on tracker row 70.
| # | State | What he said, and what exists | Tracker row |
|---|---|---|---|
| 1 | Built, not reaching him | What do we do with this data? How does it relate to Cerberus? Is the Slack feedback used? Will you use the examples I graded? Slack ratings became the bench's human column (clara-benchmark#26, merged 3:53 PM MT); per-text rows #22622 open. The label summary still reads 0 judged. Nothing shows his 52 voice verdicts from the review set loaded as truth. (first said 10-26 12:31) | 58 |
| 2 | Built, not reaching him | AppFolio work: exponential backoff, a queue or circuit breaker, a dead-letter queue. Never give up; alert when it fails. Only the retry count changed: #22240 (retry to the age cap, not 10 tries) merged 10-24. No exponential backoff, dead-letter queue or failure alert in any PR or on the page. (first said 10-24 14:27) | 59 |
| 3 | Not applied | #34: "the leasing agent will meet you at the home, what day works?" fails. Always offer real availability ("number one rule"). The production no-tour-times check exists, but nothing grades an answer with no times as a fail in the bench. The same check false-fired on Trang. (first said 10-23 19:42) | 60 |
| 4 | Not applied | #33: too many "I'll check with the team" when the knowledge base already has the answer (pets). Verdict is in the review set. No check, fixture or bench column scores a "check with the team" deflection when the KB holds the fact. (first said 10-23 19:41) | 61 |
| 5 | Not applied | Card notes still in the old "Email Sent" format. "please dont tell me its off ina feature flag". Not behind a flag: the third piece (one line per exchange instead of full email copies) was never built. (first said 10-26 17:25) Oct 27: a builder is on it now (not merged). | 62 |
| 6 | Not applied | A nameless callback email went out. The rule: no basic info like a name, no callback. 004 memory nameless_callback_note: "if Fede reaffirms, the fix is one PR". He did reaffirm here. No PR. (first said 10-23 10:58) Oct 27: a builder is on it now (not merged). | 63 |
| 7 | Not applied | A mass text from the office is not a reply. Bench pairing still counts mass texts as staff replies. The baseline notes "tenant-other judge column invalid (pairing), v1.1 re-pairs"; v1.1 not done. (first said 10-23 19:01) | 64 |
| 8 | Not applied | David Jones: duplicate card. Why no reply? Go deep. Root cause recorded (AppFolio made both cards). The real bug, an Outlook 550 bounce not treated as a dead address, has no PR. (first said 10-26 17:24) | 65 |
| 9 | Question | Clara could have helped if she could read the lease. What would it take, and what share of questions need it? Plan only. Memory says "read the lease before no info on file". No plan or percentage on the master page. (first said 10-23 19:25) | 70 |
| 10 | Question | "i was suppposed to review more". Three review sets are still ungraded: the 15-pair first-email review, review set 3 (25 tenant texts) and items 28, 31, 32, 35–40, 43, 54–58 and 64. (first said 10-26 19:59) | 70 |
| 11 | Not applied | Situs alert fires on every tenant text the team has not answered ("handed to the team" not skipped). "this needs to be fixsed". No tracker row or PR. The merged reply-late fix covers held prospect emails only. (first said 10-26 08:59) | 66 |
| 12 | Not applied | #27: voucher question. Isn't accepting them Colorado law? Production deflected to the team. Verdict recorded. No knowledge or prompt fix found for source-of-income questions. (first said 10-23 20:55) | 67 |
| 13 | Not applied | Does the bench include the emergency categories and policies? A water leak is usually urgent. No emergency policy in the bench context or prompt found. (first said 10-23 21:06) | 68 |
| 14 | Question | #39 "McKinsey" comment: a prospect asked whether McKinsey is in Lakewood and similar in size. Not recorded. JSON #39 is a different (Spanish) card now, so this comment matches no item. (first said 10-23 20:57) | 70 |
| 15 | Not applied | "it should not tag me and gera". No PR or row found. (first said 10-26 09:22) | 69 |
| 16 | Question | Where does shared code live? How do Gera and I avoid adding duplicates? Unanswered. The only related item is the "first no-duplicates check" inside the open shouldSpeak PR 2. (first said 10-25 22:08) | 70 |
| 17 | Question | "we have a ton of wip nwith nothing done done". A WIP limit was proposed. No WIP limit on the page or tracker. (first said 10-26 15:43) | 70 |
| 18 | Question | Sales target: can we promise 50% fewer tenant messages for the office, short term? Question; no answer found on the page. (first said 10-23 21:30) | 70 |
Two updates from the session on Oct 27. First, the run order and the question of maintenance before the hand-off are answered. The order is: the pure split on the same Sonnet model with all folds first, then Opus, then tweaks. The gates fold first (tracker rows 52 to 56), then get attacked. The hand-off goes out when the last fold merges. Second, the rating "Why?" box went live at 21:53Z, and no one has pressed it since. PR #22622 fixes a bug where the same verdict on two texts collapsed into one record.
Fede graded the review set by voice on Oct 23, 7:00 to 9:25 PM Mountain time. 52 of its 68 examples have a recorded verdict. Every "wrong" or "should have" verdict is a test the next Clara must pass. Items 1 to 24 are tenant texts, 25 to 36 are prospect first emails, 37 to 40 are Spanish prospect emails and 41 to 68 are repair episodes. Not graded yet: #28, 31, 32, 35 to 40, 43, 54 to 58 and 64. His comment on "#39 McKinsey" matches a different card from the one now listed as #39, so it needs a regrade.
| # | Kind | Record | Customer | His verdict | His notes (shortened) |
|---|---|---|---|---|---|
| 1 | tenant | situsgroup:1162667257 | Situs Group | office reply unrelated | "this is unrelated" — the office message shown is a mass pest-control notice, not an answer to this text; the Clara-vs-office comparison on this row is void. |
| 2 | tenant | situsgroup:1156407909 | Situs Group | missing context | v1 page showed no thread; "Hola buenos días" then an office reply about keys looked disconnected; re-grade on v2 |
| 3 | tenant | situsgroup:1156452641 | Situs Group | missing context | "I have a question" / "what is the question" with the real question missing from the page; re-grade on v2 |
| 4 | tenant | situsgroup:1157467044 | Situs Group | missing context | "is the office open tomorrow" answered with "did you leave the keys" looked disconnected; re-grade on v2 |
| 5 | tenant | situsgroup:1156660644 | Situs Group | decent, missing full picture | office knew a pest-control rotation and added the unit; Clara started a work order and asked where the roaches are, "not terrible"; gap: no access to the pest-control… |
| 6 | tenant | situsgroup:1157904179 | Situs Group | concerningly bad | office had a payment plan in progress (DocuSign); Clara sent the generic portal link. Root cause: context gap, thread copy starts Sep 30, no balance / delinquency… |
| 7 | tenant | westernslopepm:1227637967 | Western Slope | tricky; reply is fine but do not announce closing the work order | the tenant is answering Jay, so no reply to this text. The miss is upstream: once Bernie finished the smoke alarm replacement (Oct 7), the maintenance side of Clara… |
| 8 | tenant | westernslopepm:1223901781 | Western Slope | Clara great | the office reply was not helpful ("put in a maintenance request"); Clara did great. Wants to see what happens after: would she have filed the work order, and correctly? |
| 9 | tenant | westernslopepm:1233332378 | Western Slope | wrong | adding a note to the work order is wrong; expected an acknowledgement plus an FYI to whoever is assigned (handyman/vendor): "tenant wrote the garage is open, dog in… |
| 10 | tenant | westernslopepm:1228104701 | Western Slope | much better than the office, fact-check | "much better than the office", asked to fact-check. Checked: balance snapshot the same day 3:06 PM shows $0.00, last payment $1,461.25 on 2026-10-08; Clara's amount,… |
| 11 | tenant | westernslopepm:1228220222 | Western Slope | fact-check | fact-check the LLC name. Checked: Cheryl had written earlier in the thread that the check must be made out to "1240 Elm Apts, LLC"; Clara took it from the thread.… |
| 12 | tenant | situsgroup:1156656567 | Situs Group | not terrible, not great | tenant said "keys" plural with a photo; Clara asked whether both the door key and mail key arrived, an unnecessary extra question. Clara never saw the photo… |
| 13 | tenant | situsgroup:1165765179 | Situs Group | impressive | Clara referred back to what a teammate had said earlier in the thread and did not invent a "you will not be charged" line; "this one is impressive". |
| 14 | tenant | situsgroup:1157467286 | Situs Group | should have stayed quiet | "you can leave the keys in the apartment" is a human call; we cannot know that is OK or who would act on it. Stretching the limit. |
| 15 | tenant | situsgroup:1167038035 | Situs Group | should have stayed quiet | a person from the office already started this conversation; Clara chiming in with "thanks for letting us know" adds nothing and can confuse. Pattern: when the office… |
| 16 | tenant | situsgroup:1156851472 | Situs Group | good | the office had sent a parking mass text and people reply "not my vehicle"; Clara saying "no worries, that went to everyone" saves the office time. |
| 17 | tenant | situsgroup:1168357453 | Situs Group | good | "the reply is good" (Clara added the dryer details to the work order). Miriam's real reply next morning is a real reply, not a mass text; the mass text in the thread… |
| 18 | tenant | situsgroup:1159276074 | Situs Group | good | "A+". A perfect reply for something that needs no human: we know the office address. |
| 19 | tenant | westernslopepm:1223153146 | Western Slope | wrong | no team item at all in phase 1, the team already watches this inbox; "chime in when helpful, otherwise shut up". Unanswered items go to a daily digest after 24-48 h… |
| 20 | tenant | westernslopepm:1223201287 | Western Slope | should have stayed quiet | a nuanced one. The paperwork is the basic annual recertification (affordable housing, urgent) plus a lease amendment; Clara had no knowledge of either, so no reply… |
| 21 | tenant | westernslopepm:1223646502 | Western Slope | should have stayed quiet | "again, noise. Terrible." Another team item on a thread staff can already see. |
| 22 | tenant | westernslopepm:1223825094 | Western Slope | should have stayed quiet | worse than 21: escalating what a human is already handling. In AppFolio the person on the conversation already gets an email per reply; a Clara item on top would be… |
| 23 | tenant | westernslopepm:1232199367 | Western Slope | gray area | silent is defensible, but the tenant is updating permission to enter (handyman may go in if nobody answers). The right logic: check the work order; if the permission… |
| 24 | tenant | westernslopepm:1228227768 | Western Slope | good | silent is right. Anything physical and immediate ("are you there?", "I'm on my way") is something the AI cannot know. |
| 25 | prospect | 66b1eebc | Camellia | production better | the original production email is better, more human. Short prompt: "Camellia Apartments is open now" is wrong, that is a community, "open now" belongs to a specific… |
| 26 | prospect | cea1aec8 | Western Slope | production better (tone) | the production email is great. The short prompt changes the tone too much ("Because it's open now, you can move in as soon as you're ready"); he wants the same Clara… |
| 27 | prospect | 553d9000 | Western Slope | short prompt better | "Yes, we accept vouchers with the same screening as any other applicant" is right. Production deferring the Section 8 question to the property team at the tour is… |
| 29 | prospect | 441766af | Western Slope | both wrong | neither offers a time range, so he does not trust either as current. Opus again says an agent will meet them (unnecessary) and dropped the 4-bed/2-bath, square feet… |
| 30 | prospect | d45b5be2 | Western Slope | today better | production draft offers a time range, good, but uses an em dash ("an AI tell", new rule: never em dashes). Opus: "on the special you saw, I'll check with the team and… |
| 33 | prospect | c37d5234 | Western Slope | both wrong | "why so many check with the team? This is all in the knowledge base already." Verified: both arms had the identical knowledge (pet deposit $300, pet rent $35, cats… |
| 34 | prospect | ae9e19cb | Western Slope | both wrong | "a leasing agent will meet you at the home" again, and "what day and time work for you" with no real availability. "We always need to offer real availability. That's… |
| 41 | episode | G:dump:2550_5657:2026-08-03T10:35:00 | Situs Group | wrong work order: one work order, not three | the tenant listed three problems in one text; Clara filed three work orders, the office filed one. One work order makes more sense; do not split one tenant report… |
| 42 | episode | G:dump:2072_4578:2026-04-15T14:08:00 | Situs Group | wrong: should have replied and acted | misspelled Spanish (ola, ban, abotar) from someone with little schooling, but the model can read it: the shower knobs do not work and may burst. Clearly a maintenance… |
| 44 | episode | G:situsgroup:1156660644 | Situs Group | good | good (asks where the cockroaches are, permission to enter, photo). |
| 45 | episode | G:dump:1767_3810:2025-08-09T06:51:00 | Situs Group | wrong: should have filed | the window seal fell off, a very clear work order. Silence plus no office reply for 3 days is bad; we could have filed the work order and asked for details. |
| 46 | episode | G:westernslopepm:1226021456 | Western Slope | wrong: should have triaged | "was the AC turned off?" Clara should have asked clarifying questions, checked whether a work order already existed (there was one) and triaged it, instead of staying… |
| 47 | episode | G:dump:1857_4048:2026-06-16T15:18:00 | Situs Group | wrong | she answered only the clogged sink and ignored the renewal request entirely, and the flooring (carpet is nasty, everyone else has hard floors). Bad. |
| 48 | episode | G:dump:2199_4885:2025-08-31T11:12:00 | Situs Group | should have filed | 3 keys for 5 tenants is a work order. |
| 49 | episode | G:dump:2015_4422:2024-09-16T21:04:00 | Situs Group | should have filed, emergency | a leak in the laundry room is an emergency work order, high priority, not silence; it should match the emergency policy in context. |
| 50 | episode | G:dump:2279_5101:2026-05-14T08:06:00 | Situs Group | cannot grade: our copy has no toilet work order | not enough context; was there a work order, who was it assigned to, was it done? Maintenance needs much more context: outstanding work orders, assignment, closed… |
| 51 | episode | G:situsgroup:1156192039 | Situs Group | cannot grade: thread context was missing | incomplete context, something was said before. Thread rows added to every episode after this. |
| 52 | episode | G:situsgroup:1156752809 | Situs Group | wrong: should have filed | filters are broken, a very clear work order; silent is bad. Why is it missing so many work orders? |
| 53 | episode | G:dump:1245_2407:2025-12-30T13:31:00 | Situs Group | cannot grade: incomplete context | incomplete, hard to judge; but a maintenance issue. Maintenance needs a lot of dialing in. |
| 59 | episode | G:situsgroup:1168357453 | Situs Group | good | good, adds the dryer details to the existing work order (assuming there was already one). |
| 60 | episode | G:dump:1583_3348:2025-01-17T17:31:00 | Situs Group | good | good. Emergency, bucket or towels, photo. Asking permission to enter when not home is redundant on an emergency where someone is dispatched right away; not terrible. |
| 61 | episode | G:dump:1813_3961:2024-04-22T22:02:00 | Situs Group | good with a nuance: property-level vs unit-level | the washer is in the basement, a shared area, so this is a building issue; Clara asked which room or wall in her unit and about entering her unit. Tricky: when does a… |
| 62 | episode | G:dump:2141_4749:2026-04-01T16:08:00 | Situs Group | repair filed ok; name update missed; commercial treated as residential | the name correction was missed (edge case; maybe a tool). Chase Tower suite is a commercial building and Clara treated it as residential ("can they come in if you are… |
| 63 | episode | G:dump:2191_4862:2026-01-26T09:18:00 | Situs Group | good | "A plus". Structural damage is maintenance; filing the work order and asking the few questions was right. Fede, later: Clara should also have acknowledged the renewal… |
| 65 | episode | G:dump:2260_5065:2026-06-20T19:07:00 | Situs Group | good, minor | funny one. Did the tenant send a picture? Clara did not ask for one and our copy has no attachments. Minor. |
| 66 | episode | G:dump:2355_5245:2026-03-05T18:07:00 | Situs Group | good, minor nit | dishwasher not working; Clara asked for a picture, which does not help much for an appliance that does not run (a video might); minor nit, do not over-index on… |
| 67 | episode | G:dump:2493_5543:2026-08-10T22:34:00 | Situs Group | duplicate of #64 | Same text, same tenant, second tenant record (new lease Jun 2026). Graded with #64. |
| 68 | episode | G:dump:73_291:2026-08-31T15:41:00 | Situs Group | good | shared laundry washer, filed as property-level, asked for a photo of the display. Noticed no work order number was sent: production always quotes the number; the… |
| Case | Customer | Record ids | What he said | Test to pass |
|---|---|---|---|---|
| Taylor (former tenant) | Western Slope | pers_794b2bc7-9b0d-4d6e-9a90-a48fbae4c104 · thread 470977e2-8c9b-4467-8d65-fea186779933 | 10-23 09:25 "why did we reply here", "formet tenant needs to be foleded into the same tenant guardrails" | A former tenant or unknown role gets no reply. |
| Lori Ruiz | Western Slope (Pomona Park) | pers_06d179ba-d24c-4a49-967b-29522a3f6eab · thread bdaed90a-f0bf-41ba-a2ab-eff6ecfa5aef · card 3041 | 10-24 14:03 "whywould we say weekend tours need to be booked by friday"; 10-26 17:29 "why is it +970 and not +1 or no plus?" | No made-up booking cutoffs. The card phone stays in a format AppFolio links to the text thread. Her reply reaches the conversation. |
| Trang | Western Slope (Ember Estates, card 3047) | pers_08d5ec68-a7f1-4323-b4c3-d14831ea0905 · thread b26a5299-5901-4e9e-974f-9675d22f7255 · intent gc:schedule_showing 5e0d4682-1fad-5aa3-99f6-8056ae07d9fc | 10-26 09:10 "why didnt we offer is the open question"; 09:20 "doesnt ember estates have a promotion?" | The first email offers real tour times and the listing special, is not held, and a building tour books on a best-guess unit. |
| Patrick Osborne | Western Slope (Northridge, card 3048) | pers_2fdc2da9-1241-470d-9e1d-2b7e5fb63448 · thread bbc825d1-3571-48db-a51c-cc4e13b247c4 | 10-26 09:48 "so not here kat is talking to this person"; 11:33 "a test shoul dnever w[rite]" | Clara stays out while staff are on the thread. Tests never write to his record. |
| Austin | Western Slope (2841 Morningside) | pers_62402c98-2573-417b-90c6-e7b30557e563 · thread 9cc7dc8f-1786-4576-bf92-52dce34ebab0 | 10-26 11:04 "well this should not have been a hodl at all" | Thread opened by staff: no draft, no hold, on any channel. |
| Anthony | Western Slope (2841 Morningside) | pers_8f205968-ad0a-479e-9f66-e926aa875c46 | relayed 10-25 14:20 "never email the customer when something on our side fails" | An internal failure goes to #alerts only. |
| Luke Burriesci | Western Slope | pers_ef28f50d-71a8-49d7-91f9-bee5100397e8 · thread 99e5e0f8-8b9c-4f8f-8384-a0e3d60cf57c | 10-26 17:22 "why is this email even makign it into our conversations?" | An AppFolio notification email never becomes an inquiry. |
| David Jones | Western Slope | AppFolio prospect 09da9c79-d0ef-11f1-b01d-02a2910905a9 | 10-26 17:24 "why didnt we reply to david jones?", "go deep fidn out whey" | An Outlook 550 bounce marks the address dead. Duplicate cards are AppFolio's. |
| Heather | Western Slope | pers_878b6e8d-b568-4df5-8ac5-16f4de47fd5e · thread c9f809af-2a67-47dc-8731-55dac75ee51a | 10-26 10:49 "what hte heck?", 11:09 "if we already send it we say i already send it" | Never tell a renter "send it yourself". Read the card; if the application was already sent, say so. |
| Emelyn Castillo Valdez | Situs (Dahlia) | AppFolio prospect 0d176c30-d143-11f1-b01d-02a2910905a9 | 10-26 08:24 "we didnt anser the utiliyt question" | A follow-up question on the same card gets answered. |
| Genesis Diaz (three cats) | Camellia | pers_779ddb81-1cde-4097-a2e3-b15a5d55569c · thread 3d62aa84-a6cb-4e32-acc9-5b26333807f0 | 10-24 08:50 "i didnt ask for any changes" | No fixes he did not ask for. |
| Marissa | Western Slope (Pomona Park) | unanswered since Sep 24 | 10-26 14:05 "please dont this eery again, thi sis way too in the past" | Never offer to recover stale leads. |
| Nameless callback | Western Slope | conv_voice_f9e839a6 | 10-23 11:12 "i had said if they dont provide basic things like name we dont do a callback" | No name means no callback email. |
The leasing verdicts are now acceptance rows in the bench store, outside the locked v1 sets: #27 (vouchers), #33 (no "check with the team" when the knowledge base has the answer), #34 (offer real tour times), #39 (needs a regrade), Heather (read the card, never "send it yourself") and the nameless callback (no name, no callback email). File: ~/.clara-bench/sets/v1/fede-verdicts-leasing.acceptance.json. Fede also pressed the Slack rating buttons 8 times on Oct 26. None of the 8 presses carries a reason.
The scratch file holds his full words: every item by topic, all 1,438 messages in order, and his full verdict notes. Path on the Mac mini: /private/tmp/claude-501/-Users-federicochapa-agents-001/efdb403c-4197-4ea2-967a-9f1fd47f77cc/scratchpad/fede-feedback-oct23-27.md. Phones and emails are removed. One pasted credential is replaced by [credential omitted].
From Fede's call with Gera on Oct 27 (raw transcript, 13 thread chunks in #transcripts). Gera is looking into it; this is recorded as an open question, not a decision.
| Gera on the call | Our pages today (Fede's Oct 23 and 25 rulings) | Idea on the table | |
|---|---|---|---|
| Who decides the topic | A separate classifier runs before the on/off check; it gives a best guess on the first message and corrects earlier messages as the thread goes on; add a confidence score and go quiet when unsure. | The one model decides the topic inside the writing call; no stacks of small classifiers; the code layer stays thin. | Keep one model deciding, and have it return topic plus confidence as part of its answer, so "unsure means quiet" exists without a second classifier. Caution: the two silences Fede rated Wrong today (Rachael, Elizabeth) would get more common under "quiet when unsure", not less. |
Fede, Oct 30: "we need a second opinion on our approach, don't think this is working well." Two independent read-only reviews from one shared brief, Opus 5.5 and Fable 5.1 at max thinking.
| Finding | Both say so |
|---|---|
| The first-email prompt was never actually split (65k to 68k tokens per first email, plan said 7k to 19k) and grew 14% this week. | Yes |
| The facts block reads vacant units, not posted homes, and nothing checks it against the public listing. | Yes |
| The bench sample has 0 Situs, 0 Spanish, 0 reply turns and its judge trusts our facts block, so it cannot see Fede's misses. | Yes |
| Opus is worse than today's Clara on the live prospect cards Fede saw (5 of 7) because it obeys wrong inputs Sonnet ignored, better on tenant texts and on the frozen bench. | Yes |
| Stop per-card prompt patches and stop using the Day 0 compare as proof for prompt changes. | Yes |
| Fede's ratings become the bar. | Yes |
| Topic | Opus 5.5 | Fable 5.1 |
|---|---|---|
| Swap bar size | 40 blind pairs, Wrong at most 4 | Last 25 paired cards, Wrong 1 or fewer, 8+ Situs |
| Tenant-text work in this lane | Stop (Gera's hand-off) | Does not raise it |
| Extra checks | None added | Prompt coherence check with a planted-conflict control, plus a cost/latency line |
Opus 5.5, independent, read-only.
Verdict. The direction is right, but the week did not carry it out: the leasing prompt got bigger, not smaller, and the bench that says PASS cannot see the customers, message types or mistakes you rated. On prospect messages Opus is worse than today's Clara (today better on 5 of 7, by your own notes on each pair) because it does exactly what our prompt and facts say, including the wrong parts; on tenant texts it does better.
| Plan piece | What happened | Verdict |
|---|---|---|
| Small job prompts | Not done. A first email still gets one 273K-character prompt (about 68K tokens); the "decomposed" arm is that text re-filed plus an override block. Done-means says "decomposed"; roadmap step 2 says "no prompt split needed". | Added, removed nothing |
| Hand the writer facts | Built; nothing checks they are true | Missing truth check |
| Bench Opus vs today | Built with care | Wrong sample, judge trusts our facts |
| Fix inputs one at a time | 34 changes to the leasing prompt in 8 days | Does not converge |
| Swap on your go | Unchanged | Keep |
Structural problem: Opus obeys lines Sonnet ignored, so every wrong or contradictory line in a 68K-token prompt is a hidden Opus regression. One rated card at a time cannot find them all. None of the three done-means items is finished.
| Cause | Evidence |
|---|---|
| Wrong sample | Runnable items: Western Slope 39, Camellia 12, Situs 0, Spanish 0, replies 0. 4 of your 6 Wrong (Situs or replies) could never appear. |
| Judge trusts our records | Its prompt: "a fact in those records or from its tools is sourced, not invented". A wrong facts block grades as correct. |
| No check for your complaints | Missing special, office hours as availability, "a leasing agent will meet you", unposted home: none checked. |
| Noisy judge | Today's Clara against itself, same 51: 19/18/14, 7/30/14, 16/25/10, 13/28/10 (better/same/worse). |
| Rewards over-sharing | Oct 30 PASS, Ember: Opus named a $2,300 home plus a second $2,550 home with the special; judged "better". |
| Different arms | Bench PASS used today's prompt; your first-email cards used the override arm. |
| Surface | Cards | Result | Why |
|---|---|---|---|
| Prospect messages | 7 | Today better 5, both wrong 1, Opus fine 1 | Opus wrote what our inputs said: wrong home from the facts block (Lampliter), "say plainly when the office is open" (Barcelona), "leasing agent meeting them there" (Ember, twice), no special on the named home (Ember; owner traces it to the facts block). Sonnet ignored them or looked the home up live. |
| Tenant texts | 33 | Opus better | 26 right, 7 wrong, mostly correct silence; no paired ratings, but today's writer would reply on 85% of texts the office already answered |
| Bench first emails | 51 | Opus "better" 29 to 34, worse 3 to 5 | Not usable (section 2) |
The prospect calls are mine: your note names a defect today's text lacks. Customers are not worse off: they still get Sonnet, and held first emails fell from 24 of 51 to between 12 and 16. The Opus shadow is what is worse.
| Measure | Oct 23 | Oct 30 |
|---|---|---|
| Leasing prompt file, lines | 2,298 | 2,618 (+14%) |
| Numbered RULE lines | 163 | 179 |
| NEVER / ALWAYS / MUST | 248 | 270 |
| Prompt a first email gets | n/a | 273K chars; 144 NEVER, 36 "HARD FAIL" |
| Share that cannot apply to a first email | n/a | about 23% |
| Office-hours instructions | n/a | 2 orders to state hours, 4+ bans |
| Gates | about 255, plus 60 rewrite layers | Mostly re-housed in named doors; a few deleted |
| Your PRs, all lanes | 338 in 8 days, +113K / -29K lines |
Credit: real code merges (one hold function, one ask-for-help door, shared reminders). But Day 0 compares hold and send decisions, not words, yet for changes like #23482 it was the only proof above unit tests. Plumbing cleaner; prompt and checks not.
Stop: Day 0 as a proof rung for prompt changes; PASS from a set missing your surfaces; override blocks instead of deleting rules; tenant work here (it works; Gera's hand-off).
| Line | Pass mark |
|---|---|
| Blind live pairs you rate | 40: 20 first emails, 20 replies, 10+ Situs, 5+ Spanish |
| Your Wrong count | Opus no more than today's on the same 40, and at most 4 |
| Your 6 Wrong prospect cards, re-run on today's pipeline | right on all 6, 3 runs each |
| Live never-do | 0: dash, made-up time or fact, needless "check with the team", unposted home, missing special, office hours as tour times |
| Held | Opus no more than today |
| End to end | one Willows conversation: inquiry, first email, booked tour, confirmation text, record ids |
V = I read the code or data myself. O = owner's account, not re-checked.
clara-leasing.ts at ea32569650 (last main commit before Oct 23 UTC) vs origin/main aade7fb253: 2,298 vs 2,618 lines; 312,537 vs 345,117 bytes; lines naming "RULE n" 163 vs 179; NEVER/ALWAYS/MUST/DO NOT 248 vs 270; 34 commits since Oct 23. guest-card-home-facts.ts 1,449 vs 1,647 lines.run-arm.ts: only the model swapped. Bench upgrade.ts candidate argv is only --writer-model; run summary writer "claude-opus-5-5-production". "Decomposed" = prompts/first-email-decomposed.ts (re-file plus TEN FIXES) and repair-rulings.ts (rulings block, forward-to-team tool removed). Owner memory calls the PASS arm "decomposed" (O, contradicted by the code).alternatives-summary.ts buildAlternativesSummary: "say plainly when the office is open"; clara-leasing.ts line 2420 the same; bans at lines 523, 1026, 2428, 2547, 2549. Production omitted hours; Opus added "Our office is open 9 AM to 5 PM on Fridays". Owner's cause verified, and understated.clara-email-core.ts:232: "Tours happen AT the home, with the leasing agent meeting them there". In every Western Slope and Situs snapshot checked, absent at Camellia.judge.ts FE_SYSTEM_EN; criteria facts_kept, correct, answers, no_overshare, tone, brevity. NEVER_DO_CHECKS in upgrade-lib.ts (10 checks, none for the four complaints). The bar line labeled "Fede's graded cards" counts the judge's verdicts on 9 items you graded on Oct 24, not your verdicts.Independent read-only review by Fable 5.1. Evidence in the appendix.
The plan is right but was run upside down: the week went into patching the prompt and chasing a bench score, while the two things the plan put first (true facts handed to the writer, one rule per topic) were never built, so a model that follows instructions better than Sonnet now follows our wrong instructions better too. On the cards you looked at, Opus was worse than today's Clara, and the bench could not see it: it grades against our own facts block and against production, never against what AppFolio posts or your verdicts.
| Part of the plan | Sound? | Where it is structurally wrong |
|---|---|---|
| Decompose the prompt | Yes | Not done. A first email still gets about 65k tokens of prompt (plan: 7k to 19k). The promised "no topic with two rules" check was never built. |
| Code gathers facts, model writes | Yes | "Open now" comes from our vacancy roster, not from what AppFolio posts; nothing checks it against the listing page. |
| Bench Opus vs production | Half | Every line is relative to production. "Correct" is judged against the block the writer was given, so a wrong block passes. "Fede's graded cards" is the judge's opinion, not yours. Situs: 0 items. No check covers 5 of your 6 misses. |
| Fix inputs one at a time | Not as practiced | 34 prompt and 12 facts commits in 7 days, one revert, each shaped like one card. |
| Swap on your go | Yes | Fine. |
Six of six real Opus prospect cards were Wrong (the "Fine" one is a Willows test lead).
| Card | The miss | Could the bench see it? |
|---|---|---|
| Lampliter | Roster 2-bed, no special; the only posted home is a 1-bed with 4 weeks free | No: block treated as truth; 0 Situs items |
| Barcelona | Office hours quoted as tour availability | No check; a prompt order told it to |
| Ember (Kerry) | Special dropped | Partly: on the frozen Ember cards production drops the special too, and the judge rewards Opus for naming a second home with it, against your one-home rule |
| Ember (two cards) | "A leasing agent will meet you" | No check, no rule; both Opus arms wrote it |
| Situs front door | Application deferred to the team | Partly, via the team-told check |
Wrong sample, wrong measure, and wrong inputs made load-bearing: all three.
| Where | Opus vs today's Clara | Why |
|---|---|---|
| Frozen bench, 51 emails, judge | Better: 29 to 34 wins, 1 to 5 losses over 4 runs (production vs itself swings up to 10) | Cleaner, shorter, keeps block facts |
| Live cards with production's email known (3) | Worse on all 3 | Lampliter: Sonnet ignored the block and looked units up; Opus obeyed it. Barcelona: Sonnet ignored the office-hours order; Opus obeyed it. Ember: Sonnet said the special, Opus omitted it (owner's read) |
| Tenant texts, 33 cards | Roughly 24 good, 8 to 9 bad by his presses | No paired production rating; today's state is silence |
Better on the frozen set, worse on the live cards you saw: Opus does what the inputs say, and the inputs were wrong. A swap today ships those defects where Sonnet papers over them by accident.
| Measure | Oct 23 | Oct 30 |
|---|---|---|
| Leasing prompt file | 2,298 lines | 2,618 lines |
| "NEVER" in that file | 129 | 142 |
| First-email rule file | 741 lines | 850 lines |
| First-email draft checks | 8 kinds | 10 kinds |
| Outbound guard families | 3 | 3, plus one new guard file |
| Retry or redraft layers removed | 0 | 0 |
Plumbing got cleaner (one hold function, six collapses, five switch files deleted); what the writer reads got bigger on every count.
Stop: per-card prompt patches; bench runs as the progress signal; reporting PASS while your Wrong cards stand.
| Check | Number |
|---|---|
| Last 25 paired Opus prospect cards on Slack, at least 8 from Situs | Wrong 1 or fewer; "production better" 2 or fewer; 0 repeats of a fixed mechanism |
| Posted-home check, 7 days of live first emails | 100% name a posted home and quote its special |
| Prompt coherence check | 0 topics with two rules; control goes red |
| Bench | 3 full runs in one day PASS with your Wrong cards as never-do checks; Spanish at least 10 items |
| Willows end to end on Opus | 3 real conversations (email, text, call) to a booked tour, with record ids |
| Cost and speed | About 2x cost per email, half the latency |
agents/clara/lib/agent/clara-leasing.ts at ea32569650 (Oct 23) vs origin/main aade7fb253: 2298 to 2618 lines, 312,537 to 345,117 chars, NEVER 129 to 142, CRITICAL 19 to 21, HARD FAIL 38 to 40, incident dates 62 to 73; 32 commits since Oct 23 (+498/-178). evals/prompts/guest-card-first-touch-rules.ts 741 to 850 lines. Frozen first-email request systemChars 272,657 (c2930/tsnap_3f21581b...). Master page plan: "Total ~7 to 19k tokens", "Build-time coherence test".clara-bench/runner/prompts/first-email-decomposed.ts (production prompt split under four headers plus a "TEN FIXES" block). Bench PASS runs used prompt: "production as deployed (today's prompt, unchanged)" (runner/upgrade.ts lines 361, 379).src/lib/domain/leasing/guest-card-home-facts.ts line 426 openTruthUnits = rawUnits.filter(u => u.available). Lampliter frozen inputs: c2930/lamp-units.json 5 vacant units (203, 208, 209, 217, A); c2930/situs-listings.json only unit 203 offered: true (1-bed, $1,153, "4-week-free rent concession"); unit A listing offered: false, read Oct 16.c2930/tsnap_3f21581b... and tsnap_shadow_...opus): production tools save_prospect, get_available_units, check_availability, email names the 1-bed at $1,153 with the concession; Opus tools save_prospect, declare_deferred_asks, email names "a 2-bedroom, 1-bath with 832 sq ft at $1,450/mo". Facts block in both: "Open now: a 2-bedroom, 1-bath with 832 sq ft, from $1,450/mo". "No lookups" rule: PR #22932, tracker row 104.c2930/tsnap_d8eb8bbf...): completed-actions text and src/lib/domain/leasing/tour/alternatives-summary.ts lines 50 to 53 plus clara-leasing.ts line 2420 order the writer to state when the office is open; lines 520, 523, 1026, 2426, 2547, 2549 forbid it. Production's frozen draft: "2:15 PM on Friday, October 30 isn't open, but I do have 1:30 PM or 1:45 PM that day."runner/out/upgrade-2026-10-30-post-23764-prospect-first-email-full, judge.jsonl, items at appfolio-399: Opus better 5, same 3, production better 0. Card rs-2026-10-24-n30: production omits the special; Opus adds "Our 3-bedroom at $2,550 a month comes with the Grand Opening Special" (a second home); judge worst: "edging into a roster", still picks Opus. Live card 37 (Kerry), owner analysis: Sonnet glued the special to the $2,300 home. Fede's rules: "one home named" (2026-10-22), "specials always mentioned" (2026-10-26).runner/upgrade-lib.ts lines 66 to 75 (every line relative to production run 2); line 120 graded.production_better computed from judge rows, labelled "Fede's graded cards"; NEVER_DO_CHECKS line 31 (no check for special, posted home, office hours, unsourced promise). Judge criteria runner/judge.ts lines 104 to 119: "correct" against "the records the assistant had".sets/v1/prospect-first-email.json: 73 items, 51 rebuildable; Western Slope 57, Camellia 12, Situs 4 (all Spanish, 0 rebuildable). Oct 30 full runs: Opus PASS 34/14/3, FAIL 30/20/1, PASS 29/17/5, PASS 33/14/4; production vs itself 19/18/14, 7/30/14, 16/25/10, 13/28/10 (bench page rows 2130 to 2137).fede-cards.json arms: first-email-decomposed 3 cards, all wrong; production prompt on Opus 4 cards, 3 wrong, 1 fine (Willows test; Opus text not found per ratings-audit.md); repair-g3-v2 33 cards: up 12, fine 12, stayed_quiet_right 1, down 10, wrong 4 (cards 4 and 7 stored down but were "Stayed quiet, right"). Notes verbatim: "no mention of concession", "Confusing office hours with availability", "Missed specials", "No need to say a leasing agent will meet you at the property".guest-card-first-email-draft-checks.ts kinds 8 to 10 (582 to 540 lines); guest-card-fact-trace.ts FactTraceHoldReason 4 to 4 (933 to 1009 lines); outbound-safety-pipeline.ts the same three guard families on both dates (fair_housing_hold, first_email_body_guard, hallucination_guard; the fair-housing label is now a ternary at line 288), one label added (internal_text_hold), 316 to 340 lines; new card-notes/card-note-guard.ts 191 lines; deleted held-first-email-retry.ts, prior-thread-tour-rule.ts, preference-capture-flag.ts, staff-asked-*-switch.ts, availability-cache-gate.ts. Retry layers still live: retryWriterOnce in guest-card-engine-turn.ts line 897; forced-tool retry and first-turn violation retry in conversation-manager.ts (owner memory Oct 29); em-dash retry rewrote content (#23099).prs-week.txt): 120 PRs, 108 merged; 37 writer-shaped leasing/clara fixes; 24 escalation/team-notify; 22 CI/deploy/perf/sync. Facts file: 12 commits incl. #23482 and its revert #23495.feedback_fede_ratings_are_the_bar): ratings read every tick, every Wrong card a frozen check, no PASS while one reproduces. Endorsed here as change 3.Oct 30, 21:40Z, production deploy a4d7d3fc0. One real text conversation at The Willows (test property) between our prospect tester number and the Willows line. Three of the four live fixes behaved in a real reply. Nothing here is a replay.
| Fix | We texted | Clara replied (verbatim) | Result | Record ids |
|---|---|---|---|---|
| Office hours named only when they are the reason (#23811) | "Hi, can I tour unit 201 tomorrow at 1 PM?" (inside hours, slot taken) | "For your tour, 1:00 PM tomorrow isn't open, but I've got 12:30 PM or 1:30 PM for unit 201, a two-bedroom at $1,550/mo. Which works better?" | Shipped: other times offered, no office hours | reply msg_fd5f558d, Twilio SMb6c5703a6c56e67544cf2873e4e7c33d |
| Same fix, time outside hours | "Could I come tomorrow at 10 PM instead?" | Held, never sent: "10 PM isn't going to work, tours run 10 AM to 4 PM on Saturday. I do have 3:00 PM or 3:30 PM open for unit 201..." | Wording right; delivery failed (see finding 2) | held msg_held_755415bd |
| No "a leasing agent will meet you" (#23800) | "1:30 PM tomorrow works, please book it." | "You're all set, your tour is confirmed for tomorrow, Saturday, October 31st at 1:30 PM at The Willows, 1601 Blake Street, to see unit 201. Looking forward to it!" | Shipped: tour booked, calendar event created, no meet-you line | reply msg_e357aa49, Twilio SM2ef6fc852fb7f00fa252d815a96037b0, tour d0c94d19 |
| Goodbye without rule echo or hold (#23822) | "Thanks, we decided to go with another place." | "No worries at all, Door, thanks for considering us. Best of luck with the new place, and if anything changes, feel free to reach back out." | Shipped: sent, not held, no rule wording | reply msg_6400f6ba, Twilio SMf36d340f5ef86267d3171f3958ffb144 |
| Approve follows the held message's channel (#23795) | nothing to approve | n/a | Not exercised | n/a |
Conversation 81e09472-535a-4a10-9531-521b926cf949. Before-number for the today/tomorrow fix (#23792, merged after this run): the confirmation said "tomorrow, Saturday, October 31st".


Also seen: a team "asked us something we could not answer" email went out on the plain tour request Clara answered fully.
| We texted | Clara replied (verbatim) | Result | Record ids |
|---|---|---|---|
| "Hi again, could I actually see unit 201 tomorrow at 1 PM?" (slot taken) | "Glad to hear you're interested again! 1:00 PM tomorrow isn't open, but I've got 12:30 PM or 1:30 PM for unit 201. Which works better for you?" | Shipped for the declined-time reply: "tomorrow", no formal date. Before: "tomorrow, Saturday, October 31st" | msg_9cad83f8, Twilio SM3c984d67e7eb193e594be6aa784247a6 |
| "Thanks, no need, I'll pass for now." | "No worries at all, Door. If anything changes, feel free to reach back out." | Thread settled, nothing booked | msg_c9126e8d, Twilio SM06f285532e4aaf7ba929cc6f0ff286fe |
Not yet exercised after the deploy: the booking confirmation itself, where the formal date appeared earlier tonight. Next real Willows booking covers it.

Tenant comms now belongs to Gera's swarm; leasing stays with session 001. Fede, Oct 27 ~03:10Z: "gera owns tenant comms now cause maintenance is the critical path", "so we could do some hand off there", "lets give a very comprehensive hand off". Everything needed to pick this up cold is below. Questions: #engineering-bridge via Smith.
In this section: what moves · tracker rows · where everything is · how to run it · shadow pipeline · bench numbers · open findings · Fede's rulings · merge gate · traps · the bar · first week · talking to 001
| Item | Gera's swarm | 001 |
|---|---|---|
| Tenant texts (repairs and everything else) | owns | |
| Prospect texts, emails, calls | owns | |
| Shadow week loop, pairs file, Slack shadow cards (content) | owns | card design system |
| Tenant sets on the bench (repair, other, Spanish rows) and their frozen records | owns | prospect sets |
Decomposed tenant prompt (repair-g3-v2 + emergency clause) | owns | first-email decomposition |
| One-door folds: the Maintenance caller of each row | owns (re-point, delete the copy) | the core door, Leasing and Renewals callers |
| Maintenance supervised holds on template texts | already yours (Gera, #22487) |
Rows on the phase tracker; change the owner field when you pick one up. Status stays derived.
| Row | Slice | Status | PRs | What it is |
|---|---|---|---|---|
repair-path | H | in build | none yet | Repair path on the decomposed prompt, writer on Opus 5.5, Willows end to end. Proof planned: shadow week first, then Willows with record ids. |
tenant-replies-dark | J | not started | none | Tenant replies built dark behind the existing maintenance.pms_inbox_tenant_door setting (default off); maintenance first, renewals second. |
opus-shadow-week | none | in build | #22526, agent-harness #1529, clara-benchmark #25 (all merged) | The shadow week; running since Oct 26 04:43Z. |
two-issue-intake | none | open | issue #22381 | A two-issue text can lose one issue (nothing filed, nothing asked). Fix, no switch; proof 8 of 8 filed on the reproduced case, production note replay unchanged. |
full-context-bench (tenant half) | H | open | clara-benchmark #27, #28, #29 (merged) | The tenant numbers below are yours; the first-email half stays 001's. |
Maintenance part of each collapse-* row | C to E | varies | see each row | Maintenance own copies: staff notice senders (after-hours-oncall-heads-up.ts, tech-sms-dispatch.ts), review doors (maintenance-text-review.ts, handyman-text-review.ts), intake classify, work-order writer, repair check-ins and rating asks, maintenance-off-refusal.ts, maintenance-entitlement.ts, tool set, prompt examples in clara-maintenance.ts. Table: shared versus module-specific. |
Paths under ~/ are on Fede's Mac. Tenant text never goes in git or on a docs page; ids, counts and hashes only.
| What | Where | What it holds |
|---|---|---|
| Frozen tenant sets | ~/.clara-bench/sets/v1/tenant-repair-text.content.json, tenant-other-text.content.json, spanish.content.json | The v1 items, masked text: repair 261 (20 runnable), other 150 (99 with a known stay-quiet truth), Spanish slice 421. Ids and fingerprints in git at clara-benchmark/sets/v1/ with LOCK.json. Never re-freeze v1; add v1.1 beside it. |
| Frozen tenant context records | ~/.clara-bench/sets/v1/tenant-repair-text.context.json (20), tenant-other-text.context.json (150) | What the writer saw, cut at the text: tenant, unit, lease terms, work orders open then, thread, knowledge; content hash per record. Built by ~/.clara-bench/tools/freeze_tenant.py from runs/slice-h-2026-10-26/freeze-raw. |
| Slice H runs | ~/.clara-bench/runs/slice-h-2026-10-26/ | h2-tenant-{repair,other}-text-{production,restrictive,full}/ (results.jsonl, summary.json, controls.json, proof.json), h2-score.json, h2-run.log. The h- folders are the replaced first pass. |
| Sonnet 5.5 arm (running now) | ~/code/PropFlow/clara-bench-wt-sonnet55/runner/out/h3-*-full/, log ~/.clara-bench/runs/slice-h-2026-10-26/h3-run.log | Same runner and items, writer claude-sonnet-5-5, decomposed, full context. First email done; tenant repair and other in flight. The runner change is two lines in runner/writer.ts on local branch fede/sonnet-55-writer, not in a PR. |
| Bench tools | ~/.clara-bench/tools/ | run-h2.sh (three tenant arms), run-h3-sonnet.sh, score-h2.ts (row values from run folders), freeze_tenant.py, check-swap.ts, probe/ (read-only door probes). |
| Bench repo | PropFlow-Technologies/clara-benchmark, local ~/code/PropFlow/clara-benchmark | Runner (runner/baseline.ts, writer.ts, checks.ts, door.ts, opus-shadow.ts, shadow.ts), decomposed tenant prompt (runner/prompts/repair-rulings.ts, arm repair-g3-v2), labels CLI (cli/labels.ts), web app. Own CI, never PropFlow's. Its CLAUDE.md says clara-bench; the checkout is clara-benchmark. |
| Clara Bench page | /a/clara-bench; rows in artifacts/clara-bench/runs/*.json, rendered by bin/render-clara-bench.mjs | Tenant rows: 2026-10-23-bench-d, -d2, -d3, -f, -g, -g2, 2026-10-25-baseline-v1, 2026-10-26-slice-h-full-vs-restrictive; review set 3; human labels. |
| Shadow week (New Clara on Opus) | ~/agents/001/shadow-opus/ | loop.sh, loop.pid, loop.log, pairs.jsonl (155 pairs: production vs New Clara), state-*.json, build_review_set_3.py. Loop runs from worktree ~/code/PropFlow/clara-bench-wt-opus-shadow. |
| Shadow feed (turn records) | ~/agents/006/shadow/turns-westernslopepm.jsonl, loop ~/agents/006/shadow/loop.sh | Session 006's Western Slope per-turn records, the bench's episode source. Slack posting off since Oct 26 (SHADOW_NO_POST=1). Runs from worktree clara-bench-wt-shadow. |
| Slack channels for shadow cards | #western-slope C0C283G9QDR, #situs-group C0BUB71T4NM | One card per new tenant text: New Clara's reply with the rating row under it, the office's reply below as context. |
| Label record | PropFlow GradeVerdict rows (table propflow-prod, index entityType-createdAt-index); bench export ~/.clara-bench/labels/labels-YYYY-MM-DD.json | One row per press: source (slack_card, app, fede_chat), rater, verdict, button, note (arm, PMS message id). Oct 25 to 26: 9 real presses (8 Fede, 1 Gera): Wrong 3, Fine 4, Stayed quiet right 2. |
| Replay snapshot | ~/agents/001/replay-snapshots/day0b-2026-10/ | cells.jsonl (5,515 hold decisions), snapshot.json, LEDGER.md, census-stock.json. No message text; never in git. The older day0-2026-10 was superseded after Gera's #22487. |
| G3 repair episodes, filing proof | session 001 scratchpad bench-g3/, filing-proof/, filing-proof/decomp/, g3-full-mode-report.md | 261 episodes; filing comparisons on the 20 runnable. Ask 001 for a copy (scratchpad is per session). |
| Four repair cases for Fede | tracker #repair-review-cases | Same order or new order: Beate (stove), Cornisha (washer and dryer onto the AC order), Marly (extractor onto the faucet order), Karin (indoor AC leak onto the outdoor spigot order). |
Every command runs on the Claude subscription, never a metered key, and reads production only through the bench door (writes, sends and notices blocked).
| Job | Command | Needs |
|---|---|---|
| Baseline rerun, one surface | cd ~/code/PropFlow/clara-benchmark; export BENCH_DOOR_URL=https://propflowai.co HARNESS_SUPPRESS_OUTBOUND=1; unset ANTHROPIC_API_KEY; npx tsx runner/baseline.ts --surface tenant-other-text --tier full (or tenant-repair-text --tier smoke) | Subscription token from Keychain Claude Code-credentials (or ANTHROPIC_AUTH_TOKEN); door token from Keychain benchmark-door-token, account propflow (or BENCH_DOOR_TOKEN). |
| A candidate arm | add --run-id <id> --writer-model claude-opus-5-5 --writer-prompt decomposed [--context full] | Same. --context full requires decomposed. Always run production in the same hour (Fede's apples-to-apples rule). |
| All three tenant arms | ~/.clara-bench/tools/run-h2.sh, then npx tsx ~/.clara-bench/tools/score-h2.ts (H_OUT, H_PREFIX override folder and prefix) | Same. |
| Shadow week loop | start: nohup ~/agents/001/shadow-opus/loop.sh > ~/agents/001/shadow-opus/loop.nohup.log 2>&1 & echo $! > ~/agents/001/shadow-opus/loop.pid; stop: kill $(cat ~/agents/001/shadow-opus/loop.pid); posting off: change export OPUS_SHADOW_POST=1 to unset, restart | Read access to AWS table propflow-prod (queries PMSMIRROR#appfolio#<account>), the door token, Keychain slack-bot-token, the subscription. It warns if a metered or AWS key sits in the environment. |
| Read the ratings | cd ~/code/PropFlow/clara-benchmark; npm run bench -- labels --since 2026-10-25T00:00:00Z | Read-only propflow-prod; writes the JSON to ~/.clara-bench/labels/. |
| Push a run to the bench store | npm run bench -- push <runner out dir> --kind tenant_text | The bench's own table propflow-clara-benchmark. |
| Replay compare (merge proof for code) | in a propflowai worktree: HARNESS_SUPPRESS_OUTBOUND=1 npx tsx evals/replay-harness/run.ts --mode recorded --baseline day0b-2026-10 | The snapshot folder above. Exit 0 and 0 differing cells, or a known-bugs ledger entry for each diff. |
The two shadow loops run from worktrees that are behind clara-benchmark main (#26 to #29 not in them). Start your copy from a fresh worktree off origin/main (the repo's CLAUDE.md has the command). 001's loop keeps running on Fede's Mac until you post on the bridge that your copy is up; then 001 stops it, so no text gets two cards.
shadow_rate:<verdict> with conversation id, message id, arm and PMS message ref. The relay signs with SLACK_RELAY_SIGNING_SECRET (readable copy in AWS SSM /propflow/prod/SLACK_RELAY_SIGNING_SECRET). Socket: launchd co.propflow.clara-slack-socket, restart with launchctl kickstart -k gui/$(id -u)/co.propflow.clara-slack-socket.| Door and card quirk | State |
|---|---|
| Texts filed on voice or email threads were refused (judged by thread channel) | Fixed, #22351 (Oct 25) |
| Door runs posted ops and Slack alerts (97 calendar cards in 47 s, Oct 24) | Fixed, #22324 #22327 #22329; rerun posted 0 cards |
| Door skipped the work-order filing step, so filing could not be measured | #22386 runs filing read-only; issue #22384 still open for the rest |
| Backfill burst: about 33 unstyled cards in one minute (Oct 26) | Fixed: the runner refuses to post if the last tick is over an hour old or the state file is missing |
| Two cards per text (006 feed plus 001 card) | Fixed: 006 feed no longer posts |
| 8 of 15 refused texts refuse as "not an active tenant" | Open. The mirror may file those texts under the wrong person (#22320 comment) |
| Card does not update when the office replies later | Open |
| Rating polish: "Why held" line and record-id links | Open: a local commit in the 006 runner, no PR |
State of the week: the Oct 26 05:50Z backfill covered 96 texts (Western Slope 41, Situs 55). Both arms ran on 46; the door refused 50. Production would reply / hold / stay silent on 7 / 27 / 12; New Clara on 4 / 14 / 28; they differ on 19 of 46. Review set 3 (the 19 plus 6 controls) waits on Fede's verdicts by number. At the first live grading (Oct 26) Fede marked New Clara's silence Wrong on both texts (Alba, Maleah, Western Slope): a possible too-timid pattern.
Noise, the headline, counts replies where the office had already answered within 48 hours. Lower is better. Helpful counts replies that are not "I've told the team".
| Slice H, Oct 26 (170 items) | Today (Sonnet 5) | New Clara (Opus 5.5) | New Clara + full facts |
|---|---|---|---|
| Other texts: noise, of 99 the office had | 84 (85%) | 44 (44%) | 53 (54%) |
| Other texts: helpful replies | 76 of 122 (62%) | 30 of 56 (54%) | 37 of 66 (56%) |
| Other texts: "told the team" / invented facts | 46 / 8 | 26 / 7 | 29 / 8 |
| Repair texts (20): noise, of 10 | 10 | 6 | 7 |
| Repair texts: replied / helpful | 19 / 18 | 10 / 10 | 11 / 11 |
| Spanish (27): noise, of 13 | 13 | 10 | 11 |
| Spanish: answered in Spanish | 23 of 24 | 13 of 13 | 15 of 15 |
| Sonnet 5.5, decomposed, full facts | Running tonight (run-h3-sonnet.sh); no tenant numbers yet. They land in h3-tenant-*-full. | ||
2026-10-26-slice-h-full-vs-restrictive. Read: laying every fact out made Opus chattier, not "facts hurt". New Clara is less noisy but also less helpful, and Fede's live presses call some of its silences wrong; both have to move. Caveats: Opus arms got code checks only (no office-reply comparison); the comparison step caught 3 of 5 planted checks; production's prompt changed mid-run; 64 of 170 work orders show today's status, not the status on the day.| Earlier tenant rows | Question | Answer |
|---|---|---|
| baseline-v1 (Oct 25) | What does production score on frozen set v1? | Noise 82 of 96 (85%), helpful 72 of 119 (61%), 47 "told the team". Repair smoke: noise 10 of 10, filing matched the office 7 of 17. Zero writes; 120 write attempts refused. |
| G3 (Oct 24), 261 repair episodes | Today vs today's prompt on Opus vs Fede's graded fixes on Opus | Filed when the office filed 45% / 48% / 57%; "check with the team" 34% / 39% / 8%; invented facts 30% / 21% / 16%. Duplicates doubled (5% → 11%): nothing matches a text to an open order. Full mode reached only 20: judge 4.00 vs 2.85. |
| G3 filing, 20 full-mode texts (Oct 25) | Does the decomposed classifier on Opus file like production? | No. Add to open / new / ask first / reply only: 5 / 3 / 4 / 8 vs production 8 / 5 / 0 / 7. Production's thresholds are tuned for Sonnet; keep the classifier on Sonnet for the first step. |
| Willows recorded v1 (Oct 25), 11 texts | Do two rule lines fix Opus habits? | Full 8 → 9, light 6 → 8; "team has it" before a work order exists 0. Accepted version: repair-g3-v2 plus the emergency clause. |
| D3 (Oct 24) | Model reads the staff thread vs a 24 h code gate | Agrees with Fede on 22 of 23 (24 h gate: 20). In-sample. |
| D2, F, G, G2 (Oct 23) | Earlier tenant and Spanish passes | Superseded; useful for method: D2 found only 958 of 1,523 "staff answered" pairs were real answers. F: 40 of 40 replies in Spanish. |
| Finding | Evidence | Next step |
|---|---|---|
| 11 texts where Opus with full facts spoke though the office had it, while restrictive Opus stayed quiet (reverse: 2) | T:situsgroup:1168128252, T:westernslopepm:1235281725, T:situsgroup:1157661192, 1167583157, 1158507401, 1156656567, 1158696881, T:westernslopepm:1234150772, T:situsgroup:1167581991, 1167050418, 1166634742; text in h2-tenant-other-text-{full,restrictive}/results.jsonl | Make a fixture; find what in the facts block pulls Opus in |
| Filing not at parity on Opus | G3 filing row above; filing-proof/decomp/ | Re-set thresholds for Opus and clean the open-order list (8 to 43 stale open orders per Situs tenant) before moving the classifier |
| Match before filing missed | Prompt rules 3c to 3e and code check 3f: filing fell 25 → 19 to 22 of 42; indoor AC leak still attaches to the spigot order; two wrong attaches in 3b (washer and dryer onto an AC order, kitchen extractor onto a faucet order) because the right order was not in the open list Clara saw | A design for open-order matching plus Fede's calls on the four cases, not another prompt line |
| Two-issue text loses one issue | #22381, 1 of 8 runs | Fix, no switch; proof 8 of 8 |
| 8 of 15 shadow refs refuse as not a tenant | #22320 comment; door refusals in loop.log | Check how the mirror files them; never count a door refusal as Clara's silence |
| A failed wake on a PMS inbox reply raises no alert | #22601; Willows Oct 26 23:09Z, thread 8ffa78e8, decision=wake_failed (cause fixed by #22590) | One alert card through the notice door |
| A new work order tags earlier unrelated texts | #22602; Willows WILL-003963 (kitchen sink) tagged 7 earlier deposit and guest-parking texts on 8ffa78e8 | Fix, no switch, Willows proof |
| Unknown-number text left unfiled | #22612 (mirror MSG#1237846985, pending / no_match) | Filed, not built; the same mirror rung affects tenant texts |
| Photos and multi-problem rules | #22146 (photos reach the order) and #22383 (different problem, own order) merged; neither proven on a real tenant photo yet. Follow-up #22389: scheduling and access fields dropped when the note tool files a new order | First proof is the next real tenant photo; keep one order per report |
| Data gaps | 2,276 of 4,114 work orders have no status; every arm re-adds entry details already on the order because tools do not show them | Context, not prompt |
| Date | Ruling | His words |
|---|---|---|
| Oct 23 | Reply only to a known prospect, or a tenant where the switch is on; unknown sender = silent; former tenant = tenant | "The default should be off." |
| Oct 23 | Office started or is running the thread = Clara silent; never acknowledge an arrangement only a person can approve | "we're just chiming in like this, unnecessary" |
| Oct 23 | Phase 1 has two outcomes, reply or silent; no team items, no escalations; unanswered items tracked internally only; read the lease before saying "not on file" | "either we stay silent or we're helpful, or we keep track of things that are not answered, but not do anything with it yet" |
| Oct 23 | AppFolio already emails staff per tenant reply; a Clara note on top duplicates it | "a disgrace" |
| Oct 23 | Spanish is first class in every bench; never Spanish knowledge entries, Clara rewrites the fact | "we need to get really good at spanish" |
| Oct 23 | Every bench ships a numbered review set; his verdicts are the human column | "i want to see and review a few dozen examples" |
| Oct 23 | Benchmarks replay episodes (next turns, the work order filed), not one turn | "this atomic testing just of the one turn doesn't seem very useful" |
| Oct 24 | Decompose today's prompt, never rewrite; same tone; production is arm 1 in every bench | "We're not trying to reinvent the wheel here." |
| Oct 24 | One work order per tenant report, all items in it | "It makes more sense to have just one work order." |
| Oct 24 | Shared-area problems file on the property, no entry question; infer, don't ask | "nobody has a laundry in their basement, it's probably shared" |
| Oct 24 | Acknowledge every item in a text (renewal interest, a call request) | "we would love you guys to renew and to stay" |
| Oct 24 | After a work order is done, Clara checks in on her own | "is everything done? How would you rate your experience?" |
| Oct 24 | Commercial work orders supported, research first | "we should support work orders for commercial buildings" |
| Oct 25 | Noise is the primary score; "told the team" is a failure | "the number one failure for Clara is adding noise" |
| Oct 25 | "Person on it" by topic, never a clock; thin code layer | "we should not be using time. we need to reason about the ask and topic" |
| Oct 25 | No readiness floors; customers move only on his word | "they live wherever they are today and what i decide manually" |
| Oct 25 | Prompt changes may merge, then run in shadow; nothing live to customers | "its fine if we merge prompt changes but its gonna go supervised for a while, like shadow" |
| Oct 25 | Safety emergency: tell the team at once and ask the unit in the same reply (decided under the grant; Fede may override) | recorded on this page |
| Oct 26 | Decide to speak before drafting, from the whole thread across every channel | "channel should not matter" |
| Oct 26 | No partial sends; fewer gates, battle-tested | "we need to keep it simple and reliable" |
| Oct 26 | Tests never write to production | "a test should never write to production" |
| Oct 26 | Backfills never post to Slack; one card per new text | "you're spamming the channels" |
| Oct 26 | Recovering a held message: drafted via Clara's path, sent only on his yes | "do not send anything without my approval" |
| Oct 27 | Get tenant replies to a point where they can be turned on, then supervised for a while | "its gonna be supervised for a while" |
| Change | What merges it | What makes it live |
|---|---|---|
| Pure move or fold (code) | Day 0b compare byte-identical: 0 of 5,515 cells differ | Live on merge, no switch |
| Code fix that moves holds on purpose | Every differing cell matches a known-bugs ledger entry named in the PR (evals/replay-harness/ledger/known-bugs.json); 001 re-freezes with a ledger the same day | Live on merge, plus a Willows proof |
| Fix to what feeds the decision (data, context) | The grid shows 0 diffs for these; prove it with a real-case fixture | Live on merge, plus a Willows proof |
| Prompt change | Bench row (production in the same hour, Spanish slice) plus a non-regressing compare | Runs in shadow first; Fede reviews examples and flips each customer by hand |
| New capability (tenant replies) | Dark behind maintenance.pms_inbox_tenant_door, default off | Fede's word per customer, then supervised |
company-live-gate.ts holds on module_off); see tenant communications.| Trap | What to do |
|---|---|
| An apply rerun wrote six fake rows into a real Western Slope prospect's thread (Oct 26) | Bench door only; rerun scripts refuse any non-test property (#22504) |
| Bench door side effects (97 cards, Oct 24) | A door proof counts Slack posts and ops events, not only database keys |
hello@propflowai.co is Fede's real inbox | No test, bench or replay mail to it or any hello+ alias, ever; no test call, text or email reaches anyone at Western Slope |
| Willows bench tenant | Fede is the tenant at The Willows: org_sandbox, property appfolio-45, unit TEST-102 (AppFolio occupancy 986), tenant tenant_c38824c0-7cac-401f-9488-9a2aa9bc3008; his phone is in memory reference_fede_test_renewal_targets.md. Last live tenant run: thread 8ffa78e8, work order WILL-003963. The /willows-persona skill switches what his phone is treated as. |
| Mirror filing quirks | Situs history before Sep 29 2026 predates the mirror (215 of 261 G3 episodes unresolvable); texts filed under the wrong person (8 of 15); unknown number with no name stays no_match (#22612); the thread's "our side" label does not say whether Clara or staff wrote it |
| Judge pairing | The tenant-other judge column is not valid: the mirror's next office message is often about something else (controls 5 of 13, then 2 of 5). Re-pair in v1.1 before trusting a judge column |
| Production moves under you | The prompt sha changed twice during slice H; run production in the same hour as every candidate |
| Balance and work-order status | Balance is never frozen; a work order closed since the text shows today's status |
Get the decomposed tenant prompt to where Fede can turn tenant replies on, then run them supervised (a person sees each reply before it goes) at the first customer he picks. Noise is the primary score, helpfulness second. The bar is "good enough to supervise", not full autonomy. Tenant texts are off at Western Slope, Situs and Camellia today, so this blocks nothing in leasing.
| Step | Work | Starts when |
|---|---|---|
| 1 | Read pairs.jsonl, review set 3 and the 9 presses; start your own shadow loop, 001 stops its copy | now |
| 2 | Study the 11 texts and the two Wrong silences (Alba, Maleah) | now |
| 3 | Read the Sonnet 5.5 tenant arm; pick the writer model for the tenant arm | h3-run.log says ALL DONE |
| 4 | Prompt edit for "person on it by topic"; bench row with production in the same hour and the Spanish slice | after 2 and 3 |
| 5 | No-switch fixes: #22601, #22602, #22381, each with a Willows proof | any time, in parallel |
| 6 | Open-order matching design and Opus thresholds; Fede's four repair calls | his calls |
| 7 | Mirror filing for the not-a-tenant refs | in parallel |
| 8 | Repair path (H) as a mergeable shadow arm, then Willows end to end with record ids | after 4 and 6 |
| 9 | Tenant replies dark (J) behind the tenant-door setting; Fede picks the first customer for supervised | after 8 |
One channel: #engineering-bridge, addressed to Smith in the bridge message shape; 001 answers there. No DMs, no tags on Fede for routine questions, no posts in customer channels. A change to a shared piece (bench runner core, card design, replay baseline, a core door) is a bridge message before the PR.
Update Nov 2: the one retry-once kept for dash and no-tour-times on the first email is also gone. Every rewrite, retry-regenerate and redraft layer is deleted; checks hold into Supervised review only, and a misfiring check gets its wiring and context fixed. Tracked on rows 43 and 101.
Fede's decision, Nov 1 (voice notes via the Trinity DM), applied to the eight reports. His words, lightly cleaned: "I don't think there should be any rewrites. Opus 5.5 doesn't need another model to rewrite it; that's a hack we did a long time ago for Sonnet, not best practice. Gates that fact-check or catch hallucinations are fine, but they must be very well wired with all the right context or they misfire. If we need to roll out over time, hold them as supervised and put it on Slack. The rewrites have to be all eliminated." Three rules follow: every rewrite, retry and redraft layer is eliminated, none of it folds into a new rewrite path; the checks that remain can only hold, and a held reply goes to supervised review on Slack; a check that misfires is fixed by its wiring and context, never compensated by a rewrite. Code work belongs to the Clara Refactor session's PR chain; this block is the list it works from. Each row cites the report that graded it.
| Rewrite layer (edits or re-runs Clara's reply after she writes it) | What the data said | Do |
|---|---|---|
| Hand-off template swap (report 8) | 18 of 22 replies replaced; 1 real catch at a customer, 1 false "noted for the team" sent, 1 answer lost | Eliminate. |
| "Lock in" booking-promise regenerate and fixed-line rewrite (report 5) | 0 real catches in 9 customer fires; 4 rewrites made the reply worse | Eliminate. |
| Unit-availability forced retry (report 4) | 0 real catches in 10 customer fires; 7 misfires, 5 never checked the unit | Eliminate. |
| Tour-time forced retry (report 4) | 4 real catches in 9 customer fires, all Sonnet's "10 or 2" habit; 1 retry ignored by the model | Eliminate the retry. The fact it guarded (every clock time offered came from this turn's calendar read) stays as a check, below. |
| Unit-count redraft (report 6) | 2 of 2 customer fires real; 2 of 5 redrafts dropped real content | Eliminate the redraft. The rule (never a quantity of homes) stays as a check, below. |
| Time-stamp strip (report 7) | 6 of 6 real, 0 misfires, but 14 of 15 copies were of our own history note | Eliminate the strip. Fix the wiring: the writer never sees the note (history rows enter the facts block without transport headers). |
| First-email redraft rules and the second-model rewrite era (report 3) | 9 rules; "no tour times" is 20 of 23 named fires; five rules never fired, three fired once; 34 of 35 Western Slope fires were rewritten by a second model | Eliminate the redraft and the second-model rewrite. The two rules Fede asked for by name (no tour times, no dash) stay as checks, below. |
| First-email tools-off re-run | Nov 1, Situs Group: the re-run, with its tools switched off, typed 60 pieces of tool-call markup into the email; held (reported by the Clara Refactor session) | Eliminate. |
| Never fired or plainly wrong (remove now) | Evidence |
|---|---|
| Five first-email rules that never fired, plus the "no home named" and "implies prior contact" rules (report 3) | 0 fires in 30 days for five; the rest once each |
| The anomaly gate's five content signals (report 2) | Never fired; the gate's only real fire was a timeout. The timeout and tool-failure alarm stays as an engineering alarm and a hold, never a blind re-run (a tool that already ran must not run twice, #11694). |
| The "lock in" phrase list and the unit-availability retry | 0 real catches each (rows above) |
What stays, as checks that can only hold, with the held reply sent to supervised review on Slack: the supervised hold itself (report 1); the calendar rule (a clock time offered must come from this turn's calendar read); the unit-count rule; the first-email "no tour times" and dash checks, holding into the same review and team notice the supervised path uses instead of a silent row; the fair-housing judge (Fede: keep); the hallucination guard; the send door. Each one is wired with the full context it judges against (the facts block with ids, the calendar read, the actions taken) before it is allowed to hold, because a check with less context than the writer misfires, and a misfire is fixed in the wiring, never by a rewrite. A hold check that has not fired in 60 days comes up at the monthly review, per the registry rule in the synthesis below.
Sequence for the PR chain: the eight eliminations first, each proven by a byte-identical replay of the recorded turns where the layer did not fire; then the never-fired removals; then the remaining checks rewired to hold-to-supervised with the Slack review. Already merged before this note: the Oct 26 calendar-read fix (#22485).
Fede, Oct 26 voice note relayed through the Trinity DM: "review the gates we have: the hold gates, the rewrite gates, and all the outbound stuff that is blocking… what it is, why it was built, how many times it has misfired, has it actually caught any bugs, and then a decision… maybe we built that gate because we were using a weaker model, but if we were to use Opus 5.5, do we still need that gate?" This section answers that one gate at a time, biggest first. The full catalogue of gates and their 30-day fire counts is the inventory section below (001, gate inventory Oct 26); this section does not repeat it.
How each report is built. Production records first (every hold row since the table began, read-only), then the gate's origin PR, then the same cases replayed through the read-only bench door on two arms: the model production used that day, and Opus 5.5 with the same prompt. A Fable judge answers the reviewer's question on each draft: would a careful property manager stop this before it goes out, and is the problem material. The judge is compared with what the person actually did (approve, reject, let it expire), never with an office message. Counts and ids only; no tenant names or text on this page.
Words used in every report. Caught: a person rejected the draft for a defect in the draft. Misfire: the gate held something nobody changed (approved as drafted) or something nobody looked at (expired after 24 hours, never sent). Superseded: the person texted again before anyone decided, and a newer draft replaced the held one.
What it is. When a company's module is set to supervised, Clara still drafts the reply, but instead of sending it the system stores it as a hold, posts a card to the company's Slack channel, and sends only on a person's approve. A draft nobody decides in 24 hours expires and is never sent. Staff cannot edit a held draft: the approve path takes approve or reject only, so every one of the 110 approvals all time went out as Clara wrote it (the records keep no edited version; the only other path is Clara redrafting on a reviewer's emailed instruction, which the records do not mark).
Why it was built. PR #16514 (2026-10-05) made the hold cover every module's email replies (the SES lane), closing two gaps from the supervised-mode audit under Gera's Oct 4 ruling; PR #16644 the same day held a text reply routed to a supervised module even when the topic check said general. Neither PR names an incident: both close gaps in the supervised-mode net. The eval catalog puts the drafting model at Sonnet 4.6 then and Sonnet 5 since Oct 10. The hold is a blanket: it does not detect anything, it holds everything in the module.
How often it has fired, all time (453 holds, 154 at real customers; 006 pull of every hold row, Oct 26).
| Where | What was held | All | Approved as drafted | Expired (24 h, never sent) | Superseded | Rejected by a person | Engineering or test |
|---|---|---|---|---|---|---|---|
| Western Slope | Maintenance notice to resident (template) | 73 | 6 | 64 | 0 | 1 | 0 |
| Western Slope | Maintenance notice to vendor (template) | 1 | 0 | 1 | 0 | 0 | 0 |
| Western Slope | Prospect email (first email or in-thread reply) | 31 | 16 | 5 | 0 | 2 | 8 |
| Western Slope | Leasing outreach text | 1 | 0 | 0 | 0 | 0 | 0 |
| Situs Group | Maintenance notice to resident (template) | 45 | 0 | 43 | 0 | 0 | 0 |
| Situs Group | Prospect email (first email or in-thread reply) | 1 | 0 | 0 | 0 | 0 | 0 |
| JP&Co | Team notice email | 2 | 2 | 0 | 0 | 0 | 0 |
| Where | What was held | All | Approved as drafted | Expired | Superseded | Rejected by a person | Engineering or test |
|---|---|---|---|---|---|---|---|
| (no org) | Maintenance notice to resident (template) | 2 | 1 | 0 | 0 | 0 | 0 |
| (no org) | Decision request (bake gate, retired) | 8 | 1 | 1 | 0 | 0 | 6 |
| (no org) | Leasing outreach text | 3 | 0 | 0 | 0 | 0 | 0 |
| Fairhaven (test) | Clara reply text | 41 | 31 | 4 | 2 | 1 | 3 |
| Fairhaven (test) | Renewal outreach text | 12 | 5 | 0 | 6 | 0 | 0 |
| Fairhaven (test) | Renewal reminder text | 2 | 1 | 0 | 0 | 0 | 0 |
| Fairhaven (test) | Team notice email | 1 | 1 | 0 | 0 | 0 | 0 |
| Fairhaven (test) | Renewal monthly batch email | 1 | 1 | 0 | 0 | 0 | 0 |
| Willows (test) | Maintenance notice to resident (template) | 11 | 2 | 7 | 0 | 0 | 1 |
| Willows (test) | Maintenance notice to vendor (template) | 82 | 4 | 73 | 0 | 0 | 4 |
| Willows (test) | Clara reply text | 35 | 12 | 2 | 14 | 0 | 6 |
| Willows (test) | Renewal outreach text | 51 | 8 | 8 | 5 | 0 | 6 |
| Willows (test) | Prospect email (first email or in-thread reply) | 19 | 8 | 6 | 1 | 0 | 4 |
| Willows (test) | Renewal reminder text | 25 | 5 | 0 | 0 | 3 | 17 |
| Willows (test) | Team notice email | 5 | 5 | 0 | 0 | 0 | 0 |
| Willows (test) | Renewal monthly batch email | 1 | 1 | 0 | 0 | 0 | 0 |
"Engineering or test" = holds closed by a proof run, a harness, or an engineering recompose during the Western Slope first-email launch week (Sep 22 to 24). Pending rows are omitted from the outcome columns.
Has it caught bugs? At a real customer, a person has rejected a Clara draft for a defect in the draft 1 times (one Western Slope maintenance template, Oct 11, a format request, not a wrong fact). The real catches came from engineering, not from the gate: 8 Western Slope first emails were recomposed during launch week because the held draft recited the listing back, narrated internal matching, or carried an unsupported claim; those are prompt fixes that shipped the same nights. One more catch happened at the Fairhaven test org (Oct 13: a draft pointed at a renewal link that does not exist at that building; Fede's reject). Two Western Slope rejects on Sep 23 were an incident (old inquiries re-surfaced as new), not draft defects.
How often it misfired. At real customers, 24 holds were approved exactly as drafted and 113 expired with nobody deciding. The expiries are almost all the maintenance notices (work order created, rate your repair): notice text built from a template, held because the maintenance module is supervised at Western Slope and Situs. Situs has never decided a single one. Approvals at Western Slope take a median 15 minutes and a 90th percentile of 49 minutes (22 approvals), so a prospect's first email waits that long on top of Clara's own time; 3 Western Slope first emails and 2 in-thread prospect replies expired unsent, which means the lead waited a day and then the team took the thread.
Opus 5.5 replay (68 held drafts replayed so far, Oct 26). Same records, same prompt, two models, one judge. "Would be stopped" is the judge saying a careful property manager would stop the draft; "material" means a wrong fact, link, date or promise, not style. Calibration against the people who decided: on the 40 text replies (test properties), the judge would stop 2 of the 7 drafts a person rejected and 6 of the 30 a person approved, on either model, so the judge is stricter than the team on style and misses most of what the team rejected (those rejects were format asks, not facts). On the 28 real first emails the judge is not usable: it cannot see the facts bundle the writer had (open homes, tour windows), so it calls real facts "invented" and stops 22 of 28 on production and 21 of 28 on Opus 5.5. The deterministic checks per arm are the reliable comparison there; they are in report 3.
| Case set | Replayed | Production model would be stopped | Opus 5.5 would be stopped | Staff actually rejected | Production model served |
|---|---|---|---|---|---|
| In-thread prospect email replies | 0 of 4 | not replayable through the read-only door today (it rebuilds text turns and first emails only) | |||
| Fairhaven (test) reply texts | 40 | 8 (4 material) | 6 (4 material) | 2 | claude-sonnet-5 |
| Western Slope first emails | 28 | 22 (22 material) | 21 (19 material) | 10 | claude-sonnet-5 |
Judge calibration: the 8 engineering-recomposed first emails are labelled ground truth for "stop"; the production arm's stop count on those rows is the check that the judge is strict enough. The Fairhaven reply texts are a test org and say nothing about customers; they are here because they are the only held reply texts that exist. The one Fairhaven reject (the non-existent renewal link) reproduced on the production model and did not on Opus 5.5.
Decision. Fede has already ruled that supervised stays per customer and he sets it by hand (Oct 25, relayed by the Clara Refactor session in the A1 decision above: "situs stays supervised", "they live wherever they are today and what I decide manually"). So the question for this gate is narrower than keep or remove.
| Question | Options | Recommendation |
|---|---|---|
| Template notices (work order created, rate your repair) inside the hold? | (a) keep holding them; (b) send them without review and keep the hold for model-written replies only; (c) turn the notices off at Situs and Western Slope until maintenance goes live | (b). A template cannot have the defects the hold exists to catch, and 108 of 119 at real customers died in the queue. No switch: this is a fix to live behavior. |
| What happens at the 24-hour expiry? | (a) silent drop, as today; (b) drop and tell the team in the same Slack card that the person was never answered; (c) auto-send on expiry | (b). Today a prospect waits a day and nobody is told. (c) is a turn-on by the back door and is not Fede's ruling. |
| Does the hold earn its delay on model-written replies once the module runs on Opus 5.5? | (a) keep as is; (b) keep supervised but on Opus 5.5 so the reviewer sees the better draft; (c) drop the hold where the replay shows zero material stops | (b) now, (c) per customer on Fede's word, after the replay above is complete and he has read the material rows. |
Sources: hold rows read Oct 26 (006); origin PRs from git history (006); 30-day fire counts and the sampled verdicts (13 wrong, 4 right, 7 unclear of 24) in the inventory section (001); replay files in session 006's scratchpad. Report order: the anomaly review gate (below), then the first-email draft checks, then the strip and rewrite layers, one per hour.
What it is. After Clara's reply is drafted, this gate looks at how the turn went, not at the words: a tool that crashed, a validation conflict, the output gate refusing, a say-do mismatch (the reply claims an action no tool took), a bare "yes" that overrode a question, a person asking for a human, or a turn that timed out. When one of those seven signals fires it parks the draft on the conversation, pages the on-call in #alerts, and on voice says "Let me check on that and get right back to you." The same cause on the same conversation is held once per hour without a second page.
Why it was built. PR #4061 (merged 2026-07-18), the third fix PR for the Camellia tour incident of 2026-07-17, where a renter asked to tour that day, Clara confirmed a tour three days later (his move-in date) and wrote the calendar event for it, then a second email contradicted the first and gave out the building buzz code. It was scoped to the leasing and scheduling tools first and shipped with kill switches defaulting on. The model drafting replies then was the mid-2026 Sonnet; the incident class it targets (a reply that asserts something the tools did not do) is exactly the class a stronger model makes rarer, which is the Opus question for this gate.
How often it has fired. Ten held turns in all retained production logs (Vercel runtime since Sep 23, inbound processor 30 days, renewal worker 90 days with zero; 006 CloudWatch count, Oct 26). One was a real customer: Western Slope, Oct 22, a prospect email turn that timed out. The other nine were test properties: five tour-booking tool errors on Fairhaven voice tours, two tour-booking tool failures at the Willows, two turn timeouts (Willows, Yale sandbox). Two more fires in the same hour were deduplicated. Every fire was a tool error or a timeout; the five content signals (validation conflict, output gate refusal, say-do mismatch, bare-affirmative override, human request) have never fired in retained history. The gate's own earlier history (Jul 17 to Sep 22) is not in CloudWatch.
Has it caught bugs, and how often did it misfire? The one real-customer fire held an empty draft from a timed-out turn, and 18 seconds later the first-email path answered the prospect anyway, with a text from the inbox mirror two minutes after that; nobody had to review anything and the prospect saw no gap. So since Sep 23 the gate has caught zero wrong replies at a customer and produced one on-call page, for a turn the first-email path answered on its own 18 seconds later (the message rows above). At test properties it held nine turns, seven on a failing tour-booking tool and two on timeouts, with no page (sandbox fires never page); that is an engineering alarm, not a reply gate.
Opus 5.5 replay. Not applicable, and that is the finding. The model is not the main variable: a tool crash is the tool's, and a timeout is the whole turn's clock (a faster model could cut timeouts, a slower one add them, and no replay can show it because these turns cannot be rebuilt). The content signals that a stronger model could change have never fired, so there is nothing to replay. Seven of the ten held turns were voice tours and one an in-thread email, which the read-only door cannot rebuild; the two other timeouts were test properties (one text at the Willows, one email from the email stress rig at the Yale sandbox).
Decision.
| Options | Recommendation |
|---|---|
| (a) keep as is, seven signals, hold plus page; (b) keep only what has ever fired, tool failure and timeout, as a retry first and a page only when the retry also fails, and delete the five content signals and the parked-draft review surface; (c) remove the gate and rely on the send gate and the hold | (b). The gate is an infrastructure alarm wearing a review gate's clothes. Keep the alarm, drop the review. Under the rollout rule this is a change to behavior that is already live, so it ships with no switch. One caution from the gate's own source: a turn's tools may already have run before the crash, so a blind re-run could book or send twice (#11694); the retry has to be of the drafting step only, never of a tool that already ran. The five content signals come back only with a reproduced case, as a replay cell in the harness, not as a gate. |
What it is. Before Clara's first email to a new renter goes out, a set of fixed rules reads the draft. Nine of them live in one file (no tour times offered while times were on hand; one price quoted for every size; a unit label on a one-home address; a hedged money fact such as "usually"; wording that implies an earlier contact; a size question with no tour ask; a "that one is gone" claim with no availability read; a promised follow-up nothing backs; any dash). Three siblings run in the same step: no home named, a sentence nothing on file backs (the fact trace), and a unit count. None of them is a model call. Since Oct 24 a failing draft gets one retry by the writer with the findings, and if it still fails it is held and nothing is sent. Before Oct 24 a failing draft went to a second model that rewrote it, and the rewrite went out.
Why it was built. PR #13351 (merged Sep 29, "live bug fix, no switch") added the first two rules in Western Slope's launch week, when first emails closed with a size question while tour times were on hand and quoted one price across sizes. The other seven arrived one or two at a time through Oct 24 (#13471, #13568, #21058, #21085, #22178), each after a draft someone did not like. PR #22249 (Oct 24) turned the rewrite into check, retry once, hold, after the Oct 22 Ember Estates incident, where the rewrite step produced the unit counts and roster that went out.
How often it fires (CloudWatch, both log groups, Sep 28 to Oct 26).
| Era | Where | Fires | What fired | What happened to the draft |
|---|---|---|---|---|
| Rewrite era, Sep 28 to Oct 24 | Western Slope | 35 | 17 draft rules, 15 no home named, 2 unit count, 1 draft rule still failing after the one retry | 34 rewritten by a second model and sent; 1 held (Oct 5), rerun into review Oct 7, expired unreviewed |
| JP&Co | 2 | 1 draft rule, 1 unit count | rewritten and sent | |
| Test properties | 8 | 6 draft rules, 2 unit count | rewritten | |
| Check era, Oct 24 22:19Z on | Western Slope | 5 | 4 no tour times, 1 no home named plus fact trace | 2 passed on the retry and went out; 3 held |
| Situs Group | 1 | no tour times | passed on the retry | |
| Willows (test) | 2 | no tour times | 1 passed, 1 held |
Which rule does the work. Of the 23 draft-rule fires that name a rule, 20 are "no tour times offered". Three rules fired once each (a promised follow-up nothing backs, a dash, wording implying prior contact). The other five rules have never fired. Every fire in the check era is "no tour times" except the last.
The retry works; the hold path does not. Where the writer's retry ran, it passed 4 of 4 times. In all 4 held cases the retry never ran at all (the log says retried: false; three from the inbound processor, one from the web runtime, so it is not one host's problem). The cause is not pinned down. One candidate, a writer round that ended in a tool call so nothing was captured, does not fit the data: the 4 held turns and the 4 passed turns all made tool calls, and in both groups some ended on a tool result. What is left is the capture not being reached on the inbound-processor path, which needs a code trace, not more logs. Either way the design "retry once, then hold for a person" has run as "hold at once" every time it held.
Has it caught a bug a renter would have felt? No. "No tour times" is a weaker email, not a wrong one, and in the rewrite era the fix was the rewrite step that PR #22249 removed on Oct 24 (checks now hold, never rewrite). The one sibling catch at a real customer in the check era was wrong: on Oct 26 at 15:35Z the fact trace held a draft for saying the application had been sent, and the application-send tool had finished about ten seconds earlier in the same turn (001 read the same logs the same way).
Misfires and what a hold costs. A check hold writes no review record and tells nobody: the row sits as "held" on the intent until an engineer finds it and reruns it with a script. Of the 4 real held first emails, 2 were rerun into review by hand (Oct 7 by a session, expired unreviewed; Oct 26 14:41Z by 001, approved by Fede at 15:28Z as written), and 2 had no review record as of this report. Neither of those two renters went unanswered: the Oct 25 renter had asked about two homes and got Clara's first email for the other home two minutes later; the Oct 26 15:35Z renter got Clara's text with the application link within a minute of the inquiry and a Western Slope team member texted them four minutes in, so the held email did no harm. A rerun of that second one at 17:16Z by this session (not needed, from a misread of the thread) produced no email and no review record; Clara's turn ended in a team hand-off and a turn-integrity hold on the thread, which this report owns and reported to Fede.
Opus 5.5 replay. The 28 real first emails from report 1 (27 Western Slope, 1 Situs Group) were rebuilt through the read-only door on both models with the same prompt, and the same deterministic checks were run on each draft. The judge is not used here (see the calibration note in report 1).
| Checks on the fresh draft | Production model (Sonnet 5) | Opus 5.5 |
|---|---|---|
| Would be sent to the retry (any check fails) | 15 of 28 | 11 of 28 |
| Unit count stated ("a few other homes") | 9 | 7 |
| Draft rules (no tour times, dash) | 6 | 4 |
| No home named | 3 | 0 |
| Of the 14 first emails staff approved as written | 9 would retry | 6 would retry |
The fact trace and the send door did not run through the door (they need live property data), so these counts cover the nine rules, the home-named check and the unit-count check only.
Decision.
| Question | Options | Recommendation |
|---|---|---|
| Keep the nine rules? | (a) keep all nine; (b) keep the two that matter (no tour times; no dash) and delete the seven that fired once or never; (c) delete the step | (b). Seven rules are dead weight with a maintenance cost and a false-hold risk each; the two that remain are the ones Fede asked for by name. |
| Still needed on Opus 5.5? | (a) yes, as is; (b) yes, the check, with the retry fixed so it always runs; (c) no | (b). Opus trips the rules less often (11 vs 15 of 28) but still trips them, mostly on the unit count. With the retry working, 4 of 4 retries passed, so holds should approach zero on either model; the check then costs one extra writer call now and then and never a hold. |
| What does a hold do? | (a) as today, a silent row an engineer reruns by script; (b) the same review hold and team notice the supervised path uses; (c) send anyway | (b), and until it ships, (a) is a dropped lead. A fix to live behavior, no switch, one PR. |
Sources: CloudWatch log lines "First-email draft check fired", "First reply named no home fact", "unit_count_stated" and "First-email check fired", web runtime and inbound processor groups, Sep 28 to Oct 26 (006); first-email intent rows and message rows read Oct 26 (006); PR bodies #13351, #13471, #13568, #21058, #21085, #22178, #22249; replay files hold-replay-email-results (006, Oct 26); 001, gate inventory Oct 26, section 6B (3 wrong, 1 right, 2 unclear of 6 first-email holds).
What it is. Two small fixed rules read every reply Clara drafts on a text or email turn. The tour-time rule fires when the draft names a clock time ("I have 10:00 AM or 2:00 PM", "1:00 PM isn't open") and no calendar tool ran in that turn. The unit rule fires when the draft, or the renter's own message, names a unit number tied to availability and no unit lookup ran. Neither one holds or rewrites. The draft is thrown away, Clara is told to read the calendar (or the unit list) first, the tool is forced on the next model call, and she writes the reply again. The second reply goes out whatever it says. Nobody sees that it happened except the log.
Why it was built. The unit rule came first: PR #10579 (merged Sep 21) after a Camellia email on Sep 19 where Clara wrote that two units were "set to be available by September 17" and both were occupied, with zero tool calls in the turn. The tour-time rule is PR #14124 (merged Sep 30), after a Western Slope email the same week where a renter proposed Friday at 1:00 PM and Clara answered "1:00 PM on Friday isn't open, but I do have 12:00 PM or 11:30 AM" without reading the calendar. Both PR bodies say live bug fix, no switch. Both are the exact kind of incident Fede's question is about: a wrong time or a wrong unit stated as fact.
How often it fires (agent traces, Sep 21 to Oct 26). Every fire leaves a retry step in the turn's trace, with the draft that was thrown away, the forced tool's answer and the reply that went out. That is what the table below is graded from, not the model's opinion.
| Where | Rule | Fires | What the draft had done | Did the forced lookup change what Clara offered? |
|---|---|---|---|---|
| Western Slope | Tour times | 5 | 5 offered times with no calendar read (6 email, 3 text across both customers) | 3 changed, 2 same |
| JP&Co (Camellia) | Tour times | 4 | 3 offered times, 1 declined a time | 2 changed, 2 same |
| Western Slope | Unit availability | 5 | 5 named a unit | 1 changed, 4 same |
| JP&Co (Camellia) | Unit availability | 5 | 3 named a unit, 2 had no unit in the draft (the renter's message named one) | 1 changed, 4 same |
| Situs Group | either | 0 | ||
| Test properties (Willows, Yale, Fairhaven) | Tour times | 26 | 15 offered, 11 declined | 10 changed, 15 same, 1 reply had no time |
| Test properties | Unit availability | 18 | 16 named a unit, 2 did not | 6 changed, 10 same |
63 retry steps in traces; the CloudWatch warn lines count 62 (60 in the inbound-processor group, which keeps 30 days, and 2 in the web runtime group), so one trace fire has no warn line. Every fire was on Sonnet 5, the production model for the whole window. The forced tool ran every time. Each fire added a median 7 seconds to the reply (longest 28 s). No fire ever held a reply.
The 19 fires at customers, one by one, graded against the forced lookup. A "catch" means the thrown-away draft stated something the calendar or unit list then contradicted. "Moot" means the draft was right and the retry only cost time. "Misfire" means there was no claim to check, or the retry never checked it.
| Rule | Grade | Fires | What happened |
|---|---|---|---|
| Tour times | Catch | 4 | Two Camellia texts offered "10:00 AM or 2:00 PM" on days where the calendar only had a morning window; the sent reply offered 9:00 to 11:45. One Western Slope email on Oct 7 offered windows on Monday Oct 5, a day already past; the sent reply offered Wednesday. One Western Slope text re-offered four windows from an earlier read; two of them were gone and the sent reply dropped them. |
| Tour times | Moot | 4 | One decline ("12:00 PM isn't open, 11:45 or 11:30 are") was exactly what the calendar said. Two replies offered the same windows the calendar then returned. One restated the time the renter had asked for; the forced read found it open and Clara booked it in the same turn, which the draft had not done. |
| Tour times | Unresolved | 1 | Western Slope, Oct 14: the renter had asked for Saturday; the draft offered "10:00 AM or 2:00 PM"; the forced read said the asked-for time was open and to book it now; the sent reply still offered 10:00 or 2:00 and did not book. The retry read the calendar and the model ignored it. |
| Unit availability | Catch | 0 | No fire at a customer found a unit stated as available that the lookup did not have. |
| Unit availability | Moot | 3 | Camellia's one open one-bedroom, named twice, was in the lookup both times. A Western Slope text saying a unit was "not showing as available" was confirmed by the lookup. |
| Unit availability | Misfire | 7 | In 5 the forced tool was the tour calendar, not the unit list, because the property's tool set has no unit lookup; the rule accepts either as grounding, so the retry satisfied the rule without ever checking the unit (one of these was "Unit 405 is the one you're touring", not an availability claim at all; two repeated a home list Clara had given minutes earlier). In 2 the draft named no unit; the rule fired on the unit number in the renter's own message. |
Pattern worth naming: the pair "10:00 AM or 2:00 PM" appears in 4 of the 9 tour-time drafts at customers, and in 3 of them (three days, two customers) the calendar did not have those times. That is a Sonnet 5 habit, not a property fact, and it is most of what the tour-time rule caught.
Has it caught a bug a renter would have felt? Yes, the tour-time rule has, 4 times at customers in four weeks: a renter would have picked 2:00 PM on a day with no afternoon slot, or a Monday that had already passed. The unit rule has not caught one at a customer since the Camellia incident it was built for; its 10 customer fires were 3 confirmations and 7 retries that checked nothing. Neither rule has ever caused a wrong reply; the cost is 7 seconds a fire and, once, a retry the model ignored.
Does the new setup still need it? (Opus 5.5 replay.) This is the one gate so far whose fires cannot be replayed on another model: they are renter (prospect) text and email turns, the read-only bench door only rebuilds tenant texts and first emails, and the recorded-turn path (every model request saved as Clara saw it, since Oct 24) holds none of these turns because the rules have not fired at a customer since. So the honest cell is "unmeasured on the fire turns". The nearest evidence, from turns the door can serve:
| Turns replayed | Arm | Replies | Named a time or unit with no lookup (the rule would fire) | Called the calendar or unit list on its own |
|---|---|---|---|---|
| 29 recorded customer turns since Oct 24 (JP&Co email 9, JP&Co sms 2, Situs Group email 1, Western Slope email 14, Western Slope sms 3), mostly tenant emails and texts, served by the door from the saved request | Production (Sonnet 5, same prompt) | 29 | 8 | 5 |
| Opus 5.5, same prompt | 29 | 1 | 14 | |
| 40 held tenant texts from report 1 (Western Slope, Situs Group, Fairhaven test), rebuilt from the records | Production (Sonnet 5) | 40 | 2 | 0 |
| Opus 5.5 | 40 | 0 | 0 |
Reading the 29 recorded turns: 8 production replies (Sonnet 5) named a tour time or a unit with no lookup in the turn, 6 of them times. Most were first replies to a new renter, where tour windows also come from the facts bundle, so not every one is invented; but Opus 5.5 on the same requests read the calendar or unit list itself on 14 turns against 5, and the tour-time rule flagged none of its replies (the rule excuses times a recent read already put on the table, so this is the rule's verdict, not a count of tool calls). The one Opus flag and one of production's are the same turn: a renter's application email, where the unit rule read the tax form "W-2" as a unit number. On the 40 held tenant texts neither model needed either rule.
The detector numbers above come from running the same two pure rules over each arm's reply text and tool names, with the same prior-turn context the live gate builds. Both production-arm flags in the 40 texts were a tenant's own unit named in a renewal-rate reply at the Fairhaven test org, which is a detector false positive, not an availability claim.
Decision.
| Gate | Built for | Real catches since | Does the new setup still do the thing? | Verdict |
|---|---|---|---|---|
| Tour-time claim retry | Western Slope email, Sep 30: a time declined and two offered with no calendar read (#14124) | 4 of 9 customer fires were real (invented "10 or 2", a past Monday, stale windows); 0 wrong declines caught; 1 retry ignored | Unmeasured on the fire turns. 0 unbacked time claims from Opus 5.5 on the ordinary turns we could replay. Sonnet 5 invented the "10 or 2" default 3 times. | Keep for now, as the one calendar rule inside the facts check: "every clock time Clara offers came from a calendar read this turn". Re-decide after one week on Opus 5.5: every turn is now recorded with its checks, so that week answers it from the index rows alone. If Opus never trips it, delete. |
| Unit-availability claim retry | Camellia email, Sep 19: two occupied units called available with zero tool calls (#10579) | 0 of 10 customer fires were real; 7 were misfires, 5 of which never checked the unit because the forced tool was the calendar | 0 unbacked unit claims from Opus 5.5 on the replayed turns; the first-email path already forces the unit list on turn 1. | Delete the retry. Keep the one-line log so the week on Opus still counts it. If a unit rule survives, it belongs in the facts check as "a unit called open must be in this turn's unit list", with the calendar no longer accepted as proof. |
Sources: agent trace rows with a tourtimeclaim-retry or availabilityclaim-retry step, read Oct 26 (006); CloudWatch warn and info lines for both gates, both log groups, 90 days; PR bodies #10579 and #14124; snapshot index rows TurnSnapshot since Oct 24 and the two-arm replay through the read-only door (scratchpad gates/snapshot-replay-results.jsonl); report 1 replay rows for the 40 texts. No renter name, phone or message text appears here; the drafts were read from the traces and are described, not quoted beyond clock times.
What it is. A fixed list of phrases is run over every tour reply Clara drafts on a text or email turn where no tour is confirmed yet: "lock in", "locked in", "lock it in", "you're all set", "book it right away", "automatically booked", "consider it booked", "no extra steps". Until Oct 25 a match meant one regenerate (same model, told to drop the phrase); if the phrase survived, the sentence was replaced by a fixed line and the reply went out. Since Oct 25 (PR #22296) a match is a block: the writer gets one retry, and if the phrase is still there the reply is held for a person. The renter never sees any of this except the wording that results.
Why it was built. PR #9635 (merged Sep 19), after a strengthened eval case (#9572) showed the drafting model writing "I'll lock it in" as if the tour were booked, 2 of 2 runs, even with the prompt rule that forbids it. No customer incident is named in the PR. Two later changes narrowed it: #18657 (Oct 11, stands aside on a cancel or decline) and #22292 (Oct 25, "lock in a 12-month lease" is the lease special, not a tour, after a Willows email went out cut off, #22289).
How often it fires (CloudWatch, Sep 26 to Oct 26). The log group that carries these lines keeps 30 days, so this is one month, not the 90 days the other reports cover.
| Where | Fires | Phrase | Outcome |
|---|---|---|---|
| Western Slope | 5 | lock in 2, lock it in 1, locked in 1, you're all set 1 | 5 regenerated |
| JP&Co (Camellia) | 4 | lock it in 1, lock in 1, locked in 1, you're all set 1 | 3 regenerated, 1 fixed-line rewrite |
| Situs Group | 0 | ||
| Test properties (Willows 22, Fairhaven 5, Yale 3, pipeline lab 1) | 31 | lock in / locked in 28, lock it in 2, you're all set 1 | 30 regenerated, 1 fixed-line rewrite |
All 40 fires were a "lock" phrase or "you're all set"; the other four phrases on the list never fired. 10 fires in late September (from the 26th), 30 in October. Last fire Oct 24 23:35Z at the Willows; no fire since the Oct 25 block-only change, and the hold path writes no log line of its own, so a hold since then would show only as a held reply, and none has. Every fire was on Sonnet 5. The regenerate added a median 1.9 seconds (longest 11 s). No fire ever held a reply before Oct 25.
The 9 fires at customers, one by one. Graded from the trace: the draft that was thrown away, and the reply that went out. A "catch" would be a draft telling a renter a tour was booked when it was not.
| When | What the draft said | What went out | Grade |
|---|---|---|---|
| Western Slope, Sep 26, text | "give me one day and time… and I'll lock in the virtual tour" (asking for a time) | Same reply reworded, sent with a stray """ at the start (the regenerate echoed its own delimiter; fixed by #15250 on Oct 2) | No claim. Made worse |
| Western Slope, Oct 2, email | "could you confirm one date and time… once I have that, I'll get it locked in" | "…I'll check availability and send a confirmation" | No claim. Harmless |
| Western Slope, Oct 2, email | Renter was on a tour at that moment: "you're already here touring… so you're all set, no need to book anything else" (true) | "…is available for a tour this morning, let me know if that time works and I'll get it booked", wrapped in """ | True draft turned false. Made worse |
| JP&Co, Oct 3, email | "we offer 6- and 12-month terms at the same rate, so you're all set either way" (lease terms, not a tour) | Same answer plus a boilerplate "I'll send a confirmation once it's booked" | No claim; the list still matches this today (the lease exemption in #22292 covers "lock in", not "all set"). Harmless |
| JP&Co, Oct 4, text | "just to lock it in, you mean 10:00 AM on the 7th, right?" (a question) | "Just to confirm, you mean 10:00 AM…" | No claim. Harmless |
| JP&Co, Oct 6, text | "I don't actually have a way to lock in a viewing… nothing is booked for the 13th" (the opposite of a promise) | Regenerate kept the phrase, so the fixed line replaced the sentence: "I'll send a confirmation once it's booked. I'm sorry for any confusion there…" | Honest draft mangled. Made worse |
| JP&Co, Oct 20, email | "Let's get your tour locked in, what day and time works?" | "Let's get your tour set up… Once you pick one, I'll book it, and my reply will tell you whether it's confirmed" | No claim. Harmless |
| Western Slope, Oct 23, text | "1:30 isn't open, I've got 12:00 or 2:00… could I grab your full name to lock in the tour?" | Dropped both open times: "let me check what's actually open… what day and time would work?" | No claim. Made worse |
| Western Slope, Oct 23, text | "I can do 2:00 or 2:30 on the 28th… can I get your full name to lock it in?" | Same times, "lock it in" replaced by the booking boilerplate | No claim. Harmless |
None of the nine drafts told a renter a tour was booked. "Lock in" is how Clara (and leasing staff) say "book it once you pick a time"; the list reads intent as a claim. The one "all set" that was about a tour was true: the renter was on it. Four of the nine regenerations made the sent reply worse than the draft, and two of those four went out with a stray """ on a renter's phone or inbox.
Has it caught a bug a renter would have felt? No, not once at a customer in 90 days. Its own rewrite layers were the hazard: the fixed-line replacement erased an honest sentence (JP&Co, Oct 6), and a regenerate turned "you're already on the tour" into an offer to book it. Those two layers were removed on Oct 25 (#22296); what remains is the phrase list plus a hold, which has not fired yet.
Does the new setup still need it? (Opus 5.5 replay.) Like report 4, the fire turns themselves are renter tour replies the read-only door cannot rebuild, so they are unmeasured on Opus. The same phrase list run over every reply from the turns the door can serve:
| Turns replayed | Arm | Replies | Phrase on the list | What it was |
|---|---|---|---|---|
| 29 recorded customer turns since Oct 24 (mostly tenant texts and emails) | Production (Sonnet 5) | 26 | 1 | "You're all set, your tour is confirmed for Monday": a tour the records show as confirmed; Opus wrote the same confirmation without the phrase |
| Opus 5.5 | 27 | 0 | ||
| 40 held tenant texts from report 1 | Production (Sonnet 5) | 40 | 1 | "you're all set" about a renewal, not a tour; the guard would not have run |
| Opus 5.5 | 40 | 0 | ||
| 28 Western Slope first emails from report 1 | Production (Sonnet 5) | 28 | 3 | "You're all set for your tour on |
| Opus 5.5 | 26 | 3 |
Replies counted where the arm produced text (a few turns ended in a hand-off or a tool call with no reply, so the counts differ by arm). The phrase list is the production regex run over each reply; it does not know whether a tour is booked, which the live guard does know.
Decision.
| Gate | Built for | Real catches since | Does the new setup still do the thing? | Verdict |
|---|---|---|---|---|
| Booking-promise phrase guard (regenerate, fixed-line rewrite; hold since Oct 25) | An eval case, Sep 19 (#9572): the model wrote "I'll lock it in" as if booked. No customer incident named. | 0 of 9 customer fires were a claim; 4 of 9 regenerations made the reply worse, 2 sent a stray """; 1 true sentence turned false | Unmeasured on the fire turns. 0 list phrases from Opus 5.5 on 67 replayable customer turns and texts; Sonnet 5 wrote 2, both true statements. On first emails both models say "all set for your tour" at the same rate, ungraded. | Delete the phrase list. The thing it was meant to stop is a fact, not a phrase: "Clara says a tour is confirmed only when the records show a confirmed tour". That belongs in the one facts check, next to the calendar rule from report 4, where it reads the tour state instead of guessing from "lock in". With the Oct 25 hold in place, the nine customer fires above would now be nine holds on replies that had nothing wrong with them. |
Sources: CloudWatch BookingPromiseGuard warn lines, both log groups, Jul 28 to Oct 26 (scratchpad gates/booking-fires.json); agent trace rows for the 40 conversations with the safety:booking-promise step, draft and sent text (gates/booking-cases.json); PR bodies #9635, #15250, #18657, #22289, #22296; the production phrase regex run over the report 1 and report 4 replay rows (gates/hold-replay*-results*.jsonl, gates/snapshot-replay-results.jsonl). Drafts are paraphrased or quoted only for the matched sentence; no renter name, phone or message text appears.
What it is. Before an email or text goes out, a fixed rule reads the reply for a quantity of homes: a number in front of a home type ("two 3-bedrooms"), a vague count ("a few other homes"), a plural type said to be open, one type open now and opening later, or a list of homes the renter did not ask about. If it finds one, Clara is told to write the reply again without saying how many; if the second draft still counts, the count is cut by hand and the rest goes out. Email and text only; voice keeps its prompt rule. It never holds.
Why it was built. Western Slope staff wrote on Oct 19 about a Pomona Park email thread of Oct 18 in which Clara had counted the homes open now and opening Oct 20 (the planning note quotes her sentence: "two 3-bedrooms (Units 204 and 405) open now and four 3-bedrooms opening October 20"), and asked that she never give counts. Fede, Oct 19, per the same planning note: "No counts at all". A read-only pass over Clara's outbound since go-live found 16 messages that counted homes (13 Western Slope, 3 Camellia); 6 of them were the first email's own dictated first sentence, which the prompt had told Clara to write that way since Sep 25. PR #21141 (Oct 19) stopped the prompt from counting and #21145 (same day) added this check; #21806 (Oct 22) widened it to rosters and now-and-later after a Western Slope first email the count-only rule had passed. Both are fixes with no switch.
How often it fires (CloudWatch and traces, Oct 19 to Oct 26).
| Where | Replies redrafted | What the draft said | What went out |
|---|---|---|---|
| Western Slope (Oct 23, email) | 1 | The asked-for home is gone, then "a few other homes": three addresses with beds and prices | One 4-bedroom at its price, "or would you like to see something else?". The other two homes, one of them the only 3-bedroom, were dropped. |
| JP&Co (Oct 24, email) | 1 | "studios and one-bedrooms available right now" | "a studio available right now… and a one-bedroom", everything else kept |
| Situs Group | 0 | ||
| Willows test (Oct 23, two texts; Oct 24, one email) | 3 | "one-bedrooms open… a couple of two-bedrooms"; "units 105 or 106, both one-bedrooms, are open"; "one-bedrooms and two-bedrooms available" plus the special and the tour windows | "a one-bedroom is open… a two-bedroom's available"; "unit 105, a one-bedroom"; the Oct 24 email went out without the tour windows and the special (the "lock in" guard of report 5 fired on the same draft; this is the Willows incident #22289) |
6 log lines, 5 distinct replies: the Western Slope email was checked on both its doors (first-email and main loop) in the same minute. Every fire ended in the redraft; the hand cut never ran. Every fire was on Sonnet 5. Five of the six lines sit in the inbound-processor log group, one in the web runtime group.
Has it caught what it was built for? Yes, by Fede's rule: both customer fires were a quantity or a roster, 4 and 5 days after the ruling, with the prompt already fixed. The cost shows in the same two rows: the redraft is a free rewrite, and in 2 of 5 fires it dropped real content (a home type at Western Slope; the tour windows at the Willows, together with the other guard).
Does the new setup still need it? (Opus 5.5 replay.) The same rule, minus the roster kind (it needs to know which home the renter asked about, which the replay rows do not keep), run over every reply from the turns the door can serve:
| Turns replayed | Arm | Replies | Said how many |
|---|---|---|---|
| 29 recorded customer turns since Oct 24 | Production (Sonnet 5) | 26 | 0 |
| Opus 5.5 | 27 | 0 | |
| 40 held tenant texts from report 1 | Production (Sonnet 5) | 40 | 0 |
| Opus 5.5 | 40 | 0 | |
| 28 Western Slope first emails from report 1 (Sep 21 to 24, written with that week's prompt, which still told Clara to count) | Production (Sonnet 5) | 28 | 9 (counts 2, "a few" 6, now-and-later 1, plural 1) |
| Opus 5.5 | 26 | 4 (counts 2, "a few" 1, now-and-later 1) |
Neither model counts on ordinary turns. On first emails under the old prompt both did, Opus less; the Oct 19 prompt fix is what changed that, and the two customer fires since show Sonnet 5 still slips.
Decision.
| Gate | Built for | Real catches since | Does the new setup still do the thing? | Verdict |
|---|---|---|---|---|
| Unit-count check (redraft, then cut) | Fede's Oct 19 ruling after the Pomona Park email; 16 counted messages at customers before it (#21145, #21806) | 2 of 2 customer fires were real under the rule (one week of data); 2 of 5 redrafts dropped real content | Opus 5.5: 0 counts on 67 ordinary turns, 4 of 26 on old-prompt first emails (Sonnet 9 of 28) | Keep, as one rule inside the facts check ("never a quantity of homes; name one per type"). It has a standing ruling behind it. The redraft should keep every other sentence or not run; re-read after a week on Opus 5.5 from the recorded turns. |
Sources: CloudWatch guard:unit_count_stated lines, both log groups (scratchpad gates/three-gates-lines.json); agent traces with the safety:unit-count-stated step, draft and sent text (gates/three-gates-cases.json); docs/planning/no-unit-counts-2026-10-19.md; PRs #21145, #21806; the production detector run over the replay rows (gates/unitcount-arms.mts). No renter name, phone or message text appears here; drafts are paraphrased or quoted only for the counted phrase.
What it is. Older Clara replies reach the model with a bracketed note in front of them saying when they were sent ("[Context note, never write this in a reply: sent Thursday, October 15…]"). Sometimes the model copies that line into its new reply. At the send step a fixed rule removes any line that is exactly one of those notes and sends the rest. Nothing else is touched; if nothing is left, nothing is sent and the team is told.
Why it was built. PR #14874 (Oct 1, urgent): a Western Slope renter texted "I'm here" and the reply went out starting with "[Sent Thursday, … not necessarily now.]". The same PR reworded the note so it tells the model not to copy it; this strip is the backstop in case it does anyway.
How often it fires (CloudWatch, Sep 26 to Oct 26; the log group keeps 30 days).
| Where | Fires | Who was being answered |
|---|---|---|
| Western Slope | 4 | 2 staff texts on the front-door line, 2 renter texts (in Spanish, Oct 13) |
| JP&Co (Camellia) | 2 | 1 renter email (Oct 4), 1 renter text (Oct 6) |
| Situs Group | 0 | |
| Test (Willows 5, Yale sandbox 4) | 9 | staff and renter texts |
15 fires, 13 texts and 2 emails, between Oct 4 and Oct 22 (14 lines in the inbound-processor log group, 1 in the web runtime group). Each one removed exactly one line, and the rest of the reply went out as written. In 14 of 15 the copied line was the reworded note that says "never write this in a reply"; the model copied it anyway.
Has it caught a bug a renter would have felt? Yes, 4 times at customers (2 Western Slope renters, 2 JP&Co renters) and twice for Western Slope staff: an internal bracketed line would have opened the message. It has no misfires to count: the rule matches only the exact note lines, which never belong in a reply.
Does the new setup still need it? (Opus 5.5 replay.) Neither arm copied a note line on any of the replayed turns (0 of 94 Sonnet 5 replies, 0 of 93 Opus 5.5), which is a weak test: most replayed turns carried little stamped history. The 15 production fires are all Sonnet 5 on turns the door cannot rebuild, so the fire turns are unmeasured on Opus.
Decision.
| Gate | Built for | Real catches since | Does the new setup still do the thing? | Verdict |
|---|---|---|---|---|
| History time-stamp strip | Western Slope text, Oct 1: reply opened with the internal sent-time line (#14874) | 6 of 6 customer fires were real; 0 misfires | Unmeasured on the fire turns; 0 copies from either model on 187 replayed replies | Delete the output strip; clean the input instead. The note is our own prefix on history rows, so the writer should never see it (the facts block carries message text without transport headers), and the leak itself becomes one row in the door 4 leak list (retry once, then hold). That keeps Fede's Oct 22 rule: nothing edits Clara's message after she writes it. The data supports it: 14 of 15 copies were of the note that already says "never write this in a reply", so the prompt line cannot cure what the input contains; removing the line from the input can. |
Sources: CloudWatch "Internal history stamp copied into a reply" lines, both log groups (scratchpad gates/three-gates-lines.json); agent traces with the safety:history-stamp-strip step (gates/three-gates-cases.json); PR #14874; the stamp regex run over the report 1 and report 4 replay rows (gates/unitcount-arms.mts). No renter name, phone or message text appears here.
What it is. When Clara hands a question to the team in a turn (the forward tool), the reply she drafted in that same turn is thrown away and a fixed line goes out instead: "I'll check with the team on that.", "I've noted that for the property team.", or, when the team has already been asked, "Still on it. I'll get back to you the moment I hear from the team." (three variants, in English and Spanish, with a text and an email wording for the last one). The draft survives only if the model marked the forward as covering part of the ask ("scope: partial" on the tool call) and the reply still carries every answer the model recorded in that turn. One exception since Sep 24: on a brand-new renter's first email, only the promise clauses are cut and the rest ships.
Why it was built. Aug 4 (#5388): on a thread the team owns, Clara had invented an answer; the fix was "replace the whole reply, don't try to detect which sentences were substantive". Aug 10 (#5640): the replacement was also deleting real answers next to a forward, so a forward the model marks "partial" releases the reply. Aug 17 (#5826) and Aug 19 (#5915, a criminal-history question): Clara must never promise a follow-up nobody keeps. Aug 21: a "partial" reply that dropped a known answer added the known-answer check. All of it is one idea: on a turn where the team takes over, Clara's own words are not trusted.
How often it fires (CloudWatch, Sep 26 to Oct 26; the log group keeps 30 days).
| Where | Replaced | Released | Why replaced | Was an ask actually opened for the team? |
|---|---|---|---|---|
| Western Slope (Sep 27, text) | 1 | 0 | no scope on the forward | yes, team notified |
| JP&Co (Oct 6 and Oct 13, texts, same resident) | 2 | 0 | no scope; scope "full" | Oct 6: nothing opened (the tool answered "nobody was notified and nobody is going to be, answer the sender yourself"); Oct 13: team notified |
| Situs Group (Oct 14, a renewal sandbox thread) | 1 | 0 | no scope | team notified |
| Test (Willows 12, Fairhaven 2, harness commons 2, Yale 2) | 14 | 4 | no scope 8, "full" 3, undelivered 2, invalid 1 | team notified 4, logged but not delivered 8, nothing opened 2; the 4 released were all Willows |
22 lines on 19 conversations; every one on Sonnet 5. Of the 18 replacements, 11 happened because the model set no release scope at all, 4 because it said "full", 2 because the forward could not be delivered, 1 because the value was not one the gate knows. Asks opened across the 18: 7 team notified, 8 logged for pickup but not delivered, 3 nothing opened.
The 3 fires at customers.
| When | What the draft said | What went out | Grade |
|---|---|---|---|
| Western Slope, Sep 27, text | Renter could not open the video-tour link for a tour that morning. Draft: passed along so the team can email the link, and meanwhile try pasting the link into the browser. | "I'll check with the team on that." | Promise was backed (team notified); the usable workaround was lost. Made worse |
| JP&Co, Oct 6, text | A resident with a lease asked to see their unit again before move-in. Draft: office hours, the entrance code and who to ask for. The forward tool replied that this property answers these itself, nobody was notified, answer the sender. | "I've noted that for the property team." | A correct answer replaced by a false statement. Made worse |
| JP&Co, Oct 13, text | Same resident waiting at the leasing office for someone to meet them. Draft: flagged to the team, "they'll be with you shortly". | "I'll check with the team on that." | "Shortly" was unbacked; the team was notified. Catch, though the line sent to a person standing at the door is cold. |
The 15 test replacements read the same way: the drafts answered a fee, offered a tour, said a home was no longer available, and ended with "they'll follow up with you"; the template erased the answer with the promise. That is the design ("no substantive content on an escalating turn"), and it is what the data shows it costs.
Has it caught what it was built for? Once at a customer in 30 days (an unbacked "shortly"). It also sent one false line (an ask that was never opened, reported as "noted for the team") and dropped one working answer. The gate cannot tell those apart because it does not read whether an ask was opened; it reads a release flag on the tool call that the tool's own description tells the model to "ALWAYS set", and the model left it out in 11 of 18.
Does the new setup still need it? (Opus 5.5 replay.) The three customer fire turns are renter texts the read-only door cannot rebuild (the Situs sandbox turn replayed: both models forwarded and wrote the same "I'll check with the team and get back to you"). On the turns the door can serve, how often each model hands off and, in the same reply, writes a promise clause (the pair that makes this gate fire):
| Turns replayed | Arm | Replies | Handed off | Handed off and promised |
|---|---|---|---|---|
| 29 recorded customer turns since Oct 24 | Production (Sonnet 5) | 26 | 7 | 2 |
| Opus 5.5 | 27 | 5 | 4 | |
| 40 held tenant texts from report 1 | Production (Sonnet 5) | 40 | 28 | 16 |
| Opus 5.5 | 40 | 34 | 10 | |
| 28 Western Slope first emails from report 1 | Production (Sonnet 5) | 28 | 6 | 6 |
| Opus 5.5 | 26 | 6 | 6 |
Both models write "I'll check with the team and get back to you" when they forward, because the prompt tells them to, in those words. The gate then replaces the reply for saying it unless the model also set the release flag to "partial"; the tool description demands that flag, and Sonnet 5 left it out on most fires. Opus 5.5 does not change that pair; it hands off about as often and promises less on texts. Caveat: in the door the forward tool runs nothing, so these counts say what each model writes, not whether an ask would have opened.
Decision.
| Gate | Built for | Real catches since | Does the new setup still do the thing? | Verdict |
|---|---|---|---|---|
| Hand-off template gate (whole reply replaced on a forward unless "scope: partial") | Aug 4 invented answer on a team-owned thread (#5388); Aug 19 promised follow-up nobody kept (#5915) | 1 of 3 customer fires (an unbacked "shortly"); 1 false "noted for the team" sent; 1 answer lost; 11 of 18 replacements were a release flag the model left out despite the tool telling it to | Opus hands off as often and promises the same words; the pair that fires this gate is written by the prompt itself | Delete the template replacement. What it protects is "promises match actions", and that is a fact the open-ask record already holds: a "I'll check with the team" is allowed when an ask was opened (notified or logged) and blocked (retry once, then hold) when nothing was opened. The reply is never swapped for a template, and the answer next to the promise ships. The first-email clause cut goes the same way. |
Sources: CloudWatch Agent:escalated-gate lines, reason=same_turn_escalation and same_turn_escalation_released, both log groups (scratchpad gates/handoff-lines.json, handoff-fires.json with each forward's input and result); agent traces for the 19 conversations (gates/handoff-traces.json); conversation-manager.ts, the same-turn escalation scope comment (Fede decision of Aug 10) the release-scope helper and the forward tool's scope schema in src/lib/tools/_constants.ts; PRs #5388, #5640, #5826, #5915; the door replay of the four customer turns (gates/handoff-replay-results.jsonl) and the promise detector run over the report 1 and report 4 replay rows (gates/handoff-arms.mts). No renter name, phone, code or message text appears here.
Goal: a repeatable, safe upgrade. Each writer job names its model in code, in src/lib/platform/ai/model-paths.ts. A swap is one PR per job that changes that one line. Rollback is GitHub's Revert button. Never a per-company model setting (Fede, Oct 27: "this is a global rollout").
| Job | Model today | Bench surface | Proof needed |
|---|---|---|---|
| First email | claude-opus-5-5 | first_email | Steps 1 to 5 |
| Prospect email reply | claude-opus-5-5 | prospect_reply | Steps 1 to 5 |
| Prospect text reply | claude-opus-5-5 | prospect_reply | Steps 1 to 5 |
| Text beside the first email | Haiku | None yet | Record its request first |
| Quiet-lead follow-up (and its re-engagement twin) | Haiku | None yet | Record its turn first |
| Tour-reminder translation | Haiku | None yet | Record its request first |
| Card notes | claude-opus-5 | Not named yet | Name a bench surface, then steps 1 to 5 |
env -u ANTHROPIC_API_KEY npm run -s bench -- <command>:
upgrade --surface first_email --model <id> [--tier smoke|full]
upgrade --surface prospect_reply --model <id> [--tier smoke|full] [--production-model <id>]
upgrade --surface tenant_text --model <id> [--tier smoke|full]
fede-cards control
The last one is the planted-defect check on its own. Each run shows a "Fede cards" column: Fede's rated cards (17 Wrong, 1 Fine) are a frozen check, and an arm FAILS while any of his Wrong cards still reproduces. Any never-do finding fails the arm. The bar: better than production on the judge, Fede cards clean, never-do zero. Full tier decides; smoke is a first look. Limit: prospect_reply replays each turn's recorded prompt, so it judges a model swap only, never a prompt change.day0l-2026-10 (6,487 cells). It cannot see prompt text, so it is not proof for prompt changes.model-paths.ts. The body names the bench run row on the Clara Bench page, the Day 0 result and the Willows ids. Merge is Fede's go for a swap; live everywhere on merge, no switch.The quiet-lead follow-up and its re-engagement twin (Haiku, no recorded turn); the text beside the first email (Haiku, no recorded request); the held-message redraft writer; the tour-reminder translation (Haiku). Each needs its request recorded before it can be swapped with proof.
Full page: AI leasing assistant landscape (Viktor, OpenClaw, EliseAI, BetterBot, Zuma, Funnel and others). Ideas worth stealing, in order: (1) remember a correction after one note and undo it with one message; (2) on a reply-all thread read everything, answer only when named or when it is her item; (3) plain-words orders from staff within their own permissions ("move Maria's 3 PM to Thursday and give it to Ryan"); (4) standing orders by chat ("every evening send me tomorrow's tours"); (5) scheduled check-ins that stay quiet unless a person is needed (an agent out tomorrow with three tours, ask who covers); (6) never send a follow-up a person already sent. Complaints to design against: follow-ups before staff arrive, more tours but fewer leases, long replies to quick questions. Recommendation: fold 1, 2 and 6 into the coworker work already planned; the rest after those are proven at Willows.
Three opinions, one recommendation (Oct 26 evening). Fede: "are you sure we need 29 gates?", "it seems like too many individual little pieces built once per incident", "look at anthropic / openai guidance or other frameworks / startups". Two independent reviewers at max effort (Opus 5.5 and a second Fable 5.1), each with the report and the published guidance (Anthropic, OpenAI Agents SDK, NeMo, Guardrails AI, Sierra, Decagon, Fin, Ada, Parloa), both attacked the first answer. Their full texts are below. What all three agree on, and the recommendation:
| # | Door | Where | What it does | Can it stop a reply? |
|---|---|---|---|---|
| 1 | Should Clara speak | before the writer | Person on it, nothing to answer, muted, unknown role, runaway, bare "thanks" (built: shouldSpeak) | silent, with a reason |
| 2 | Facts in | before the writer | Code hands the writer a facts block with ids: home, rent, size, open date, special, tour slots, policies, actions taken. The writer gets only this turn's tools. | never; it removes the mistake |
| 3 | Declaration | at the writer | Strict schema: the writer declares the ids it used (homes, slots, prices, facts, actions, asks, language) | never; the API guarantees it |
| 4 | Claims match the record | after the writer | One code join: every declared fact is in the facts block, every promise has an action (booked, filed, team told via an open ask), plus a leak list (undeclared times or money, time stamps, tool markup). Replaces 30 prose-pattern lines. | the only content hold: retry once, then one hold record with the check name, and an alert that re-fires at expiry |
| 5 | Voice | after the writer | Dashes, greeting, renter's language, staff-note shape | never: retry once, then send; the residue is a bench row |
| 6 | Send door | at send | Consent, live, module on, test scope, ceiling, quiet hours; any error sends nothing and alerts | refuse with a reason |
| judge | Fair housing | after the writer | Cheap word screen, then a separate model judge (Fede: keep) | hold, as today |
Where the first answer (five doors) was wrong, per both reviewers: it kept the fact trace as the base of the facts door (its one real fire was wrong; it judges where a fact came from, not whether it is true); it had no severity tier, so every fire held and a missing tour time cost a lead; it said nothing about the hold lifecycle (110 of 124 renter-facing holds expired unseen, the largest harm in the window); two of its seven deletes were unsafe (the typed tool-call strip and the work-order claim rule still do real work); and the 5,515-cell replay proves hold decisions, not content checks, which need recorded drafts with every fire labeled and about 300 clean cases to show under 1% false fires.
What stops one-line-per-incident: a closed registry of checks with a build check that fails any PR adding one without a row (id, join target, origin case, three controls in English and Spanish, false-fire rate, status hold / retry-only / observe / retired). An incident's first artifact is a replay ledger row and a bench row, filed by cause (fact missing, prompt ignored it, schema cannot say it, hold died, tool bug). Only "had the fact, declared it, still wrote something else" earns a column in door 4. A hold check that has not fired in 60 days or crosses the false-fire line is demoted at a monthly review.
Order of work (Fede: yes. He had approved it before Oct 31; the "awaiting his go" label here was wrong and the steps sat idle five days. Builders started Oct 31 under the grant): (1) hold lifecycle first: the retry that never ran on held first emails, and holds that re-alert at expiry; (2) facts in, with ids, on the decomposed prompt already in shadow; (3) declaration schema; (4) door 4 as one join, run in observe mode beside today's 56 lines for a week on recorded drafts, then the lines retire in one PR each as the compare and the bench show no new misses; (5) voice to retry-only; (6) registry and build check. Disagreement left open: fair housing as a hold (Opus) or alert-only (Fable); Fede said keep, so it stays a hold.
| Step | Done | Open |
|---|---|---|
| 1 Hold lifecycle | Fede's ruling Nov 2 replaced "fix the retry" with "no retry": a first email holds on any finding (#24379). A check-held text is now a regular Supervised review with the 20-hour warning (#24378); a held draft wrong as written never reaches the card (#24400). | Email half #24420 (opened 04:34Z, red: needs a rebase on #24400). Until it lands, a check-held email still expires silently at 24 h. |
| 2 Facts in, with ids | Open homes = homes on the market (#24125); facts block carries ids, trace records it (#24142); thread facts with ids for later turns (#24247). | Open-unit rent and size are not on the ids line yet (door 4 reads them observe-only). |
| 3 Declaration | Recorded on the trace (#24151); writer fills it, an action is backed only by a call that succeeded (#24161). | First email only; text turns get it in wave 2. |
| 4 Door 4, one join | Live in observe mode since Nov 1 (#24193); replay of 357 real first emails: today's checks fired on 111, door 4 would hold 1. Wave 2 folds landed (#24258, #24288). | Observe week runs to Nov 8; then the 56 lines retire one PR each as the compare and bench show no new misses. Live since Nov 1: 6 first emails through the join (2 Western Slope, 1 Situs, 3 sandbox), 0 would-hold, 0 findings. Too few to say anything about the hold rate yet. Both Western Slope emails (Sierra, Bre) were sent; Bre replied two minutes later. Emmanuel (Situs) was held by the send door for internal notes in the draft, approved by Fede at 04:11Z, and the email left at 04:11Z from the Situs company inbox (Gmail id 1a19a74784a6071e); the "delivery failed" flag on the hold was a false stamp from a text attempt and is being cleared. Text delivered 04:23Z. |
| 5 Voice retry-only | Dash check retry-only on text and email (#24169); a dash never holds a first email or follow-up (#24196). | none |
| 6 Registry + build check | Closed registry of every check that can stop, hold, retry or rewrite (#24172); build check fails a PR adding one without a row (#24187). 307 live rows. | Monthly demotion review not yet scheduled. |
Gaps the report named that are still open: 110 of 124 renter-facing holds expired unseen in the window; #24420 closes that for email. Fair housing stays a hold (Fede: keep).
No, Clara does not need 29 gates. 29 counts rule lines, mostly one idea coded again per incident. Clara needs four output gates: three code checks and one model judge. The rest belongs upstream, before the writer, at the send door or on the bench.
| Gate | Checks the draft against | Type | If it fires | Absorbs |
|---|---|---|---|---|
| 1. Facts match our records | The turn's facts block (each home's price, special, open or gone, date, slots), the thread, the turn's actions. Never "which tool ran" | Code | Wrong or unlisted fact: block, retry, hold. Missing must-say fact: retry, send | Fact trace, tour time this turn, weekday, price per size, gone claim, home counts, follow-up facts, hallucination guard, no tour times |
| 2. Promises match actions | Action record and open asks (booked, filed, sent, team told), both ways | Code; the writer lists its promises | Unbacked: block, retry, hold. Unsaid action: retry, send | Hand-off rewrites, booking promise, promise copies, work-order claim and confirmation, say-do mismatch |
| 3. Nothing internal leaks | One pattern list: stamps, tool-call markup, reasoning, prompt text, dashes | Code | Block, retry, hold; never strip | Stamp, tool-call, reasoning and prompt strips; dash checks |
| 4. Fair housing | Model judge behind a cheap word screen | Model | Flag, send, review same day | Today: holds first emails, flags replies |
| Not an output gate | What |
|---|---|
| shouldSpeak (before the writer) | Code only for one-answer lookups (opt-out, mute, unknown sender, Supervised, runaway, tapback). "A person is on it" is the model's call. Today nothing returns silent |
| Send door | Consent, quiet hours, live company, test recipient, duplicate, daily ceiling |
| Failure rule | Crash, timeout, empty reply: send nothing, alert |
| Upstream | One home's facts handed in, replacing forced lookups; tools take only real slot or home ids [1]. Fixes "this is dumb" |
| Bench only | Greeting, report shape, staff-note tone, size question, prior-contact wording, follow-up style |
Every real catch in 30 days is one of three:
One gate per idea covers them. Only fair housing needs meaning no pattern can see, so it alone is a model judge.
Facts are checked by code. Anthropic calls a model judge "generally not a very robust method" [2]; NVIDIA's own model-run fact check is "strongly dependent on the capability of the LLM" [9]; Sierra's code guards "can't be talked out of doing their job" [10].
Gate 1 is the crux. It asks whether a fact is true, never where it came from. Asking where caused the false holds: Patrick's own application link, and Trang's and Anthony's correct times.
The facts block is also the list of what Clara may say. Hand in one home, and any count or extra home is unlisted, so over-sharing needs no separate gate. With a complete block, gate 1 rarely fires. Gates come down by fixing data, not by deleting checks.
Severity sets the action [14]:
| Severity | Action |
|---|---|
| Harm: false fact, broken promise, leak | Retry, then hold (refrain) |
| Omission: a must-say fact left out | Retry, then send and log |
| Style | Log only |
Never fix (rewrite). No tour times, the busiest check (20 of 23 named fires), should never hold.
Fair housing holds on one path and flags on another. Flag and send everywhere until labeled cases prove it precise enough to block. Its one sampled fire looked false, and expired holds risk ignoring the prospects it protects. A cheap screen in front keeps the judge to a small slice, the cascade behind Anthropic's 0.05% production refusal rate [6].
Voice cannot wait, and today voice checks edit words. On calls, gates 1 and 2 live in the data and tools: the booking tool refuses a closed slot. Even the call error line, "get right back to you", is a promise that needs an owner.
Only a measured rate after that adds a row (fact type, action type, pattern) to an existing gate. Never a new code path.
Fix the lifecycle first. It multiplies every false fire. On held first emails the retry never ran (3 of 3), and 110 of 124 renter-facing holds expired unseen. Every fire records its gate and finding (Managed Agents keeps a reason code per decision [4]). Every retry runs. Every hold ends in a send, a person or an alarm.
Then five tests:
Right: the five ideas, and style to the bench.
The farewell loop and the shape checks can go.
Fetched directly in this review: 1 to 8, 9 (fact-checking page), 10, 12, 13 (engine page), 14 and 15. Read through research subagents and quoted as their fetches rendered them: 9 (GitHub page), 11, 13 (Procedures), 16, 17 and 18.
Internal evidence: gate-report.json and gate-report.html, hold-gates-inventory.html, the gates and core sections of the Clara coworker loop v3 page. Code was read at repo HEAD 604b8ac278: followUpFinding, the first-email draft checks, no-dashes, shouldSpeak, the anomaly review gate and the work-order confirmation.
Short answer: six doors, not 29 lines. Only one may hold a reply Clara wrote, for content. The leverage is before and at the writer, not after. Prose-pattern checks are why each incident adds a line.
| # | Door | Where | How it decides | Hold or silence? |
|---|---|---|---|---|
| 1 | shouldSpeak | before the writer | code lookups (opt-out, mute, unknown role, Supervised, runaway, tapback); the model judges "person on it, anything to answer" in the writing call, reason logged | silent with a reason; hold only where Fede set Supervised |
| 2 | Facts in, choices out | before the writer | code builds the facts block with ids (home, rent, size, open date, special, slots, policies, date, actions taken); the writer gets only this turn's tools | never: it removes the mistake |
| 3 | Declaration | at the writer | strict schema: the writer declares what it used, as facts-block ids (homes, slots, prices, facts, actions, asks, language) | never: the API guarantees the schema |
| 4 | Claims match the record | after the writer | ONE code join: declared ids exist in the facts block, actions in this turn's ledger, "with the team" has an open ask (askState); a leak list for undeclared times, money, "the team will", internal markup | the only content hold: retry once, then one hold record, review door and alert |
| 5 | Voice | after the writer | code: dash, greeting, renter's language, staff-note shape | never: retry once, then send; residual is a bench row |
| 6 | Send door | at send | code: consent, live, module on, test scope, ceiling, quiet hours; any error sends nothing and alerts; a Supervised hold re-alerts at expiry | refuse with a reason; hold with a record |
| judge | Fair housing | after the writer | separate model instance, alert-only as today | Fede's call |
Today's 56 lines:
| Today | Lines | Goes to |
|---|---|---|
| Provenance (first-turn lookup, tour-time-this-turn, forced tour/availability retries, stale slot, "gone" claim, fact trace) | 7 | deleted by doors 2 and 3 |
| Fact and promise shapes (fact rules, date, counts, tour times, home named, promises, work-order claim, follow-up facts, time stamp, markup) | 30 | door 4, as declaration columns and leak entries, not regexes |
| Style (dashes, greeting, staff-note, blast opener, size question, prior contact) | 12 | door 5, retry only |
| Silencers (runaway, mute, tapback, bare closer, farewell loop) | 5 | door 1; farewell loop deleted |
| Empty reply, anomaly gate | 2 | door 6; anomaly keeps crash and timeout only (retry once, else nothing); its five never-fired signals go to the bench |
The strongest guard is a removed choice before the model: Sierra's deterministic guards "can't be talked out of doing their job" (its example: no account tools before authentication). Every wrong correctness hold in the sample was a provenance check (which tool ran this turn); of two tour-time parsers, one missed "I can also do Thursday at 7:15" 10 of 10 times and the other held a correct French reply (it reads only English and Spanish). Wording never fixes a parser; a join on ids has none, so it is language-agnostic by construction. Only block-and-retry fits Fede's no-rewrite rule; the frameworks' "fix" actions are what he ruled out.
In code today: twelve inline blocks in the main loop, four outcome types, one real looped registry, retry-then-hold built three ways, eight hold sites stamping the wrong check name. Door 4 is that registry, generalized.
Severity is a property of the check, set in code, identical at every company, never a setting. Only door 4 holds a written reply; voice retries then sends: Opus writes a dash 2 in 72, one retry clears nearly all, and a held first email is a lost lead (110 of 124 renter-facing holds expired unseen).
No new production judge: the 006 judge stopped 22 of 28 real first emails without seeing the facts bundle; judges live on the bench, calibrated to Fede's verdicts.
An incident's first artifact is a reproduced case, never a check: a ledger row on the frozen replay plus a bench row, filed by cause (data missing, prompt ignored it, schema cannot express it, hold died, tool bug). Only "had the fact, declared it, still wrote something else" earns a change to door 4, as a column or leak entry with a registry row (id, join target, origin case, three controls, false-fire rate, status: hold, retry-only, observe, retired). A build check fails any PR adding a check without the row; a monthly review demotes a hold check whose false-fire rate crosses the line or that has not fired in 60 days (control green). This is Anthropic's evals guidance: failures become regression cases, graders deterministic where possible.
Does each check on Clara's messages still earn its place now that she writes with Opus 5.5, a split prompt, and the facts handed to her? Each row says what incident the check was built for, what it really caught in the last 30 days at Western Slope, Situs and JP&Co, and whether the new setup still makes that mistake. Samples: 72 bench first emails per arm, 55 tenant texts per arm, 12 Opus repair replies, and 006's 28-card first-email replay. Supervised holds, maintenance templates and fair housing are out of scope. Read only: nothing was changed or sent.
By check: keep 29, delete 7, fold 18, pending 2. By row: keep 15, delete 4, fold 5, pending 1.
| Gate | Built for | Real catches since | Does the new setup still do the thing | Verdict |
|---|---|---|---|---|
| First email: no tour times offered | Sep 28, a replay of 170 Camellia guest cards: emails ended on a size question and dropped the tour offer (#13351). | The busiest first-email check: 20 of the 23 named rule fires in 30 days (006). No renter-felt bug found. On 28 replayed cards it fired 4 times on production, 2 of them on emails staff approved as is. | Yes. Opus still trips it: 4 of 28, 1 on a staff-approved email (006). | Keep |
| First email: says how many homes are open | Oct 22, a Western Slope first email listed three 3-bedrooms open now and one opening later; Fede: never say how many are open (#21806). | Main loop redraft fired 3 times in 30 days (Western Slope 2, JP&Co 1). On 28 replayed cards: 9 production fires, 5 on staff-approved emails. | Yes. Opus 7 of 28 (5 on staff-approved, 006). On 72 bench emails 1 of 72 on each arm; the Opus one (case 019) named a three-home roster, a right fire. | Keep |
| No dashes (first email and main loop) | Oct 23 to 24, Fede on the review set: never an em dash, it is an AI tell (#22178, #22236). | Production first emails used dashes in 60 of 72 bench drafts. 006: 3 of 28 production fires, 2 on staff-approved. | Mostly fixed by the writer: Opus 2 of 72 bench emails, 0 of 28 (006), 0 of 55 tenant texts, 0 of 12 repair replies. Production tenant texts: 2 of 55. | Keep |
| First email: every fact traced to a source | Sep 25, Fede's goal for the Western Slope engine: zero invented fees, dates, specs or promises (#12074). | 1 real fire in 30 days (Western Slope, Patrick). It was WRONG: the check missed the application link Clara had sent herself. | Not measurable today: it needs the live source bundle the door does not return. | Keep |
| First email: fact-shaped rules | Sep 23 to Oct 19, Camellia and Western Slope replays: one price for all sizes, 'Unit 313' on a house, 'typically one month', 'nothing open' with no answer, 2.5 bath from marketing text, an invented pet rule, a past tour time. | 0 fires in 30 days for every one of them (006 production list). | Opus 0 of 28 on each (006). Each is a question of whether a sentence matches a fact the writer is now handed. | Fold |
| First email: shape rules Opus never trips | Sep 26 to 29, Western Slope replays: an email that named no home (#12744), a bare 'what size?' (#13568), 'just confirming' in a first message (#13568), 'I've noted that for the team' and nothing else (#12546). | names_home 2 fires, bare hand-off 1 (Cheryl, a test applicant, unclear), prior contact 1 (unrated), size question 0. On 28 cards names_home fired 3 times on production, all 3 on staff-approved emails (misfires). | No. Opus 0 of 28 on all four (006). | Delete |
| First email: reads like a staff note, not an email | Sep 23, 'I found the exact home the address matches' (#11252); Oct 25, a preventive check for the decomposed writer (#22314). | Not counted in the 30-day logs. | Yes. Opus wrote a staff report instead of an email once in 72 (case 023). Production prompt: 6 of 72 missing a greeting. | Keep |
| Weekday and date match | Sep 26, three real Western Slope sends said 'tomorrow, Friday September 19' with the date one day off (#12530, #12543). | 0 real fires in 30 days. | No. 0 of 72 on both arms, 0 of 55 tenant texts, 0 of 12 repair replies. Planted control fired 10 of 10. | Fold |
| Tour time named this turn must come from this turn's calendar read | Oct 16, the Willows test building, one text thread: Clara offered 12:00 or 2:30 after the calendar said not to list times, and a stale 4:30 (#20338; widened Oct 20, #21497; false holds fixed Oct 23, #22024 and #22036). | 0 real-customer fires in 30 days; 2 fires, both on the test property. Its own replay found one live-customer email (Oct 14) with an unbacked 2:00 PM. #22036 found 0 real prospects silenced (Western Slope 143 sent, Situs 1). | No. 0 of 72 on both arms, though 62 and 65 drafts name a clock time. Planted control fired 10 of 10, but only after the wording changed: 'I can also do Thursday at 7:15' slipped past (0 of 10). | Fold |
| Forced retries: tour-time claim and availability claim | Tour time: Sep 30, Western Slope email 7ddd98a6 (#14124). Availability: Sep 19, Camellia said units 420 and 524 were open, both occupied (#10579). | Logged in 30 days: tour time Western Slope 10 (5 conversations), JP&Co 8 (4); availability Western Slope 10 (4), JP&Co 8 (2). Neither fired on either bench arm. | 006 report 4 (#gate-claim-gates): tour-time retry, 9 customer fires in 90 days, 4 real catches (invented "10 AM or 2 PM" twice at Camellia, a past Monday and stale windows at Western Slope), 4 moot; Opus read the calendar itself 14 of 29 turns vs Sonnet 5 of 29 and named no time without a read. Availability retry, 10 fires, 0 real catches, 7 misfires (5 where the forced tool was the calendar so the unit was never checked); Opus 0 unbacked unit claims. Every fire was Sonnet, forced tool ran 63 of 63, median 7 s added, never a hold. | Keep tour-time as the one calendar rule inside the facts check, re-decide after a week of Opus snapshots; delete the availability retry, keep its log line |
| First turn must look things up before answering | Apr 8, a bare Zillow lead: Clara skipped her tools and invented price ranges (no PR). | 0 real-customer fires in the pulled 30-day logs. | Yes. Opus skipped the lookup 3 of 72 times; production prompt 17 of 72. The retry fixed every one. | Keep |
| Typed tool-call text stripped from the reply | Aug 2, an eval bug where tool-call markup landed in a test tenant text (#5257). Not a customer incident. | 0 real fires in 30 days. | No. 0 of 72 on both arms, 0 of 55, 0 of 12. Planted control 10 of 10. | Delete |
| On error, send nothing | Oct 1, residents were texted engineering fallback text; Fede: if we error, we don't answer (#14348). | Not counted as a fire; it is the failure path. | Not model-dependent. | Keep |
| Internal time stamp stripped from the reply | Oct 1, a Western Slope prospect texted 'I'm here' and the reply began with '[Sent Thursday, October 1...]' (#14874). | 6 real catches in 30 days (Western Slope 4, JP&Co 2), all right: the stamp would have reached the person. | No. 0 of 72 on both arms, 0 of 55, 0 of 12. Planted control 10 of 10. | Keep |
| Hand-off promise with nobody reached | Sep 5 to Oct 20, Western Slope cards: 'the team will be in touch' when no one was told (#7119, #11980, #12370, #21395). | 3 real catches in 30 days (Western Slope 2, JP&Co 1), all looked right. 006 audit (report 8, Oct 26 eve): the template swap fired 22 times in 30 days (18 replaced, 4 released), 3 at customers: 1 real catch (JP&Co Oct 13), 1 false 'noted for the team' sent when nobody was notified (JP&Co Oct 6), 1 working answer thrown away (Western Slope Sep 27). Of the 18 replacements: team notified 7, logged but not delivered 8, nothing opened 3. The leasing prompt itself mandates the wording the gate punishes. | No on 72 Opus emails (0), 55 Opus texts (0), 12 repair replies (0). Production tenant texts: 1 of 55. Planted control 7 of 10 (the 3 misses had a real hand-off tool call, so they were correct passes). | Fold: delete the template swap; door 4 'promises match actions' allows the promise when the open-ask record shows an ask opened, retries once then holds when nothing opened; the answer beside the promise ships. Full audit: #gate-handoff-template. |
| Other unbacked-promise checks | Aug 19, a Willows bench prospect asked about a felony and heard 'the team will be in touch' (#5915); Oct 18 replay, 'I'll check with the team' on six cards (#21085); Oct 25 send gate (#22296). | Send gate 1 (JP&Co, Ibrahim, unclear). First-email copy 1 (unrated). | No. Opus 0 of 28 (006), 0 of 72, 0 of 55. | Fold |
| Tour promised or confirmed but not booked | Sep 18, 'I'll get it locked in for you' before booking (#9635); Sep 30, 'See you at 1' after nothing was booked (#14353). | 7 real catches in 30 days (Western Slope 3, JP&Co 4). The redraft cleared 6; 1 JP&Co reply was not cleared. 006 audit (report 5, Oct 26 eve): the phrase guard itself ('lock in' list, rewrite until Oct 25, block-only since #22296) had 0 real catches in 9 customer fires; 4 of its rewrites made the reply worse and 2 sent a stray quote mark. The 7 real catches above come from the say-do check (promise with no booking on record), not the phrase list. | No. 0 of 72, 0 of 55. Planted control 10 of 10. | Fold: keep 'tour confirmed only when the records show one' inside door 4 (promises match actions); delete the phrase list. Full audit: #gate-booking-promise. |
| Claims a work order was filed with none filed | Oct 1, a flaky renewal eval, not a customer (#14864). | 0 real fires in 30 days. | No. 0 of 12 Opus repair replies with filed actions, 0 of 55, 0 of 72. Planted control 10 of 10. | Delete |
| Says it did something no tool did | Apr 27, 'Done, I'll flag this for PM' with no escalation fired (#485). | 1 real catch in 30 days, right: JP&Co, Will, Sep 30, said '1 PM works' while the tour was at 4:45 PM. | Not measurable today: needs the full live tool trace. | Keep |
| Anomaly review | Jul 17, a Camellia tour incident (#4061). | 10 fires in all kept logs, 1 real and right (Ember, an empty reply). Its content signals never fired (006). | Not replayable: voice turns and tool crashes cannot be rebuilt (006). | Keep |
| Follow-up texts: fact-shaped checks | Jul 14, a follow-up invented 'our studios and two-bedrooms' and 'Tuesday at 10 AM' (#3762); Jul 16 pricing (#3936); Oct 22 availability (#21806). | 1 real catch in 30 days, right: Jessalyn at Pomona Park was told '2-bedroom' where every home is 3 or 4 bedrooms (unit-type). | Not measurable today: follow-ups have no decomposed writer yet. | Fold |
| Follow-up texts: dash and mass-text opener | Jun 19, no 'Hi there' mass-text opener (#2509); Oct 23 no-dashes rule (#22236). | 0 real fires in 30 days. | Not measurable today: no decomposed follow-up writer. | Keep |
| Silencers: runaway ceiling, muted person, tapback, bare closer | Jun 13, Willows and Camellia texted each other for hundreds of turns (#2341); Aug 28 to 30, Camellia replies to a thumbs-up and to 'Thank you' four times (#6519, #6552); Aug 28, Fede's per-person mute (#6526). | Bare closer 6 in 30 days (Western Slope 2, Situs 2, JP&Co 2). Runaway 9, all sandbox. Muted and tapback 0 found. | On all 5 inbound closers and tapbacks in the 55-text sample, both production and Opus chose to stay silent on their own. Runaway, mute and tapback run before the model. | Keep |
| Silencer: farewell loop | Jul 30, a plumber's auto text and Clara traded 11 rounds of 'Take care!' (#5047). | 0 fires found in 30 days. | Both models ended the conversation on their own on all 5 closers in the sample. | Delete |
| Email greeting and footer added after writing | Mar 20, the first email reply feature (no incident). Sep 23, two greetings stacked in a held draft (#11371). | Not logged. | Opus left out the greeting once in 72 (the staff-report case). | Keep |
Fold: First email: fact-shaped rules. Each rule restates one fact that now sits in the facts block. One check that compares every sentence to that block covers all seven, and none has fired in 30 days.
Delete: First email: shape rules Opus never trips. These were built for the production prompt's habit of thin, odd first emails. The decomposed writer is handed the home and told it is a first message; it never produced these shapes, and the one with real fires misfired every time.
Fold: Weekday and date match. Today's date is a fact in the facts block. A date check belongs inside the one facts check, not as its own gate.
Fold: Tour time named this turn must come from this turn's calendar read. Fede's hunch holds. It was built from one test-line thread, has never fired on a real customer, and twice held correct replies on the test line. The real rule (a named time must be in the calendar read) is a fact match, so it moves into the one facts check.
Delete: Typed tool-call text stripped from the reply. Built for an eval-provider bug, never a customer, and a rewrite layer Fede ruled out. If the markup ever shows up, the empty-reply rule (do not answer) is the right response, not an edit.
Fold: Other unbacked-promise checks. Three copies of the hand-off promise check above, with no right catch between them. Fold them into that one check.
Delete: Claims a work order was filed with none filed. Built for an eval flake, never a customer, never fired. The work-order confirmation rule (#16358) covers the real case.
Fold: Follow-up texts: fact-shaped checks. Each one compares a sentence to a fact (price, policy, size, slot, count). They belong in the one facts check once follow-ups are written from the facts block.
Delete: Silencer: farewell loop. The model now ends these itself, and the runaway ceiling still stops any bot-to-bot loop.
Sources: first-email replay and anomaly numbers from 006 (#gate-first-email-checks, #gate-anomaly-review, #gate-supervised-hold). Origins verified with git log and the PR bodies. 17:16Z manual rerun rows excluded. Case ids in gate-report.json.
Read-only, built Oct 26 2026. Window Sep 26 to Oct 26 (UTC). Western Slope, Situs Group and JP&Co (Camellia) only. Every row is a time an output check stopped, held, silenced or forced a retry of a Clara message. Raw rows: gate-ledger-30d.json (scratchpad of session 001).
| Door | Fires | Right | False | Unresolved |
|---|---|---|---|---|
| Door 1, should Clara speak | 43 | 12 | 0 | 31 |
| Door 4, claims match the record | 78 | 4 | 14 | 60 |
| Door 5, voice | 0 | 0 | 0 | 0 |
| Door 6, send door (+123 Supervised holds, not judged) | 5 | 0 | 0 | 5 |
| Fair-housing judge | 1 | 0 | 1 | 0 |
| All doors | 127 | 16 | 15 | 96 |
What it says. Of 31 fires with readable words, 16 stopped a real mistake and 15 stopped a fine message. Door 4 is where the false ones live: 14 false against 4 right, and every false one judged where a fact came from, not whether it was true (fact trace, tour time from this turn, promise backing, the "lock in" phrase list). The right ones are plain record checks: wrong tour time, empty draft, a home size that does not exist, bare thank-yous left alone. 96 fires are unresolved because the log keeps only that the check fired, never the words it stopped; that gap is itself a finding for the hold record (one record, with the check name and the words).
Doors. 1: silencers before the writer (shouldSpeak said "reply" on all its lines, so 0 fires). 4: fact, provenance and claim checks, including forced-tool retries. 5: no fires recorded. 6: send-door refusals; Supervised holds are a setting, counted but not judged.
| Date | Customer | Channel | Check | What the message said | What happened next | Fires | Verdict |
|---|---|---|---|---|---|---|---|
| 2026-10-04 | Western Slope | first email: fact trace (fact_trace_untraced) | Carmen, Pomona Park: draft said "I'm not able to pull up live tour times" | held; later rerun and sent | 1 | RIGHT Slots were on hand; saying none could be pulled is a false claim. | |
| 2026-10-04 | JP&Co | first email: implies-prior-contact (forced redraft) | Will, Camellia: first draft not kept | redrafted, then the fair-housing judge held the redraft (row below) | 1 | UNRESOLVED Pre-redraft text is not stored. | |
| 2026-10-05 | Western Slope | first email: draft check (first_email_check_failed) | Robert, Northridge: held draft not kept | held; reruns then sent four different first emails in 21 minutes; Oct 7 review rerun expired unseen | 1 | UNRESOLVED Held words are not stored; only the Oct 7 rerun draft is. | |
| 2026-10-21 | Western Slope | first email: fact trace (fact_trace_untraced) | Morgan, Pomona Park: "Unit 405 runs $2,495/mo, which is above the $1,800/mo budget you mentioned... 585 Redcliff Cir, #201 ... listed at $1,750/mo" plus tour times | rerun into review, expired unseen after 24 h; nothing sent | 1 | FALSE Every fact is on file: $1,800 budget on her card, #201 vacant and listed at $1,750. The trace judged where the fact came from. | |
| 2026-10-25 | Western Slope | tour time not from this turn's calendar read | Anthony, 2841 Morningside Court: first draft offered the slots the first email was handed | retry told "do not name any tour time" | 1 | FALSE The slots were real; the gate did not count the handed-in slot read (fixed by #22485, Oct 26). | |
| 2026-10-25 | Western Slope | first email: no tour times offered (on the retry) | Anthony, 2841 Morningside Court: "Thanks for reaching out about 2841 Morningside Court, it's available now, a 4 bedroom, 2 bath with 1,518 sq ft at $2,300/mo. I'd love to get you in for a tour: what day and time work best for you?" | held; staff got an "I couldn't get a clean reply out" email; nothing sent to him | 1 | FALSE Sendable email; the missing times were caused by the previous gate's own retry instruction. | |
| 2026-10-26 | Western Slope | tour time not from this turn's calendar read | Trang, Ember Estates: first draft offered the slots the first email was handed | retry told "do not name any tour time" | 1 | FALSE The slots were real; the gate did not count the handed-in slot read (fixed by #22485, Oct 26). | |
| 2026-10-26 | Western Slope | first email: no tour times offered (on the retry) | Trang, Ember Estates: retry draft without times | held; rerun into review, approved by hand, sent 15:28Z (about 47 min late) | 1 | FALSE Sendable email; the missing times were caused by the previous gate's own retry instruction. | |
| 2026-10-26 | Western Slope | first email: fact trace + names-home (draft check) | Patrick, Northridge: "I've sent the application for Northridge Apartments, it'll come from AppFolio in the next few minutes." | held; Kat replied to him by text at 15:38Z | 1 | FALSE True: the application went out at 15:35:30Z; the trace did not count its own turn's tool. | |
| 2026-09-30 | JP&Co | text | hallucination guard (parked) | Will, Camellia: "1 PM works" when the tour on file was 4:45 PM | parked, nothing sent | 1 | RIGHT Wrong tour time against the tour record. |
| 2026-10-22 | Western Slope | anomaly gate (agent turn timed out) | Ronald, Ember Estates: empty draft | held; a retry 18 s later sent a reply listing units 361, 363, 364 and a Nov 2 opening (the Ember overshare incident) | 1 | RIGHT An empty draft should never send. The harm came from the retry path, not the gate. | |
| 2026-10-26 | JP&Co | send gate: unbacked_promise_in_retry | Ibrahim, Camellia: "Signing the lease before you arrive in the US isn't something I have on file, so that's one the property team can go over with you directly... would a virtual tour work in the meantime? I'm happy to set one up" | parked, nothing sent; no Slack page | 1 | UNRESOLVED Depends on whether Camellia offers virtual tours and whether a team ask was filed; not checked. | |
| 2026-10-26 | Western Slope | send gate: promise-backing | Austin (applicant, Beverly reviewing): "I've passed along the references for Terry Dwayne and Stacey Farney, along with the 2019 to 2020 rental history..." | parked; the team notice with the same facts was posted in the same second | 1 | FALSE The pass-along really happened (team notice 17:01:42.9Z). Separate issue: the office was running this thread (door 1). | |
| 2026-10-26 | Western Slope | text | cadence unit-type check (held after retry) | Jessalyn, Pomona Park: "...a tour of our 2-bedroom homes still interests you" | held, still pending | 1 | RIGHT Every Pomona Park home is 3 or 4 bedrooms. |
| 2026-10-24 | JP&Co | unit count stated (forced redraft) | (pre-redraft text not kept) | redrafted, then sent | 1 | UNRESOLVED Log line only. | |
| 09-26..10-26 | Western Slope | unit count stated (forced redraft) | (not kept) | redrafted, then sent | 1 | UNRESOLVED WS 2 lines in the Oct 26 pull; one is the Cheryl test lead (excluded). | |
| 09-26..10-26 | Western Slope | text/email | booking-promise phrase ("lock in") | (pre-rewrite text not kept) | regenerated and sent (3) | 3 | FALSE 006 audit, Oct 26: 0 real catches in 9 customer fires; 4 rewrites made the reply worse. |
| 09-26..10-26 | JP&Co | text/email | booking-promise phrase ("lock in") | (pre-rewrite text not kept) | regenerated and sent (3), 1 not cleared and blocked | 4 | FALSE Same audit as above. |
| 09-26..10-23 | Western Slope | first email draft checks (redraft era, before Oct 24) | (pre-redraft text not kept) | redrafted, then sent | 17 | UNRESOLVED Pre-redraft words are not stored. | |
| 09-26..10-23 | Western Slope | first reply named no home (redraft) | (not kept) | redrafted, then sent | 15 | UNRESOLVED Same. | |
| 09-26..10-23 | Western Slope | fact-trace sentence strip | (not kept) | sentence cut, then sent | 5 | UNRESOLVED Same. | |
| 10-24..10-26 | Western Slope + Situs Group | first email check, retry passed | (not kept) | sent after one retry | 3 | UNRESOLVED WS 2, Situs 1; first draft not stored. | |
| 09-26..10-26 | Western Slope + JP&Co | text/email | tour-time claim with no calendar read (forced tool retry) | (not kept) | redrafted with a forced lookup, then sent | 9 | UNRESOLVED WS 5 turns, JP&Co 4; the draft before the forced lookup is not stored. |
| 09-26..10-26 | Western Slope + JP&Co | text/email | availability claim with no lookup (forced tool retry) | (not kept) | redrafted with a forced lookup, then sent | 6 | UNRESOLVED WS 4 turns, JP&Co 2. |
| Date | Customer | Channel | Check | What the message said | What happened next | Fires | Verdict |
|---|---|---|---|---|---|---|---|
| 2026-09-27 | Western Slope | sms | bare closer skip | Inbound: "Thank you." | nothing sent | 1 | RIGHT Bare thanks or goodbye; a reply would be noise. |
| 2026-09-29 | Western Slope | sms | bare closer skip | Inbound: "Thank you!" | nothing sent | 1 | RIGHT Bare thanks or goodbye; a reply would be noise. |
| 2026-10-01 | Western Slope | sms | bare closer skip | Inbound: "See you then!" | nothing sent | 1 | RIGHT Bare thanks or goodbye; a reply would be noise. |
| 2026-10-03 | Western Slope | sms | bare closer skip | Inbound: "Okay thank you" | nothing sent | 1 | RIGHT Bare thanks or goodbye; a reply would be noise. |
| 2026-10-13 | JP&Co | sms | bare closer skip | Inbound: "Okay, thank you. See you soon!" | nothing sent | 1 | RIGHT Bare thanks or goodbye; a reply would be noise. |
| 2026-10-16 | Western Slope | sms | bare closer skip | Inbound: "Thank you so much!" | nothing sent | 1 | RIGHT Bare thanks or goodbye; a reply would be noise. |
| 2026-10-19 | JP&Co | sms | bare closer skip | Inbound: "Thank you!" | nothing sent | 1 | RIGHT Bare thanks or goodbye; a reply would be noise. |
| 2026-10-19 | Situs Group | sms | bare closer skip | Inbound: "Thank you" | nothing sent | 1 | RIGHT Bare thanks or goodbye; a reply would be noise. |
| 2026-10-20 | Western Slope | sms | bare closer skip | Inbound: "thank you so much" | nothing sent | 1 | RIGHT Bare thanks or goodbye; a reply would be noise. |
| 2026-10-20 | JP&Co | sms | bare closer skip | Inbound: "Thanks!" | nothing sent | 1 | RIGHT Bare thanks or goodbye; a reply would be noise. |
| 2026-10-21 | Situs Group | sms | bare closer skip | Inbound: "👍" | nothing sent | 1 | RIGHT Bare thanks or goodbye; a reply would be noise. |
| 2026-10-24 | JP&Co | end_conversation (model chose silence) | Inbound: "Gracias ! Yoira E. El 23 oct 2026, a la(" after her tour was confirmed | nothing sent; tour reminder still went out next day | 1 | RIGHT Bare thanks after a confirmed tour; nothing to answer. | |
| 09-26..10-26 | Western Slope | text/email | end_conversation (model chose silence) | (not kept: log line only) | nothing sent | 5 | UNRESOLVED Counted in the Oct 26 pull; lines carry no inbound text and were not re-found by id. |
| 09-26..10-26 | Western Slope + JP&Co | text/email | conversation already ended | (not kept) | nothing sent | 6 | UNRESOLVED WS 3, JP&Co 3; log line only. |
| 09-26..10-26 | Western Slope | text | staff sender (a staff member texted the line) | (not kept) | nothing sent | 14 | UNRESOLVED 14 lines, all one conversation; likely right but not read. |
| 09-26..10-26 | JP&Co | resident scope (resident wrote the prospect inbox) | (not kept) | skipped, parked | 3 | UNRESOLVED Log line only. | |
| 09-26..10-26 | Western Slope | text | follow-up cadence stop | (not kept) | touch skipped | 3 | UNRESOLVED Temporal log only. |
| Date | Customer | Channel | Check | What the message said | What happened next | Fires | Verdict |
|---|---|---|---|---|---|---|---|
| 09-26..10-26 | Western Slope | text/email | company not live | (not kept) | held, not sent | 5 | UNRESOLVED Western Slope is live, so these 5 lines need reading; not re-found by id. |
Supervised holds, one JSON row each: maintenance_resident_sms expired unreviewed: 106; maintenance_resident_sms approved and sent: 5; maintenance_resident_sms still pending: 5; team_notice_email approved and sent: 2; maintenance_resident_sms rejected: 1; maintenance_vendor_sms expired unreviewed: 1; clara_prospect_email expired unreviewed: 1; maintenance_resident_sms approved, delivery failed: 1; clara_prospect_email still pending: 1. Maintenance holds are Gera's lane (Fede, Oct 26).
| Date | Customer | Channel | Check | What the message said | What happened next | Fires | Verdict |
|---|---|---|---|---|---|---|---|
| 2026-10-04 | JP&Co | fair-housing judge (fair_housing_hold, "familial status") | Will, Camellia: redrafted first email for a one-bedroom, west-facing, floor 3+ ask | held; rerun sent 34 min later (22:41Z) with tour times and unit 220 | 1 | FALSE Nothing about family in his ask or the draft (Oct 26 inventory read the held text as fine). |
Left out (listed in the JSON): test companies, the Cheryl test lead, manual reruns, alert-only fair-housing flags, cadence lines with no company, rewrites that forced no retry. Unresolved: the held words of Robert and of Will's first draft are not stored; Ibrahim needs Camellia's virtual-tour policy; count-only rows need text the log never kept. Sources: hold, intent, message and unit rows read by key; CloudWatch; Slack held cards; the Oct 26 gate report.
The case. Nov 1, 4:04 PM MT: Clara's call summary about a resident's cleanup call went to the Situs Group leasing inbox. Nov 2, 7:43 AM MT: a Situs org admin replied to it, To Clara, Cc the leasing inbox: "let's call and explain to him that we have the city inspection and need it cleaned up today." Clara answered him alone 16 seconds later: she could not place calls and did not know who "him" was. Fede: the team will reply-all on these notices to add teammates and tell Clara what to do; Clara must keep the context of her own message and answer basic questions about it, through the one coworker loop, no separate path.
Root cause, from the raw records (session 006, read-only).
| Step | What happened | Why |
|---|---|---|
| 1. The reply matched the notice | The team-notice reply door found Clara's notice by its message id (In-Reply-To) within a second | Working as built (ADR-0146 §2) |
| 2. The door refused the sender | "neither a recipient nor staff", so the reply fell to ordinary triage | The staff check reads the notice's property mailbox, and a call-summary notice carries no property (company, person and the call only), so the check answered "not staff" before looking at his role. He is an active org admin on the company record. |
| 3. A second door took it | The new staff email turn (merged that morning, #24426) recognized him as staff and ran a coworker turn | Working as built, but it minted a brand-new conversation ("no canonical thread matched"), so Clara saw one line and none of her own notice, the call or the caller. |
| 4. It sent | No hold rule fired (Situs staff notices are Live); the reply went to him alone, dropping the leasing inbox from the thread | The staff turn replies to the sender only and does not keep Cc. |
Second gap, from the code: when the door does accept a staff reply, it reads the words with the one staff-input reader; an answer is relayed to the person on the notice's own conversation (under Supervised, held for Slack review) and "handled" closes the notice, but a question or a note returns "not an answer" and nothing happens. So a teammate asking Clara "what was this call about?" gets silence today.
BEFORE staff reply ──► notice door: "not staff" (no property on the notice) ──► triage ──► staff email turn, NEW conversation ──► blind reply to sender only
AFTER staff reply ──► notice door: staff by the company's role record ──► the notice's own conversation (call + Clara's notice in history)
├─ answer ──► Clara relays to the person (Supervised: held, Slack review) (exists)
├─ handled ──► notice closed, nothing sent (exists)
└─ question / note ──► the coworker turn on that conversation, reply-all on the thread, Cc kept (new)
└─ unclear ──► held, Supervised review card on Slack, no guess (new)
Proven live, Nov 2, 11:10 AM MT, Fairhaven test company (Willows testing is closed by Gera today). Same shape as the Alton case: a hand-off notice with no property, a staff test mailbox that was not on the notice replies with a question, reply-all to the original recipient.
| Before (Nov 2, 7:43 AM, Situs Group) | After (Nov 2, 11:10 AM, Fairhaven) | |
|---|---|---|
| Staff check | "neither a recipient nor staff; left to triage" | recognized by company role, "team reply routed to the engine" |
| Where Clara thought | a brand-new conversation, nothing of the call in view | the call's own conversation (10 messages before, 12 after: the staff reply and Clara's answer) |
| What she said | could not place calls, did not know who "him" was | what the caller asked for, the two matches offered, and that nothing was promised |
| Who got it | the sender alone, team dropped | the sender, with the original recipient still on Cc, threaded on the notice |
| Time | 16 s | 34 s from send to delivered reply |
| Fix 3, plain staff email (Nov 2, 6:54 PM MT, Fairhaven) | Situs, 5:40 PM: a staff reply to Clara's virtual-tours intro (random message id, no notice row) matched nothing; a fresh conversation, Clara wrote that nothing had been sent | A tour-confirmed email to the team went out tagged (notice tn_38a9212b, kind fyi, source staff_email); a staff test mailbox not on the email replied with a question; the reply found the email's own thread (conv-staff-mail-db97f4e9…) and Clara answered there with the tour, the time and its cancellation in view; 43 s, nothing held. Two side findings filed as issues: Clara's own answer is tagged again, so a second reply lands on a split thread; the first turn logged the sender as identity unknown and opened a matter. |
Evidence: inbound-processor log 18:10:19 to 18:10:43Z (reader outcome "question", notice-thread "answered on the thread (sent=true, held=false, cc=1)", action coworker_turn; no "minting a new conversation" line); conversation conv_voice_c59a5d61… rows at 18:10:10 (team reply) and 18:10:31 (Clara, staff-only); the reply in both test inboxes at 18:10:46Z with In-Reply-To and References chaining to the notice's reply key. One side finding filed as #24536: the reply opened with a "Property:" line printed twice, taken from the conversation's property although the notice had none.
The one mechanism. No new path. A staff reply to any notice Clara sent lands on the conversation the notice came from, where Clara's notice is already recorded as her own message. The one reader says what the words are; answer and handled do what they do today; a question or a note runs the ordinary coworker turn on that conversation, so Clara answers from the call, the person and her own notice, and replies on the thread with everyone who was copied. A reply-all that adds a teammate works the same way: the thread id matches the notice, and the sender is checked against the company's staff roles.
| PR | Change (each a fix to live behavior, no switch) | Proof |
|---|---|---|
| 1 (#24489, merged Nov 2) | The notice door checks the sender against the company's staff roles (the same lookup the staff email turn uses), with the property-mailbox check kept as the second chance. Fixes step 2 and the reply-all case. | Read-only replay of the Nov 2 reply shape (notice without a property, org-admin sender); Willows live: a real hand-off notice to a tenant test mailbox, a staff test mailbox replies, the reply lands on the call's conversation. Test mail from the Microsoft tenant only. |
| 2 (#24494, merged Nov 2) | A reply read as a question or a note runs the coworker turn on the notice's conversation and answers on the thread, Cc kept, hello@ copied. A reply the reader cannot place (unclear) is held into the Supervised review card, never answered with a guess (Clara Refactor, Nov 2: gates hold, never compensate). Fixes steps 3 and 4 and the "what was this about?" gap. | Willows live: a staff test mailbox asks "what did he call about?" on a notice thread; Clara answers from the call with the team still on the thread. |
| 3 (#24534, merged Nov 2) | Every staff-facing email Clara sends becomes a notice a reply can find: the send is stamped with a reply key and a notice row (kind fyi) points at the staff conversation that already holds Clara's email. Needed by a second real case the same afternoon (Nov 2, 17:40 MT, Situs Group): a staff member replied to Clara's virtual-tours intro, which had a random message id and no notice row, so the reply fell to the staff email turn, which started a fresh conversation and Clara wrote that nothing had been sent on the thread. The other half of that case, staying quiet when a staff member addresses a named colleague on a thread Clara is only copied on, ships from the Situs Onboarding session inside the one speaking decision. | Read-only replay of the Nov 2 intro send and reply shape; Fairhaven live (Willows testing is off): a staff test mailbox replies to a Clara staff email that is not a hand-off, and the reply lands on the conversation holding Clara's email. Proven Nov 2, 6:54 PM MT, row below. |
Owners: 006 builds PR 1 and 2 under Fede's Nov 2 go; the Clara Refactor session holds the staff loop and the one reader (staff-loop-v2 §12) and reviews the cut. Sources: inbound-processor log group Nov 2 14:43Z, the stored notice row (kind handoff, no property), the S3 copy of the reply's headers, team-notify/inbound-email-reply.ts, team-notify/ingest.ts, agents/clara/lib/email/staff-email-turn.ts. No renter name or number appears here.
This design grew into one framework for every ball in the air, and it now has its own page: Follow-through (Proposed, pending Fede's review). Every signal goes through one read that decides what Clara says now and who owes what: Clara does her own balls, a teammate's ball closes on evidence of what people did, and everything else lands by itself. The first build is the next-morning re-engage after a missed tour, behind a company setting that is off by default, and the seven decisions are on that page.
Ownership. The follow-through page owns the ball record, the read's ball output and what goes on the list. Clara Refactor (001) owns the shared "load the turn's context" step, and the read is a caller of it, not a second loader. Situs external-updates owns the PMS inputs the read needs (current assignee, showing status, staff-booked tours). The scope resolver owns who someone is, by roles at the company, with or without a building.
Proof. A read-only replay of real Situs and Western Slope records first, with the read's verdict per thread logged and nothing sent; then a Fairhaven live run with record ids; then a supervised week at one customer, on Fede's word. Willows testing is off. Fixtures: the five Situs cases from the first pass, each with a control where the read must stay quiet.
Today one large prompt, picked by who is writing, drafts every reply, and code the model never sees patches it afterwards.
Fede, Oct 25: the foundation was written on early models, with a weaker model in production. It grew a prompt of negative examples (“don't do this, don't do that”), a new rule after every incident, and contradictions. Objective: clean it up and shrink it.
We rebuild it as one shared engine under every module, so each decision (reply or not, who to ask, what is already in hand) is made in one place.
shouldSpeak; about 13 paging paths, two owner lookups and every module's own fallback become the one notice engine (askTheTeam; internal failures go to #alerts, never to the customer or their staff); the unchecked "it's with the team" sentence becomes askState; AppFolio literals become the PmsInbox adapter; each module's own hand-off becomes one record and one state. Labels from the modules table.One example runs through every row: a tenant texts "the kitchen sink is leaking, unit 4B".
| Box | What it is | On the example |
|---|---|---|
| Turn assembly | Gather everything Clara needs before she thinks: who this is, their home, their lease, open work orders, what staff already said in this thread. | Finds Maria in 4B, sees no open plumbing order, sees nobody from the office has replied yet. |
shouldSpeak | The one question: reply, hold, or stay silent, and why. | Reply: a tenant, nothing open, nobody on it. |
| Notice engine | The one place Clara asks for a person: a question to staff, a hand-off, a hold card; failures to the alerts channel. | If the leak were a flood at night, this is what pages the emergency line. |
| The record | What Clara writes down: the work order, the ask she made, the offer, and a step-by-step ledger of what she did. | Files one work order for 4B, logs "replied, filed WO 1234". |
| Proof rail | How we know she behaves: the frozen sets (the 5,515 decisions), three arms compared (production, production on a new model, decomposed prompt), and the judge. | Tonight's replay and the shadow cards. |
PmsInbox | The door to the customer's property software, written once as a neutral interface; AppFolio is one plug-in behind it, Yardi could be another. | Reads Maria's text from the mirror and writes the work order back. |
"Full context (hypothesis)" means we believe giving the model the full record instead of a slice fixes many bad replies, and it is being tested, not assumed.
Today these six exist as scattered copies (the duplication table above). The lane makes each exist once, in src/lib/domain/core/, one file per box. Modules import from core and core never imports from a module. Nothing outside the PMS folder names a PMS.
How Fede and Gera avoid adding duplicates:
Proposed, pending review Refactor the base first (Fede, Oct 25): reusable interfaces, common things in one place, no module with its own hand-off or escalation. Lesson: renewals and leasing were built separately.
Code decides what each turn is and gathers the facts; the model only writes, and checks only block.
Prompt note (006, report 8, Oct 26). Today's leasing prompt orders the writer to say "I'll check with the leasing team and get back to you" on every forward, and the hand-off template gate then punishes that very line unless a flag is set. In the thin prompt, part 1 (hard rules) carries one hand-off sentence, and it should be written from the facts block: say what you did ("I asked the team about X") only when the ask actually opened (the action list in part 5 shows it as notified or logged); when nothing opened, say nothing about the team. The open-ask record then checks the sentence at door 4. Audit: #gate-handoff-template.
One central ask-for-help and notice engine (Fede, Oct 25, after a Western Slope first email failed its draft check and the fallback emailed the customer's staff asking them to reply): every hand-off, hold, alert and fallback in every module goes through one piece of the core. No module invents its own. An internal failure (a draft check, a tool error, a send that could not go out) never reaches the customer or their staff; it goes to #alerts and to the record, and Clara stays quiet. The Escalations session's full audit of today's paths (who each one reaches) lands as its own section on this page. A turn that does not send never raises a team notice on its own (Austin, Oct 26: a held draft still emailed the team; that path closes with the office-thread fixes).
Recipient by cause, one rule (Fede, Oct 25: "deep inspection of all spread out logic of holds or engineering alerts that notify the customer vs engineering, folded into the overall Clara re-architecture"):
| Cause | Who hears about it | What the renter or staff see |
|---|---|---|
| Engineering error: a draft check failed, a tool or send errored, a record could not be written, a timeout | #alerts and the record only | Nothing. No reply, no staff email, no hold card. Clara stays quiet and the ask stays open on her side. |
| Clara cannot help: the fact is not on file, the policy is missing, the request needs a person | The hand-off framework: one tracked ask to the owner, through askTheTeam | The renter gets a reply only if it helps; staff get one notice, never a second for the same thing. |
| A person is already on it (reasoned from the ask and topic against the staff thread, never a time cutoff) | Nobody | Nothing. shouldSpeak returns silent. |
| Critical emergency (fire, flood, no heat, lockout) | The emergency path the customer set up, plus #alerts | The renter gets the emergency line; staff get the page. |
Inspected Oct 25 Every place in the code where a hold, a draft check, a hand-off, a fallback or an engineering error decides who hears about it. Twenty-two paths, read from the code on Oct 25, 2026 (Escalations session, after the Western Slope 2841 Morningside Court case). "Rule row" is the row of the table above each path should resolve to. "Fixed" means PR #22417, merging Oct 25, no switch, live everywhere.
The short version: one shared engine already exists for staff notices (the team-notify engine, one recipient per company, one row per event) and the voice path already sends engineering failures to #alerts only. The holes were all in the text and email paths and in one leasing caller: an engineering failure was spelled as "hand-off, unhandled", and "unhandled" is what emails the customer's staff. Four paths did that. Thirteen more decide their own recipient without going through the engine.
| # | Path (plain words) | Trigger | Who heard about it, Oct 25 morning | Rule row | Through the engine? | Now |
|---|---|---|---|---|---|---|
| 1 | Held first email: ask the property owners to reply (leasing prep) | A draft check held Clara's first email to a new lead | Customer staff inbox, by email ("I couldn't get a clean reply out. Can you reply to them directly?") one second after the #alerts card | Engineering error | Yes (engine, its own family) | Fixed: deleted. The #alerts card is the only signal. |
| 2 | Text turn threw | Clara's text reply crashed on the last retry | Customer staff inbox, by email, as an ordinary hand-off; #alerts also paged | Engineering error | Yes (turn outcome, unhandled) | Fixed: alert outcome, no staff email. |
| 3 | Email turn crashed mid-reply (fail open) | The email agent threw after changing something | Customer staff inbox, by email; #alerts also paged | Engineering error | Yes (turn outcome, unhandled) | Fixed: alert outcome, no staff email. |
| 4 | Text turn crashed mid-reply (fail open) | Same crash, text channel; the text outcome never saw the crash reason and read it as "no reply" | Customer staff inbox, by email | Engineering error | Yes (turn outcome, unhandled) | Fixed: crash reason carried; alert outcome. |
| 5 | Agent-loop draft checks held the reply (fair housing, weekday or date, a tour time the calendar did not return, a tour confirmed that nothing booked, the anomaly review gate) | A check held the reply and blanked it, with no reason recorded | Customer staff inbox, by email, as a hand-off (each hold also pages Slack) | Engineering error | Yes (turn outcome, unhandled) | Fixed: holds are stamped "draft check held"; alert outcome; the 15-minute no-reply page still fires to #alerts. |
| 6 | Held first email, PropFlow's own notice | Same hold as row 1 | #alerts card (was the hello@ mailbox until PR #21608 on Oct 21) | Engineering error | No (direct Slack post) | Correct recipient, own path. |
| 7 | Voice: a call-end door failed | Clara could not file the call's outcome | #alerts only | Engineering error | Yes (alert outcome) | Correct. The model the text and email paths now copy. |
| 8 | Voice: a promise not kept | A tool errored on a call and Clara told the caller "the team will follow up" | Customer staff inbox (the caller is owed the callback) | Clara cannot help (the callback) plus engineering error (the tool) | Yes | Open: keep the callback ask, add the #alerts page; one prompt still allows the promise another forbids. |
| 9 | Text to a company line with no building | Onboarding left a line unmapped | Customer staff inbox, by email; no engineering alert | Probably engineering error (our config) | Yes (unhandled) | Open question for Fede: treat as ours (#alerts) or theirs (hand-off)? |
| 10 | No reply and no reason | Clara wrote nothing and no guard claimed the silence | Customer staff inbox, by email (Gera's Decision 13) | Clara cannot help | Yes | As designed. Left alone. |
| 11 | Emergency on-call chain stopped on a store error | Our store failed partway through the chain | Office inbox: "the on-call chain stopped early on a system error, please call the rest of the list" | Critical emergency (plus engineering error) | No (its own note path) | Keep the page (life safety); add #alerts; move onto the engine. |
| 12 | Gas or unknown-caller page when both lookups failed | Identity lookups failed on an emergency call | Customer PM inbox: "looking up the number failed on our side" | Critical emergency | No | Keep (an emergency always pages); add #alerts; move onto the engine. |
| 13 | Renewal stuck in "prepare failed" for 24 hours | Any cause, including our runner errors | PM email plus a text to the renewal contact: "hit a problem AppFolio hasn't told us the reason for" | Split: PMS refusal is theirs, runner error is ours | No (renewal copy of its own) | Open: split the cause; ours goes to #alerts only. |
| 14 | Key pickup calendar write failed | Tool failure during move-in | Clara forwards to the PM (a person must book it) | Clara cannot help, after an engineering error | No (forward tool) | Open: page #alerts too; keep the ask. |
| 15 | Work order filing refused by the PMS | The PMS was down or refused | PM paged: "Clara couldn't file a work order" | Clara cannot help (the PM can act) | No (promise alarm) | As ruled Aug 30. Move onto the engine. |
| 16 | Mass-text or staff-desk send failed | A send failed in a flow a staff member started | Text back to that staff member | Clara cannot help (direct feedback to the actor) | No | Fine: the staff member asked for the action. Move onto the engine. |
| 17 | First-email ops feed card | Compose failed, no reply, or held | The per-customer Slack channel the customer can see: "Clara could not draft a reply, nothing sent" | Engineering error | No (ops feed) | Open: the customer can see our failure wording; route to #alerts or soften. |
| 18 | Undelivered reply, tour confirmation with no booking, guard-rejected forwards, dead letters, stuck sagas, engine send failures | Various internal failures | #alerts only | Engineering error | Mixed | Correct recipient; several own paths to fold in. |
| 19 | Lease not drafted email | Lease draft failed | Nobody (no live caller) | n/a | No | Dead code; delete. |
| 20 | Promise alarm "we tried to send it and it did not go through" | A send failed on a promised message | Nobody (the human-notification lane refuses it) | Engineering error | No | Effectively dead; delete or route to #alerts. |
| 21 | Message taken, tour expected, gated inbound, tour slot refused, handyman stand-down, vendor notices, renewal holds | Real business outcomes | Customer staff, as intended | Clara cannot help | Mostly yes | Correct. Remaining own paths fold into the engine. |
| 22 | Leasing-team auto-fire blanks the reply without a reason | A hand-off already told the team | Can produce a second "unhandled" staff notice for the same ask | A person is already on it | Yes | Open: stamp the silence so the engine stays quiet. |
Proved at Willows, Oct 25, 22:43Z (PR #22417 merged 22:04Z, live 22:35Z). A Zillow-style inquiry to the Willows mailbox ran the real path: webhook, lead parser, first-email draft. The draft was held by a check (no tour times, no home named). What happened next, before and after:
| Before: Western Slope, Oct 25 20:18Z (real customer) | After: Willows, Oct 25 22:43Z | |
|---|---|---|
| Held first email | Yes, intent e65ca74b | Yes, intent 6784aef9, guest card 347 |
| Engineering told | #alerts card at 20:18:48Z | Internal notice logged (a test property never posts to #alerts) |
| Customer staff emailed | Yes: "Can you reply to them directly?", delivered 20:18:49Z | No. Zero staff-notice rows, zero sends, SendGrid empty |
| Draft kept on the record | Yes | Yes, status held |
Why the hole stayed open for a month (history dig, Oct 25, from PR bodies, git history and four session transcripts): six earlier leaks since Sept 28, and every fix closed one sender, never the decision of where a Clara failure goes.
| Date | What reached the customer or staff | What got fixed | What stayed open, and why |
|---|---|---|---|
| Sept 28 | Western Slope leasing inbox: "Clara's first email could not be composed. Please reply to them directly." | #13343: notice kinds labelled ours or theirs; a held first email is ours and goes to our inbox only | Anything sent to staff by another route. Fixed at the one file that sent it. |
| Sept 25 to 30 | Western Slope staff got "Guest card not created" error emails, one for Fede's own test call | #14252, #15547: failed guest cards go to the engineering channel | Other staff notices were judged by reading their wording; gaps logged as follow-ups and not done. |
| Sept 30 | Prospects got "I wasn't able to generate a response, please call our office"; a crash leaked Clara's instructions to residents; the client got a "tour-day update" error email | #14337, #14348: on a failure send nothing and page Fede ("If we error, we don't answer. I get pinged.") | A single gate on all outbound was proposed and Fede rejected it; the session wrote "fix each leak at its source". Email staff notices never got the text rule. |
| Oct 7 | New: "I couldn't get a clean reply out. Can you reply to them directly?" to the property's people on a held first email (#17408); "please add it in AppFolio" (#17509) | Nothing; both were built as features in an overnight autonomous run | Not Fede's ruling. Because they went through the team-notify engine they counted as normal team notices and passed the Sept 28 check. |
| Oct 14 | Western Slope inbox: "AppFolio showing not recorded, please add it in AppFolio" | #19579 deleted it; a failed AppFolio write holds, retries, alerts engineering | Scoped to AppFolio writes; nobody searched for other "please do it yourself" asks. |
| Oct 21 | Held first email at Pomona Park emailed to hello@ | #21608: the ours branch posts one #alerts card | The builder prompt and PR body said the team email path is "untouched". |
| Oct 24 | (set-up) #22249 removed the rewrite steps on first emails: a failed check now means one retry, then hold | Fewer bad drafts shipped | Kept the Oct 7 ask on purpose ("the team sees the existing held-email copy"); more holds meant the Oct 7 path fired more, which is why it showed up Oct 25. |
The one-path refactor, in four moves (plan only, not for today): (1) one decider vocabulary for every channel: every turn, tool and job ends as exactly one of handled, ask, hand-off, alert; the deciders for text, email and voice already agree, so the job is making every own-path caller in rows 6 to 20 produce one of those four instead of picking a recipient. (2) One sender: the team-notify engine becomes the only code allowed to address customer staff; a build check fails any other file that names an owner email, a PM inbox or a staff phone. (3) Alerts are a sink, not a recipient: "alert" writes the record and posts #alerts through one function; nothing else posts to Slack about a failure. (4) A table, not code, maps cause to recipient (the four rows above), and the harness shipping with the fix keeps every engineering-failure fact out of the staff path for good.
Proposed, not decided. Each is one small PR chain, no behavior change first (byte-identical replay), then the one-line behavior change. None adds a switch.
| Interface | Replaces | One question it answers |
|---|---|---|
shouldSpeak(turn) → reply · hold · silent, with reason | 5 supervised-hold sites, 9 live checks, the opt-out withhold, the "human replied" policy skip | "Should Clara say anything here, and if so does a person see it first?" One place, one log line per decision. The shadow feed's overlay is the first draft of it. |
askTheTeam(matter) → one notice | 13 paging paths and the two owner lookups | "Who owns this, and how do they want to hear it?" Writes the EscalationMatter, picks the owner once, sends one notice through the team-notify engine (then delete the engine switch). It is also the only path for a fallback: an internal failure writes the record and posts to #alerts, never a message to the customer or their staff (Fede, Oct 25). |
readStaffInput(words) → answer · handled · question · note · unclear, with facts (ids) and a reason | The phrase-list reader (classifyStaffReply), the rewrite step and the verbatim fallback on every place staff words come in (email reply, team note, held-card thread, a teammate in the thread) | "What did staff just tell me, and is any of it for the person?" Only answer can reach the person, as fact rows with ids for door 4; every doubt is unclear (one line back to staff). Never sends. Built Oct 27, nothing calls it yet; the callers move in the next PR. Benchmark: 0 wrong sends in 144 cases, the phrase list on main 38. Design. |
askState(thread) → asked · reached · acted · stale | Nothing today; the prompt says "it's with the team" unchecked | "Before I say it's with the team, is it?" Read of the matter record; the design's state rule; verifier blocks the sentence when the record disagrees. |
PmsInbox adapter (read texts, read staff replies, post a text) | AppFolio literals in core, mirror-specific ids downstream | "What did the tenant and the staff say in the customer's system?" Neutral rows; AppFolio one implementation (Fede's Oct 15 rule). |
Decided, Fede, Oct 25 evening (Clara Refactor session). Two rulings on shouldSpeak:
A wrong pick loads the wrong rules, tools and facts. Design: one resolver for every door, in code: dated records, then the PMS label, then the door default. A role only moves toward silence. Unknown senders stay silent on the AppFolio copy and the mailbox. Proof: Benchmark E, not started.
Fede: give the model all the correct context (for a tenant: knowledge base, conversation, lease, work orders, staff messages) and trust it. No proof yet that it does not invent facts; it is the top bench row, run before any prompt is rebuilt around it.
Cerebrus grades sent replies; nobody reviews its flags, and it cannot judge whether Clara should have spoken. Decided: D, fold (Fede, Oct 30) Keep its checks as plain app code, delete the engine and its CI check, throw the 362 existing flags away (Fede, Oct 30 evening: "we have more data now"), and, pending his answer, post each new text or email flag as a Slack card with the same rating buttons; checks nobody rates by Nov 25 are deleted. Numbers; full inventory and options.
Answer: yes, a short one. Proposed, pending the bench. Every major vendor guide tells you to state the goal and explain the why. No study tests a 2 to 4 sentence goal block on its own, so our bench settles it. A long mission essay is the one thing the evidence argues against.
(a) Short goal at the top, 2 to 4 sentences -> DO IT (all three vendors recommend it) (b) A "because" on each key rule -> DO IT (Claude generalizes from the reason) (c) Long mission or vision preamble -> DON'T (more words, more missed rules)
| Source | Finding | Applies to Clara? |
|---|---|---|
| Anthropic best practices | Context or motivation behind an instruction helps; "Claude can generalize from the explanation." One role sentence makes a difference. | Yes: (a) and (b) |
| Anthropic context engineering | Aim for the "right altitude": not brittle if-else rules, not vague guidance. | Yes: keep the goal concrete |
| OpenAI GPT-4.1 guide | Template opens with "Role and Objective". Instructions at the top beat the bottom if stated once. | Yes: (a), placement |
| OpenAI GPT-5 guide | Contradictory instructions cost the model reasoning effort. | Yes: never restate rules in the goal |
| Google Gemini guide | State the goal clearly and concisely; context first, the ask last. | Yes: (a) |
| Persona study (EMNLP 2024) | A persona did not help on 2,410 factual questions. | Partly: Clara writes, not quizzes |
| IFScale (NeurIPS 2025) | More instructions, more misses; models favor earlier ones. | Yes: against (c), goal goes first |
| When Instructions Multiply | Success falls steadily as rule count rises. | Yes: against (c) |
| Explanations in context (EMNLP 2022) | Explanations help, but only large models. | Yes: Opus 5.5 is large |
| Goal drift (2025) | A goal stated in the system prompt drifts as context grows; firmer wording drifts less. | Partly: our turns are short |
| Underspecified prompts (ACL 2026) | Unstated needs are guessed right 41% of the time; listing every rule does not fix it. | Yes: goal covers unwritten cases |
Three sentences, at the very top, before rules and inputs:
<objective> This email answers the prospect's question using only the facts below and offers two tour times from the tour windows. Success: they reply to pick a time, and every fact we state is in the inputs. Never invent a fact, a price or a time; if a fact is missing, say we will confirm it. </objective> <rules> ... each key rule with a short "because" ... </rules> <inputs> home facts, tour windows, knowledge base </inputs>
If a job's inputs run long, add one line at the end restating the ask.
Not in the block: the company vision ("the PMS stays the system of record" is true but does not change this email), copies of rules already in the rules section, or any line with no effect on the message.
Gap: no source tests a short objective block alone on a writing task. Quotes above are from the Anthropic page read in full; other rows are paraphrased from fetched abstracts and guides.
First we replay every live customer's real records through today's code and save the exact output at every gate. After each refactor step we replay them and compare byte for byte; any change is named, unless it is a known bug we meant to fix.
Design only. Codes like F3 point at the map above.
Oct 27: the plan to make this the one harness, folding the others into it, is on the Harness Atlas, October 27 plan.
No new rig. This is a new leg of evals/replay-harness, the atlas's recommended replay engine: it enters at the real door (runPipeline → dispatchInbound), proves its sandbox before turn one and already records gates. The capture record is its TurnMeasure with new fields.
The second engine is the hold replay behind #22334 (1.57M decisions over 203 buildings, byte-identical). It was a local script; here it becomes permanent.
| Piece | Reused from | Path |
|---|---|---|
| Real door, sandbox proof, parity alignment | Replay harness | evals/replay-harness/run.ts, lib/sandbox.ts, lib/diff.ts |
| Read-only snapshot outside the repo, manifest with sha256 | Gold mine extractor | evals/replay-harness/goldmine/ |
| Bench target, Temporal fail-closed | Shared BenchConfig | scripts/harness/types.ts, defaults.ts, temporal-guard.ts |
| Scrubber | Promise harness | scripts/promise-harness/scrub.ts |
| Subscription billing, never API keys | Eval runner | scripts/lib/subscription-eval-runner.ts |
| Mutation control registry | Pre-push gate | scripts/mutation-controls/ |
Bench scores quality; this checks equality. Clara Bench (frozen set v1, clara-benchmark#21) has judges score replies. This harness asks only: did any byte change at any gate?
Corpus and safety. Real records from Western Slope, Situs, JP&Co/Camellia and Willows, read only, snapshotted once, scrubbed with a stable map per record, stored outside the repo. Git holds ids and hashes only. Replays write to the bench at the test property with HARNESS_SUPPRESS_OUTBOUND=1.
| Tap | Map | Bytes captured | Modules | Channels |
|---|---|---|---|---|
| 1 Door and lane | A1 to A7, S5 | Entry point fired; fixed-rule lane picked; any canned reply | All | Text, 3 email lanes, PMS inbox, guest card, voice transcript |
| 2 Router rung | S6 | The rung that claimed it (vendor, PM confirm, staff, tour, pass to Clara) | All; tour is leasing | All |
| 3 Turn inputs | B1 to B13 | Trimmed history, knowledge block, every <system_context> message | All | All |
| 4 Assembled prompt | C1 to C24 | Full system prompt text, tool list, cache breakpoints | Workflow per module (C11) | Email overlay (C18) separate |
| 5 Model and side checks | D5, D6 | Every tool round's raw output; every topic verdict. Recorded, then replayed | All | All |
| 6 Guards after the model | E1 to E19 | Draft text after each guard, and which guard changed it | Leasing E4, maintenance E5, rest shared | All |
| 7 Stay quiet | F1, F-e1 | Reason code | All | Text, email |
| 8 Hold decision | F3, F5, F-e2 | {hold, reason} at each of the 8 callers, plus the rule R1 to R7 that fired | All | Text, 3 email lanes, both doors |
| 9 Door gates | F7 to F9, F-e3 | Each step reached and its verdict, in order | All | Text door, email door |
| 10 Would-send | F10, F-e5 | Adapter, synthetic recipient, final body bytes, caught by the kill switch | All | All |
| 11 Asks, pages, PMS, reminders | F12, F13, F17, F18 | Ask opened and its owner; page; PMS write intents; promises | All; page is maintenance; PMS writes leasing first | All, incl. voice transcript |
| 12 Records saved | G1 to G8 | Rows written, with generated ids and times replaced by placeholders | All | All |
Two pins keep it deterministic: the clock is set to each record's original time (C2, B13), and every read comes from the snapshot.
| Mode | Model | Passes when | Used for |
|---|---|---|---|
| Recorded | None called. Each round's answer is played back by record, turn and round, not by prompt match | Every tap identical, except cells on the ledger | Every refactor step. A prompt change shows at tap 4 alone; the gates still get the same answer, so the report can say "prompt changed, gates identical" |
| Live | Called on the subscription | At or above baseline on Clara Bench judges. Words always differ, so never equality. Bench v1 has no renewals or prospect reply turns; those steps stay recorded-only until it does | Steps that change the prompt on purpose |
Bugs are not carried forward. Each known defect is one row; its cells (record × tap) are skipped, so its fix is not a regression, and the row becomes that fix's acceptance test.
Row: id · ref · tap and gate · module, channel · record selector (ids kept locally) · today · right · status.
| Id | Ref | Tap, gate | Module, channel | Today (expected wrong) | Right looks like |
|---|---|---|---|---|---|
| KB-01 | Machine map, 8 callers; #22334 unified the function, not the order | 8, F3/F5/F-e2 | All, every lane | The hold is asked from 8 places; PMS email checks "a person replied" before the hold, text holds at the row line and again at the door | One decision, one order, same outcome on every lane |
| KB-02 | Machine map F-e4 | 8 and 9, F-e2 then F-e3 | All, SES email | At a company not yet live, SES makes a held draft and approval card; Graph refuses the same input and logs it | Live check first: refused and logged, no card |
| KB-03 | Fede's anti-pattern (Clara new hire); bench no_team_told check, clara-benchmark#21 | 6 and 10, E15/E17 | Tenant comms, leasing; text, email | "I don't have that on file, I'll let the team know" goes out | Answer, or stay quiet and open one tracked ask |
| KB-04 | Hand-off audit (appendix): one in three hand-offs never reaches a person | 10 and 11, E17/F12 | All; text, email | Reply says it is with the team; no ask is opened | Claim a hand-off only when the ask opens in the same turn |
One local command on the subscription, never in CI:
HARNESS_SUPPRESS_OUTBOUND=1 npx tsx evals/replay-harness/run.ts --mode recorded --baseline day0-2026-10
evals/replay-harness/results/, a before and after .md per step (counts, hashes, code stamp); bytes in a gitignored .detail.local.md.replay-byte-diff.control.ts replants #22334's defect (door exception narrowed to emergencies) and must turn tap 8 red.On the testing ladder this is rung 3: every live customer's real book, read only, byte-identical. Atlas row: Clara byte replay, extends evals/replay-harness, recorded model, byte equality per tap, rung 3.
Fede, Oct 25 evening: every real record since each customer went live, census re-run first (August's count covered Camellia and Yale only). Decision 2 stays open; decision 3 was decided later the same night; no Day 1 go yet.
Fede, Oct 25 night (Clara Refactor session): a pure move must be byte-identical on the Day 0 compare. A refactor that also fixes a bug on purpose passes only if every differing cell matches a known-bugs ledger entry named in the PR. Prompt changes are judged by the bench. On strictness Fede said "maybe fine".
Prompt changes, same night, Fede's words: "its fine if we merge prompt changes but its gonna go supervised for a while, like shadow" and "nothing live to customers". A prompt change may merge on its bench row, but it runs in shadow: drafts on cards beside what Clara sent, nothing reaches a tenant. He reviews examples over days ("as the judges have gotten it wrong") and flips shadow to sending per customer by hand, the same rule as A1.
Clara joins the office like a new employee, and the first thing she learns is when not to speak.
| # | Question | If yes |
|---|---|---|
| 1 | Should I be quiet here? (a thanks, a reaction, an unknown sender, a mass-text answer) | Stay quiet |
| 2 | Should a person take this? (a floor, a money call, an exception) | Stay quiet to the tenant; track it for the owner |
| 3 | Is a person already on it? | Stay quiet |
| 4 | Is my reply actually helpful? | Reply. If not helpful, do not reply. |
shouldSpeak is for.She replies only when it helps. Otherwise she stays quiet and tracks the work, asks one owner for help, or stays out because a person is already on it. The anti-pattern: “I don't have that on file, I'll let the team know,” plus one more email to the team. Two dials per customer: scope (which jobs she holds) and autonomy (a person approves first, or she sends by written policy). Fair housing, legal and emergencies are never hers. Long term she carries most routine work (Fede's aspiration); phone is out of scope.
Next, Clara starts answering tenants: maintenance first, renewals second. Proposed, pending review
Fixes to what is already live ship with no switch and go live everywhere on merge. New outbound, such as tenant replies, the morning list or staff texts, ships dark: off by default until Fede turns it on per customer.
Tenant replies are released one module at a time (Gera, Oct 26). If maintenance is released at a company, replies the one call declares as maintenance can go out; a reply on any topic whose module is not released stays silent or held, never sent. The gate already exists at the send door (door 6): company-live-gate.ts takes the module the writer declares (door 3) and holds on module_off. The module's own mode is the release: off = silent or held, supervised = hold card, on = sends. So the one company setting above sits behind that per-module gate, not in place of it, and today's maintenance.pms_inbox_tenant_door is the first of those gates and stays.
| Step | What | Switch? |
|---|---|---|
| 1 | Read-only replay of every customer's real book through the state rule and ownership routing; byte-identical where behavior is unchanged; hand-off correctness becomes a standing number | none |
| 2 | Fixes to live behavior: no "with the team" without a tracked ask; no held drafts on Clara's own channels (covered topics answered live, the rest an ask); routing by the ownership map; delivery confirmed before a hand-off counts; a staff answer passed to the renter and recorded on its ask; today’s standing-vs-one-off sort keeps saving standing answers until the question in step 4 replaces it; identity checked before a policy answer | none, live everywhere on merge |
| 3 | Willows live run with Fede's own number as staff: a prospect conversation to an ask, the chaser, the intro, a piece of work sent to Clara by text | — |
| 4 | New outbound: the morning list to staff; transfer confirmed by text to the caller; Clara introduces herself; Clara texts staff questions; Clara chimes in inside their shared inbox; Clara proposes PMS settings; after an answer Clara saves the standing ones as policy herself (the vision's "your answer becomes policy"; no “remember this?” question, Fede Oct 24) | company setting, default off, Fede turns on per customer |
| 5 | Sandbox company in the customer's shape, then a supervised week at the customer | — |
After the foundation (Fede, Oct 26 night): attack the false holds and false passes that (1) hold leasing emails for no reason, (2) let wrong facts out, and (3) cause accidental escalations to customers, "so this never happens again". Door 4 for holds, doors 2 and 4 for facts, door 4 promises plus the ask-for-help door for escalations. The 30-day gate ledger below is the before-number; the same 30 days and the 5,515 frozen decisions give the after-number. Nothing is attacked before every fold and the pure-split baseline are merged.
How nothing gets lost (Fede, Oct 27 night: 'i keep reminding you of things we forgot'): every piece of feedback Fede gives, in any session, becomes a tracker row with an owner the same day (136 items from Oct 23 to 27 are rows 58 to 71 and the 'What Fede said' section). The tracker derives a 'Said, not acted on' list: every row that came from his feedback and has no PR after two days. That list renders here and on the morning page; a row stays on it until a PR exists or Fede closes it.
Rows from Fede's feedback with no PR after 2 days, oldest first, derived from the tracker by bin/derive-master (UTC date 2026-11-02).
| Row | What he said | First said | Owner | Days with no PR |
|---|---|---|---|---|
| 7 | Maintenance intake: a two-issue text can lose one issue (nothing filed, nothing asked) | 2026-10-25 | fede | 8 |
| 10 | Situs Group onboarding package: Clara's intro texts, what she does, the who-owns-what map, the hello per role | 2026-10-25 | fede | 8 |
| 13 | Hand-off fixes to live behavior on the shared interface: the one-in-three hand-offs that needed a person and never reached one | 2026-10-25 | fede | 8 |
| 14 | 'Did a human already reply' signal from AppFolio, so Clara stays out when a person is on it | 2026-10-25 | fede | 8 |
| 17 | Tenant replies path dark: maintenance first, renewals second, behind the existing tenant-door setting | 2026-10-25 | fede | 8 |
| 20 | Decision 3 (decided): the morning list is a per-company setting, off everywhere for now | 2026-10-25 | fede | 8 |
| 66 | Unanswered-tenant alert skips threads already handed to the team (Situs) | 2026-10-27 | 001 | 6 |
| 68 | Emergency categories and policy in Clara's context and the bench (a water leak is usually urgent) | 2026-10-27 | 001 | 6 |
| 69 | Alerts never tag Fede and Gera | 2026-10-27 | 001 | 6 |
| 70 | Open questions from Fede's feedback (Oct 23 to 27) | 2026-10-27 | 001 | 6 |
| 82 | Deep review, one case at a time: every shadow-card rating and every supervised hold since launch (context, category, previous messages, should-reply logic) | 2026-10-27 | 001 | 6 |
| 88 | Follow-up writer stops echoing a bedroom count from the card notes (Jessalyn: "2-bedroom" at an all-3-and-4-bedroom property) | 2026-10-27 | 001 | 6 |
| 90 | "I can't add anything here" is a recognized outcome with no outbound at all (one-door design) | 2026-10-27 | 001 | 6 |
| 97 | Tenant-text judge fixed so candidate rows are scored, not shown unjudged (control failed Oct 25) | 2026-10-27 | 001 | 6 |
| 98 | Post-release watch: the week after a swap, the same shadow cards plus a daily count of sends, holds and Wrong ratings for that job | 2026-10-27 | 001 | 6 |
| 113 | Willows guest-card first email has no live driver (rows 93, 104, 47 cannot be proven live) | 2026-10-27 | 001 | 6 |
| 122 | A Zillow relay lead for a building we hold is filed on the company front door because the building-name match is exact (door 2 intake adapter) | 2026-10-27 | 001 | 6 |
| 125 | Ember Estates lead: we couldn't locate the property (design + harness first) | 2026-10-28 | Situs Leasing session (handed off by Fede, Oct 28) | 5 |
| 129 | A neighbor's complaint (drugs, noise, a person) is never answered as a repair follow-up; Clara stays quiet and it goes to the office | 2026-10-29 | 001 | 4 |
| 135 | A short acknowledgement beats silence when a tenant explains themselves or asks when the tech comes | 2026-10-30 | 001 | 3 |
| 143 | First-email prompt split (real, by deletion) - parked | 2026-10-30 | 001 | 3 |
| 144 | Lampliter third-party inquiry (Michael Neuhalfen), tour already on the card | 2026-10-30 | Situs Leasing session | 3 |
The base is refactored first; Fede's three priorities sit on top of it, in his order. Proposed, pending review
Fede's three priorities, in his words and his order:
The full advice on the order (why the base is refactored before any module) is in the tracker's working notes.
Not a calendar. Each slice starts when the slices it depends on are done, and its state is what the tracker shows today.
States derived from the phase tracker by bin/derive-master (tracker refreshed 2026-11-02 05:58Z), never typed by hand.
| Slice | Work | Depends on | State |
|---|---|---|---|
| A Frozen baseline | A fixed corpus per surface (prospect first email, prospect reply, tenant repair text, tenant other text, Spanish slice across all), smoke and full tiers, content-hash locked, production's run recorded as the baseline row on the Clara Bench page. Renewals and voice get their first frozen set later. Owner: Benchmark lane. Status: see the derived state in this row. Clara Bench set v1 is frozen and locked (clara-benchmark PR #21 merged; a drift test with four planted changes went red on all four; the text stays off git, masked, on this Mac). Production's baseline is adopted on the Clara Bench page as row 2026-10-25-baseline-v1. Headline, tenant texts the office had already answered within 48 h: today's writer would reply on 82 of 96 (85% noise), and only 72 of its 119 replies help, the rest are "I've told the team". Repair texts, smoke of 20: noise 10 of 10, filing matched the office 7 of 17. Prospect first emails, 51: 24 held by send checks, 50 of 51 helpful. Live tenant noise is 0 today only because the tenant switch is off at both customers; the bench bypasses the switch. Zero writes, zero notices during the run. Caveat: the Clara-vs-office judge column on tenant-other texts is not valid yet (the mirror pairs the wrong office reply in 8 of 13 controls); v1.1 re-pairs. Earlier that day the answer to "do we have a frozen baseline?" was no; renewals and voice still get their first frozen set later in the week. | nothing | in build · 1 proven, 1 part landed, 1 not started · from the tracker |
| B Hold: one door | shouldSpeak seam at all 8 hold callers, no behavior change, byte-identical replay (PR #22450, merged Oct 25 night). The six hold backstops stay: each guards a path the earlier check never sees (final module after routing versus provisional; router replies with no Clara turn; door sends with no turn), and all six now call the one shouldSpeak function. That is the collapse: one implementation, several call points. Next, same slice (PR #22473, armed, Day 0 byte-identical): the first-email check folded in, isLeasingSupervisedAt deleted, one log line per decision, and the first no-duplicates check (scripts/check-should-speak-duplicates.ts, zero-entry allowlist, mutation control). | A | done · 2 proven · from the tracker |
| D Topic: one classifier | The four module classifiers (tour intent, repair triage, renewal decisions, lease questions) behind one shared classification, byte-identical replay. | A | merged, unproven · 1 merged, unproven · from the tracker |
| F Model plumbing | One registry of model paths for every Clara call site, pass-through, byte-identical replay (PR #22446, merged Oct 25 night). | A | merged, unproven · 1 merged, unproven · from the tracker |
| G Situs onboarding package | Clara's introduction to the Situs Group team and the working agreement, built and proven at the Willows first. | A | in build · 2 merged, unproven, 1 not started · from the tracker |
| C Ask for help + staff notices | askTheTeam(turn, ask) pass-through seam over 23 callers on the three existing paths (page the team 12, held text 7, turn outcome 4), Day 0 byte-identical (PR #22455, open). askState(thread) maps to asked, reached, acted, stale; no caller yet. Still outside the seam, each its own next slice: tellStaff (16 callers), sendOrHoldTeamNotice (13, business notices not asks), sendStaffAskSms (6), pagePropertyManager, two direct notice-engine callers. Then every fallback routed through the one notice engine, the staff-facing fallbacks removed (the first-email draft-check fallback found Oct 25, and whatever the Escalations audit lists), failures to #alerts only. | B | in build · 2 proven, 1 merged, unproven, 1 part landed, 2 not started · from the tracker |
| H Bench: full context vs restrictive | Full-context versus restrictive row on the frozen set. Repair path on the decomposed prompt, writer on Opus 5.5, Willows end to end. | A, F | in build · 1 proven, 3 merged, unproven, 3 in flight, 3 part landed, 1 not started · from the tracker |
| I Hand-off fixes, live | Hand-off fixes to live behavior on the shared interfaces, plus a Willows run. | C | in build · 6 proven, 18 merged, unproven, 3 in flight, 2 part landed, 15 not started · from the tracker |
| E The rest of the 47 → 4 | One small PR per table row above, same move each time: one door, byte-identical, delete the copies. PMS write becomes one adapter. | C, D | in build · 14 proven, 11 merged, unproven, 5 part landed, 6 not started, 1 canceled · from the tracker |
| J Tenant replies, dark | Maintenance first, then renewals, dark. Held drafts that expire in silence become an approval that stays open. | I, H | not started · 3 not started · from the tracker |
| K PMS inbox adapter + review | PmsInbox adapter pass-through; the core-and-modules diagram checked against the code; review with the numbers. Found Oct 26 (Lori Ruiz, Western Slope): PMS notification emails get drafted to and refused; a text-door refusal (party not in thread) is read as "no phone"; an inbound on an unlinked PMS text thread never reaches the person. Belongs here, not a new item. | E, J | in build · 1 part landed · from the tracker |
Status of each phase, derived from PR and decision state and refreshed on a schedule.
Separate URL on purpose: the tracker is rebuilt by a script on a schedule (status derived from each row's PRs, decisions and production evidence), so it cannot live inside hand-written HTML. Open it on its own at /a/clara-new-hire-phase-tracker.
Working notes (ledgers, bench results, model inventory, both final-pass reviews, the chapter list) live on the tracker page under Working notes.
Order of execution and dependencies live on the tracker: clara-new-hire-phase-tracker#roadmap
Proposed, pending review Design only: no fixes, no PRs, nothing sent. Fede, Oct 26: "before we go into quick fixes i want a deep dive into each area, see how the new architecture would have covered it or not covered", and "these are exactly the kind of weird bugs and edge cases we want to roll into the new architecture". Each case is read against the sentences on this page as they stand, then turned into a test the refactor must pass.
Verdicts: COVERED a sentence on this page prevents it · PARTLY the page points the right way but leaves the failing part open · NOT COVERED the page says something that would still let it happen · NOT ADDRESSED the page is silent.
Two Western Slope first emails were held on the same check. Trang got nothing until a session sent the draft by hand at 15:28Z. Patrick got his application link but no answer to his lease question and no tour offer. The drafts were written correctly once; code around the writer broke them.
| 1. What happened | 2. Owner on this page | 3. Covered? The sentence | 4. What the design must say | 5. Battle-test case |
|---|---|---|---|---|
Tour times stripped by provenance. Trang's first draft offered two real slots from the facts block. The "tour times from this turn" guard only counts a calendar call made this turn, so it ordered a retry with no times; the first-email check then held it for having no times. Intent 5e0d4682, snapshot tsnap_4faebf4f, 14:41Z. | Turn rebuilt, layer 5 (facts resolved by code: "tour windows") and "Checks that only block". | PARTLY "Checks that only block: a count, a closed time" checks a fact, and layer 5 hands the slots in as facts, so the new turn should not have this gate. But the machine map still lists "tour times from this turn" as a guard and the page never says it is deleted or rewritten. | A check compares the draft against our own records (the slot list, the calendar, the listing rows), never against which tool ran this turn. Every provenance guard is listed and converted or deleted. | Fixture #22483 (offline replay of the real held draft, XFAIL today, strict mode red). Assert: draft names one given slot. Positive control: the writer's own first draft passes. Joins Day 0 as ledger row on tap 6 (which guard changed the draft) and tap 8. |
The one retry never ran. The writer-call capture skips any round with a tool call; every first email ends with the declare tool, so there was nothing to retry (retried: false). Same shape Oct 25 20:18Z. | Turn rebuilt: "Retry once, then hold for a person". | PARTLY The sentence promises a retry; nothing on the page proves the retry exists on each path. | The retry is part of the core turn, not each module's capture code, and the harness records "retry attempted: yes / no" as its own cell. | New tap: retry attempted, per held turn. Assert yes on Trang and on the Oct 25 hold. Mutation control: replant the skip, cell turns red. |
Half a turn. Patrick: the application went out through AppFolio at 15:35Z, then the reply was held on the same check. He got the link and silence. Intent ff83442c, card 3048. | The record ("a step-by-step ledger of what she did"); four ways a turn ends. | NOT ADDRESSED The page treats a turn as one outcome (send, hold, quiet, ask). It never says what happens when an action already ran and the words did not. | A turn's actions and its words are two outcomes on the record. A held reply carries the actions already taken, the retry is told they are done, and the person seeing the hold sees them. Words are never edited after writing (Fede's no-rewrite rule; the send-layer split was refused Sep 30); the fix is the writer passing, not a partial send. | Patrick fixture (not in any harness today): tap 11 shows the application intent, tap 8 the hold. Assert: hold record lists "application sent"; retry draft does not resend it. Control: same input without the application ask holds nothing. |
| Silent holds. Trang's hold had no review record, so the conversation page showed nothing and no one could approve from Slack or the app; four turn snapshots marked held (Oct 25 to 26) have no hold row, though what that flag means is not yet proven; the records that exist store a reason, not the check name. | Notice engine; "Recipient by cause"; Supervised ("an approval card is the design"). | NOT COVERED The cause table puts "a draft check failed" under engineering error: "Nothing. No reply, no staff email, no hold card. Clara stays quiet." Read literally, that is Trang's silence. It also contradicts "Retry once, then hold for a person". | A blocked reply to a renter is never silent: it is one hold record (draft, check name, finding, renter's latest words, actions taken) plus the #alerts line, recoverable everywhere. Staff are still never emailed about our failure. | Census cell: every held turn has exactly one hold record with a check name. Today red: since Oct 25 noon only one Western Slope first-email hold row exists against at least four held turns. |
Judged against the hub's held-messages design (option B: one hold record, the draft inline on the conversation page, approve, edit and send, retry and dismiss on every surface, automatic retry on a new renter message): it fits this page's rules. It adds no routine queue, holds exist only where Fede chose supervision or Clara could not write a sendable reply, and it merges the two hold paths (the intent row and the review queue) into one record, which this page needs and never states. The page should adopt it as the review door's design.
Ember advertises six free weeks on its listings. Trang never heard about it, and the email she did get quoted a price range that mixed in a home not open until Nov 2.
| 1. What happened | 2. Owner on this page | 3. Covered? The sentence | 4. What the design must say | 5. Battle-test case |
|---|---|---|---|---|
| Special dropped. "6 weeks free" was on the listing rows for 363 and 364 Ember Ln and came back in the unit lookup. A prompt line says "Tool results never carry concessions; if the block is absent, no specials are running", so Clara left it out on purpose (her own reasoning says so). Asked "any specials?", she would have said no. | Layer 5 facts; layer 3 job prompt; "Build-time coherence test". | NOT ADDRESSED The word special appears once, as today's prompt block. Nothing says where a special lives or when it is shown. The coherence test catches two rules on one topic, not a rule that contradicts the data. | A listing special is a field on the home with an end date, shown on the same line as that home's rent wherever the home is named, and never copied into the knowledge base. Past its end date it is gone. | Bench episode on Trang's frozen turn (prompt change, so live mode plus shadow, per decision 3). Assert: special mentioned; "any specials?" answers yes. Control: a home with no special, none mentioned. Mutation: put the prompt line back, red. |
| Specials in the knowledge base. The concession watcher writes temporary listing specials into the knowledge base; four stores, five render paths, none with an expiry. | Knowledge block ("full dump, no search"); the modules table, Knowledge row. | NOT ADDRESSED The page fixes where the knowledge base is read, not what may be written into it. | Where facts live: each fact has one home (listing facts on the listing, standing policy in the knowledge base). The knowledge base holds no temporary fact. | Replay cell on tap 3 (turn inputs): the knowledge block contains no listing special for any property in the corpus. |
| Ranges, and one wrong. The sent email said "$2,775 to $2,795, 1,289 to 1,435 sq ft, open right now". $2,775 and 1,435 belong to 2863 Florida St, which opens Nov 2. Two prompt rules disagree (quote a range; name by size), so the model picked differently at 14:40 and 15:15. | Layer 5: "availability shaped to one home with no counts"; layer 3: each job "owns its homes-to-name rule exactly once". | PARTLY "One home, no counts" is on the page; "no ranges" and "open now means open today" are not. | The facts block names one home with its own price, size, open date and special; never a range; a home that opens later is never "open now". | Same episode. Assert: one price, one size, no "to"; no figure from a home not yet open. Control: a single open home quotes its own numbers. |
| 1. What happened | 2. Owner on this page | 3. Covered? The sentence | 4. What the design must say | 5. Battle-test case |
|---|---|---|---|---|
| Later messages lost. AppFolio folds repeat messages into one inquiry; we keep one, so Clara answers the first and never sees the rest. Situs card 8995 (photos, utilities), WS card 3047 (tour ask). A 60-day sweep found 5 cards with this shape (3 Situs, 2 Western Slope). | PmsInbox adapter ("read texts, read staff replies"); turn assembly. | PARTLY "Neutral rows; AppFolio one implementation" puts it in the right box, but the page says nothing about one row per message, or that a new message is a new turn. | PmsInbox returns messages, each keyed by the provider's own message id; every new message is a turn; prospects and tenants follow the one rule (the dark tenant copy already does). | 003's local harness: 7 cases, 18 checks, 7 red today, 18 red with a planted defect, all green on design A. Today it is rung 1 on the testing ladder (fixtures with a planted defect: build hygiene, not proof). To join the gate, its real cards enter the Day 0 corpus and its 7 red cells become ledger rows on taps 1 and 12. |
| Rerun duplicates the message. Re-running Trang's first email wrote "I'm interested..." a second time (14:40 and 15:15), because each run mints a fresh id. The lead card and first bubble also show the same words. | The record; replay harness "Records saved". | NOT ADDRESSED Idempotent writes are not mentioned. | Every write is idempotent on a stable key (provider id, or the guest card's own key); a rerun or retry never adds a row. | Replay twice on the same record; assert tap 12 identical row count. Control: a genuinely new message adds one row. |
| Half-done hold (Patrick, bug 7), and an empty thread for 30 to 60 s before the first save. | As case A, row 3; the record. | NOT ADDRESSED | As case A. The renter's message is saved when it arrives, before the model runs. | Tap 12 order: message row written before the first model round. |
Customer-side, listed only (not ours to code): Situs staff reconnecting Google with the calendar box ticked; the Situs AppFolio auto-responder, which sends three canned emails per renter beside Clara's.
| 1. What happened | 2. Owner on this page | 3. Covered? The sentence | 4. What the design must say | 5. Battle-test case |
|---|---|---|---|---|
Kat replied to Patrick inside AppFolio; the reply was mirrored; Clara stayed silent. The approve path closed the held draft, and no follow-up cadence started. Conversation bbc825d1. This worked. | shouldSpeak ruling; Recipient by cause, row 3; PmsInbox ("read staff replies"). | COVERED "'Is a person already on it' is reasoned, never timed", and "A person is already on it: Nobody. shouldSpeak returns silent." PARTLY on the aftermath: the page never says what happens to a held draft or the cadence once a person takes the thread. | When a person takes the thread, Clara's held draft closes with that reason on the record and her own follow-ups stand down until the thread is handed back. | The staff-took-thread fixture being built separately. Assert: shouldSpeak silent with reason "person on it", held draft closed, no cadence row. Control: same thread with no staff reply, Clara replies. |
The card that announced Trang's hold tagged Fede and Gera, looked like the old cards and offered nothing to press, so the only way out was a session running a script.
| 1. What happened | 2. Owner on this page | 3. Covered? The sentence | 4. What the design must say | 5. Battle-test case |
|---|---|---|---|---|
| The card for a held item tagged Fede and Gera (a fixed list in code, whose comment says it is deliberately not a setting), used the old renderer and had no actions. Fede: "it should not tag me and gera", "why is it not using the new design". | Notice engine ("A module never picks its own recipient"); modules table, "Review door for automated sends"; tenants: "Hold card, property Slack". | NOT ADDRESSED Who is tagged, which renderer, and what a person can do on the card are absent. The review door is a row in the modules table, not one of the four interfaces. | The review door is named as a core interface: one renderer, the four actions, recipients from the cause table, nobody tagged by default; the mention list is a per-customer setting Fede picks. | Render tap on would-send (tap 10) for every hold in the corpus: zero mentions by default; the same card shape and actions on every surface; then a production screenshot of a real card. |
The page decides well who speaks and who hears; it is thin on what is true and what is left behind. Three of five cases are gates that judge by where a fact came from (a calendar call this turn, a "no specials" block) instead of by the fact in our own records, and the new turn removes their reason without naming them for deletion. Two hold paths exist, and one leaves no record, so a blocked reply becomes silence; the page's own cause table, read literally, keeps that silence. Facts live in several places at once (specials in four stores, messages overwritten into one row), so the facts block cannot be trusted until each fact has one home and one key. A turn is drawn as one outcome, but real turns act and then speak, and half a turn is the worst shape. PMS literals sit in core leasing code on every path we read: the listings syncs and listing details import the AppFolio scraper directly, core reads AppFolio-shaped inquiry fields, the special is extracted inside the scraper, and the first-email send, consumer, prep and inbound-message files name AppFolio (debt, not fixed here). None of these were caught by the Day 0 harness, because it freezes today's behavior; each one needs a ledger row so the refactor does not lock it in.
Proposed, pending review. Read-only sweep of production holds, Slack cards, GitHub issues and incident pages, Oct 26 2026. Nothing was changed or sent.
318 real-life cases. 28 fixed and tested, 76 fixed but untested, 63 open, 5 regressed, 142 by design, 4 unknown. Of the 176 that are not by design, only 28 are fixed and tested.
Each case is read against the master design on this page with the battle-test words: COVERED, PARTLY, NOT COVERED, NOT ADDRESSED. Each case not fixed and tested gets one line on how it joins the Day 0 replay harness (evals/replay-harness, #22445) as a fixture or the bench as an episode. No new harness folder. The pattern is #22483 (held first email with tour slots) and #22488 (staff took the thread).
| Source | FIXED+TESTED | FIXED, UNTESTED | OPEN | REGRESSED | BY DESIGN | UNKNOWN | Total |
|---|---|---|---|---|---|---|---|
| Holds (rows and cards) | 1 | 13 | 4 | 3 | 141 | 4 | 166 |
| Incident pages | 7 | 12 | 5 | 2 | 0 | 0 | 26 |
| GitHub issues | 20 | 51 | 54 | 0 | 1 | 0 | 126 |
| All | 28 | 76 | 63 | 5 | 142 | 4 | 318 |
By root cause, cases that are not by design:
| Root cause | Cases | FIXED+TESTED | FIXED, UNTESTED | OPEN | REGRESSED | UNKNOWN |
|---|---|---|---|---|---|---|
| hold handling (review door) | 36 | 6 | 15 | 15 | 0 | 0 |
| sync dropped a message | 30 | 2 | 20 | 7 | 0 | 1 |
| identity/matching | 29 | 1 | 15 | 12 | 0 | 1 |
| other code bug | 25 | 9 | 11 | 4 | 1 | 0 |
| provenance gate stripped real facts | 22 | 3 | 4 | 12 | 3 | 0 |
| missing fact in prompt | 12 | 1 | 1 | 10 | 0 | 0 |
| tours and staff-desk logic | 10 | 5 | 2 | 3 | 0 | 0 |
| writer quality (prompt rules) | 8 | 0 | 8 | 0 | 0 | 0 |
| unknown | 2 | 0 | 0 | 0 | 0 | 2 |
| review mode by design | 1 | 1 | 0 | 0 | 0 | 0 |
| wrong fact in KB | 1 | 0 | 0 | 0 | 1 | 0 |
Against the design, holds and incidents that are not by design (graded by reading each against the page; issue rows carry a keyword guess and are not counted here): COVERED 5, PARTLY 32, NOT COVERED 2, NOT ADDRESSED 12. Most NOT ADDRESSED cases sit in three places the page is thin on: the hold record and review door, idempotent writes in PmsInbox, and what is left behind after a turn acts.
453 hold rows in production since Aug 31: 158 at Western Slope, Situs Group and Camellia, 295 at test companies (Willows, Yale, Fairhaven; left out). Plus 6 first-email holds that exist only as a Slack card, with no hold row. Maintenance texts are Supervised by design, but 107 of 119 expired after 24 hours with nobody pressing anything, so the resident never got the "work order created" or "rate the repair" text. That is the largest real-life gap in this ledger; #22487 (open) touches the same Supervised path.
| Date (UTC) | Customer | Property | Kind | Gate | Held text, first line | Outcome | Root cause | Verdict | Design | Harness view |
|---|---|---|---|---|---|---|---|---|---|---|
| Oct 11 to 26 | Situs Group | many | maintenance texts (42) | Supervised | Work order ... created / rate the repair | expired unreviewed | review mode by design | BY DESIGN | shouldSpeak Supervised + bt-e review door: PARTLY | Already frozen: Day 0 tap 8 records the Supervised hold at the maintenance caller. Add a bench episode only for the expiry outcome: assert an expired resident text raises one owner notice. Control: an approved card raises none. |
| Oct 11 to 26 | Situs Group | many | maintenance texts (3) | Supervised | Work order ... created / rate the repair | still pending | review mode by design | BY DESIGN | shouldSpeak Supervised + bt-e review door: PARTLY | Already frozen: Day 0 tap 8 records the Supervised hold at the maintenance caller. Add a bench episode only for the expiry outcome: assert an expired resident text raises one owner notice. Control: an approved card raises none. |
| Oct 11 to 26 | Western Slope | many | maintenance texts (5) | Supervised | Work order ... created / rate the repair | approved and sent | review mode by design | BY DESIGN | shouldSpeak Supervised + bt-e review door: PARTLY | Already frozen: Day 0 tap 8 records the Supervised hold at the maintenance caller. Add a bench episode only for the expiry outcome: assert an expired resident text raises one owner notice. Control: an approved card raises none. |
| Oct 11 to 26 | Western Slope | many | maintenance texts (1) | Supervised | Work order ... created / rate the repair | approved, delivery failed | review mode by design | BY DESIGN | shouldSpeak Supervised + bt-e review door: PARTLY | Already frozen: Day 0 tap 8 records the Supervised hold at the maintenance caller. Add a bench episode only for the expiry outcome: assert an expired resident text raises one owner notice. Control: an approved card raises none. |
| Oct 11 to 26 | Western Slope | many | maintenance texts (65) | Supervised | Work order ... created / rate the repair | expired unreviewed | review mode by design | BY DESIGN | shouldSpeak Supervised + bt-e review door: PARTLY | Already frozen: Day 0 tap 8 records the Supervised hold at the maintenance caller. Add a bench episode only for the expiry outcome: assert an expired resident text raises one owner notice. Control: an approved card raises none. |
| Oct 11 to 26 | Western Slope | many | maintenance texts (1) | Supervised | Work order ... created / rate the repair | rejected | review mode by design | BY DESIGN | shouldSpeak Supervised + bt-e review door: PARTLY | Already frozen: Day 0 tap 8 records the Supervised hold at the maintenance caller. Add a bench episode only for the expiry outcome: assert an expired resident text raises one owner notice. Control: an approved card raises none. |
| Oct 11 to 26 | Western Slope | many | maintenance texts (2) | Supervised | Work order ... created / rate the repair | still pending | review mode by design | BY DESIGN | shouldSpeak Supervised + bt-e review door: PARTLY | Already frozen: Day 0 tap 8 records the Supervised hold at the maintenance caller. Add a bench episode only for the expiry outcome: assert an expired resident text raises one owner notice. Control: an approved card raises none. |
| 2026-09-01 01:51Z | Camellia (JP&Co) | Camellia Apartments | decision_request | supervised review | Kurt Schmittgen got in touch. You're all set , tour confirmed for Monday, August 31 at 2:00 PM at Camellia Apartments, 1235 Grant Street. | rejected | identity/matching | FIXED, UNTESTED | askState verifier + askTheTeam: PARTLY | Day 0 fixture: the June call re-graded in Sept; assert no hold when the tour row exists. Control: a real missing tour holds. |
| 2026-09-03 14:16Z | Camellia (JP&Co) | Camellia Apartments | decision_request | supervised review | Rodulf Parra Fuentes got in touch. ¡Listo, Rodolfo! Le agendo el recorrido para hoy a las cuatro y quince de la tarde. | rejected | identity/matching | FIXED, UNTESTED | askState verifier + askTheTeam: PARTLY | Day 0 fixture: the June call re-graded in Sept; assert no hold when the tour row exists. Control: a real missing tour holds. |
| 2026-09-03 23:24Z | Camellia (JP&Co) | Camellia Apartments | decision_request | supervised review | Jordan Miller got in touch. You're all set, Jordan! Tour confirmed for tomorrow, Wednesday August 12 at 9:00 AM at Camellia Apartments, 1235 Grant Street, Denve | rejected | identity/matching | FIXED, UNTESTED | askState verifier + askTheTeam: PARTLY | Day 0 fixture: the June call re-graded in Sept; assert no hold when the tour row exists. Control: a real missing tour holds. |
| 2026-09-03 23:24Z | Camellia (JP&Co) | Camellia Apartments | decision_request | supervised review | Jordan Miller got in touch. As for the co-signer/guarantor question, the leasing team will be in touch to confirm that for you. | rejected | review mode by design | BY DESIGN | askTheTeam: COVERED | Already frozen by Day 0 tap 8. No fixture needed. |
| 2026-09-20 23:02Z | Western Slope | Northridge Apartments | clara_prospect_email | first-email review | Thanks for reaching out about a 2 bed, 2 bath home for you, your girlfriend, and your friend , we have two-bedroom, two-bath units here at Northridge, with rent | approved and sent | review mode by design | BY DESIGN | shouldSpeak (module Supervised lookup): COVERED | Already frozen by Day 0 tap 8 (Supervised lookup). No fixture needed. |
| 2026-09-21 16:44Z | Western Slope | appfolio-194 | clara_prospect_email | first-email review | The home at 486 32 1/8 Road is a 2 bed, 1 bath, 890 sq ft place renting for $1,250/mo , great fit if you're eyeing an October 1 move-in. | approved and sent | review mode by design | BY DESIGN | shouldSpeak (module Supervised lookup): COVERED | Already frozen by Day 0 tap 8 (Supervised lookup). No fixture needed. |
| 2026-09-21 17:17Z | Western Slope | appfolio-208 | clara_prospect_email | first-email review | The home at 566 Placer Street is no longer available. | approved and sent | review mode by design | BY DESIGN | shouldSpeak (module Supervised lookup): COVERED | Already frozen by Day 0 tap 8 (Supervised lookup). No fixture needed. |
| 2026-09-22 18:34Z | Western Slope | Ember Estates | clara_prospect_email | first-email review | 2863 Florida Street is open now , a 3 bed, 2 bath home at 1435 sqft, $2,775/mo, and it doesn't take any pets so that's an easy fit for you. | rejected, superseded by a recomposed draft | writer quality (prompt rules) | FIXED, UNTESTED | Turn rebuilt layer 3/5 + bt-b facts block: PARTLY | Bench episode (prompt change, shadow per decision 3) on the frozen turn: assert no listing recital, no unbacked claim, one home. Control: a clean prior draft passes. |
| 2026-09-22 18:55Z | Western Slope | 2841 Morningside Court | clara_prospect_email | first-email review | Thanks for your interest in 2841 Morningside Court , it's a spacious 4-bedroom, 2.5-bath single-family home in Orchard Mesa with 1,518 sq. ft., a 2-car garage, | rejected, superseded by a recomposed draft | writer quality (prompt rules) | FIXED, UNTESTED | Turn rebuilt layer 3/5 + bt-b facts block: PARTLY | Bench episode (prompt change, shadow per decision 3) on the frozen turn: assert no listing recital, no unbacked claim, one home. Control: a clean prior draft passes. |
| 2026-09-22 19:50Z | Western Slope | Ember Estates | clara_prospect_email | first-email review | Thanks so much for reaching out to Ember Estates HZ! We've got a nice range of 3-bedroom, 2-bath homes available right now, generally running $2,300-$2,850/mo a | approved and sent | review mode by design | BY DESIGN | shouldSpeak (module Supervised lookup): COVERED | Already frozen by Day 0 tap 8 (Supervised lookup). No fixture needed. |
| 2026-09-22 22:28Z | Western Slope | appfolio-208 | first email | none (dropped before hold) | Sara (card 2951) and Nathan (card 2952) hit a timing race and Clara quietly dropped both first emails | no reply, no hold | sync dropped a message | UNKNOWN | bt-c "renter's message is saved when it arrives": NOT ADDRESSED | Day 0 corpus: both cards; tap 12 order: message row written before the first model round; assert one outcome per card (reply or hold, never neither). |
| 2026-09-22 22:53Z | Western Slope | appfolio-208 | clara_prospect_email | first-email review | Thanks for reaching out! The home at 566 Placer Street is no longer available. | approved and sent | review mode by design | BY DESIGN | shouldSpeak (module Supervised lookup): COVERED | Already frozen by Day 0 tap 8 (Supervised lookup). No fixture needed. |
| 2026-09-22 22:53Z | Western Slope | appfolio-208 | clara_prospect_email | first-email review | Thanks for reaching out! The home at 566 Placer Street is no longer available. | approved and sent | review mode by design | BY DESIGN | shouldSpeak (module Supervised lookup): COVERED | Already frozen by Day 0 tap 8 (Supervised lookup). No fixture needed. |
| 2026-09-23 17:28Z | Western Slope | 2841 Morningside Court | clara_prospect_email | first-email review | I found the exact home the address matches: 2841 Morningside Court, Grand Junction, is a 4-bedroom, 2.5-bath single-family home in Orchard Mesa with 1,518 squar | rejected, superseded by a recomposed draft | writer quality (prompt rules) | FIXED, UNTESTED | Turn rebuilt layer 3/5 + bt-b facts block: PARTLY | Bench episode (prompt change, shadow per decision 3) on the frozen turn: assert no listing recital, no unbacked claim, one home. Control: a clean prior draft passes. |
| 2026-09-23 18:17Z | Western Slope | appfolio-208 | clara_prospect_email | first-email review | Thanks for reaching out about a tour! The home at 566 Placer Street is no longer available, but we do have a couple of other homes open right now , 2863 Florida | rejected | sync dropped a message | FIXED, UNTESTED | bt-c PmsInbox, idempotent writes: NOT ADDRESSED | Day 0 fixture: replay the historical inquiry twice; assert tap 12 adds no new first email for an inquiry older than go-live. Control: a fresh inquiry gets one. |
| 2026-09-23 18:18Z | Western Slope | appfolio-171 | clara_prospect_email | first-email review | The home at 2978 Krista Street is no longer available. A couple of options I do have open right now are 2863 Florida Street (3 bed, 2 bath, 1,435 sq ft, $2,775/ | rejected | sync dropped a message | FIXED, UNTESTED | bt-c PmsInbox, idempotent writes: NOT ADDRESSED | Day 0 fixture: replay the historical inquiry twice; assert tap 12 adds no new first email for an inquiry older than go-live. Control: a fresh inquiry gets one. |
| 2026-09-23 18:36Z | Western Slope | 2841 Morningside Court | clara_prospect_email | first-email review | The home at 2841 Morningside Court is a 4 bedroom, 2.5 bath single-family house in Orchard Mesa, 1,518 sq ft, with a 2-car garage and a fenced-in backyard, avai | rejected, superseded by a recomposed draft | writer quality (prompt rules) | FIXED, UNTESTED | Turn rebuilt layer 3/5 + bt-b facts block: PARTLY | Bench episode (prompt change, shadow per decision 3) on the frozen turn: assert no listing recital, no unbacked claim, one home. Control: a clean prior draft passes. |
| 2026-09-23 18:39Z | Western Slope | 2841 Morningside Court | clara_prospect_email | first-email review | Thanks for reaching out about 2841 Morningside Court , happy to help you and your husband learn more. | approved and sent | review mode by design | BY DESIGN | shouldSpeak (module Supervised lookup): COVERED | Already frozen by Day 0 tap 8 (Supervised lookup). No fixture needed. |
| 2026-09-23 23:37Z | Western Slope | 2841 Morningside Court | clara_prospect_email | first-email review | The home at 2841 Morningside Court is open now. | rejected, superseded by a recomposed draft | writer quality (prompt rules) | FIXED, UNTESTED | Turn rebuilt layer 3/5 + bt-b facts block: PARTLY | Bench episode (prompt change, shadow per decision 3) on the frozen turn: assert no listing recital, no unbacked claim, one home. Control: a clean prior draft passes. |
| 2026-09-24 00:57Z | Western Slope | appfolio-406 | clara_prospect_email | first-email review | I can get you a look at the home at 2863 Florida Street , I have Thursday at 9:00 AM or Friday at 8:00 AM open, which works better for you? | approved and sent | review mode by design | BY DESIGN | shouldSpeak (module Supervised lookup): COVERED | Already frozen by Day 0 tap 8 (Supervised lookup). No fixture needed. |
| 2026-09-24 01:05Z | Western Slope | 2841 Morningside Court | clara_prospect_email | first-email review | The home at 2841 Morningside Court is available now, and it's a great fit for anyone wanting extra space and privacy. I have Thursday at 9:00 AM or Friday at 8: | rejected, superseded by a recomposed draft | writer quality (prompt rules) | FIXED, UNTESTED | Turn rebuilt layer 3/5 + bt-b facts block: PARTLY | Bench episode (prompt change, shadow per decision 3) on the frozen turn: assert no listing recital, no unbacked claim, one home. Control: a clean prior draft passes. |
| 2026-09-24 01:05Z | Western Slope | Ember Estates | clara_prospect_email | first-email review | Thanks for your interest in 2863 Florida Street , it's a 3 bed, 2 bath home at 1,435 sqft, listed at $2,775/mo. | rejected, superseded by a recomposed draft | writer quality (prompt rules) | FIXED, UNTESTED | Turn rebuilt layer 3/5 + bt-b facts block: PARTLY | Bench episode (prompt change, shadow per decision 3) on the frozen turn: assert no listing recital, no unbacked claim, one home. Control: a clean prior draft passes. |
| 2026-09-24 01:05Z | Western Slope | 2841 Morningside Court | clara_prospect_email | first-email review | The home at 2841 Morningside Court is open now, and having all four of you along for the tour is no problem , the application works the same whether it's one ap | approved and sent | review mode by design | BY DESIGN | shouldSpeak (module Supervised lookup): COVERED | Already frozen by Day 0 tap 8 (Supervised lookup). No fixture needed. |
| 2026-09-24 01:13Z | Western Slope | Ember Estates | clara_prospect_email | first-email review | The home at 2863 Florida Street is a 3 bed, 2 bath, 1435 sqft home listed at $2,775/mo. A 1-month lease term would be a leasing-team decision. | rejected, superseded by a recomposed draft | writer quality (prompt rules) | FIXED, UNTESTED | Turn rebuilt layer 3/5 + bt-b facts block: PARTLY | Bench episode (prompt change, shadow per decision 3) on the frozen turn: assert no listing recital, no unbacked claim, one home. Control: a clean prior draft passes. |
| 2026-09-24 01:17Z | Western Slope | 2841 Morningside Court | clara_prospect_email | first-email review | The home at 2841 Morningside Court is a 4 bedroom, 2.5 bath single-family home in Orchard Mesa with 1,518 square feet, a 2-car garage, and a fenced-in backyard, | approved and sent | review mode by design | BY DESIGN | shouldSpeak (module Supervised lookup): COVERED | Already frozen by Day 0 tap 8 (Supervised lookup). No fixture needed. |
| 2026-09-24 01:21Z | Western Slope | Ember Estates | clara_prospect_email | first-email review | Thanks for reaching out about 2863 Florida Street , a 1-month lease term would be a leasing-team decision, but I'd love to get you in to see the place. | approved and sent | review mode by design | BY DESIGN | shouldSpeak (module Supervised lookup): COVERED | Already frozen by Day 0 tap 8 (Supervised lookup). No fixture needed. |
| 2026-09-24 01:47Z | Western Slope | Pomona Park Townhomes | clara_prospect_email | first-email review | Unit 300 at Pomona Park Townhomes is open now, and I'd love to help you find the right fit here. I have Thursday at 9:00 AM or Friday at 8:00 AM for a tour , wh | expired unreviewed | review mode by design | BY DESIGN | shouldSpeak (module Supervised lookup): COVERED | Already frozen by Day 0 tap 8 (Supervised lookup). No fixture needed. |
| 2026-09-24 14:52Z | Western Slope | appfolio-194 | clara_prospect_email | supervised review | I'll check with the team on that. | expired unreviewed | review mode by design | BY DESIGN | shouldSpeak (module Supervised lookup): COVERED | Already frozen by Day 0 tap 8 (Supervised lookup). No fixture needed. |
| 2026-09-25 01:29Z | Western Slope | appfolio-400 | clara_prospect_email | first-email review | Thanks for reaching out about Flats on Elm , happy to help you find the right home here. Let me know what size or layout you're looking for, and I can point you | approved and sent | review mode by design | BY DESIGN | shouldSpeak (module Supervised lookup): COVERED | Already frozen by Day 0 tap 8 (Supervised lookup). No fixture needed. |
| 2026-09-25 15:12Z | Western Slope | 2841 Morningside Court | clara_prospect_email | supervised review | Good morning, Barbara , sorry to hear your husband isn't feeling well, hope he's back on his feet soon. | approved and sent | review mode by design | BY DESIGN | shouldSpeak (module Supervised lookup): COVERED | Already frozen by Day 0 tap 8 (Supervised lookup). No fixture needed. |
| 2026-09-25 15:45Z | Western Slope | 2841 Morningside Court | clara_prospect_email | supervised review | You're all set , your tour is now confirmed for Monday, September 28 at 11:00 AM at 2841 Morningside Court. Glad he'll get some rest today, and we'll see y | approved and sent | review mode by design | BY DESIGN | shouldSpeak (module Supervised lookup): COVERED | Already frozen by Day 0 tap 8 (Supervised lookup). No fixture needed. |
| 2026-09-25 20:14Z | Western Slope | Northridge Apartments | clara_prospect_email | first-email review | Thanks for reaching out about 565 Redcliff Cir , it's currently available for lease, and I'd love to help you find the right fit for an October 9th move-in. Wan | approved and sent | review mode by design | BY DESIGN | shouldSpeak (module Supervised lookup): COVERED | Already frozen by Day 0 tap 8 (Supervised lookup). No fixture needed. |
| 2026-10-04 18:07Z | Western Slope | Pomona Park Townhomes | first email | fact_trace_untraced | Carmen, guest card 2980: first email held by the fact trace | no record (card only) | provenance gate stripped real facts | OPEN | bt-a "Silent holds" + bt-a row 1: NOT COVERED | Day 0 tap 8 census: every held turn has one hold record with a check name; assert hold row exists. Control: a clean first email writes none. |
| 2026-10-04 22:05Z | Camellia (JP&Co) | Camellia Apartments | first email | guard-blocked | Will, guest card 196: first email held by an outbound safety guard | no record (card only) | unknown | UNKNOWN | bt-a "Silent holds": NOT COVERED | Pull the Oct 4 turn snapshot into the Day 0 corpus; tap 6 names the guard. Assert one hold record with the guard name. |
| 2026-10-05 07:39Z | Western Slope | Northridge Apartments | first email | first_email_check_failed | Robert, guest card 2983: held, then four different first emails went out 10:38 to 10:59Z, each naming another home | rerun sent four emails in 21 minutes | identity/matching | UNKNOWN | bt-c "Rerun duplicates the message" (idempotent writes): NOT ADDRESSED | Day 0 fixture: replay the rerun twice on conversation 0d714ce0; assert tap 12 one outbound first email. Control: a genuinely new renter message adds one. |
| 2026-10-06 19:30Z | Camellia (JP&Co) | Camellia Apartments | team_notice_email | supervised review | Lorraine Lago in Unit 410 at Camellia Apartments has completed their lease renewal. Their new rent is $1,150/mo on a 12-month term. | approved and sent | review mode by design | BY DESIGN | shouldSpeak (module Supervised lookup): COVERED | Already frozen by Day 0 tap 8 (Supervised lookup). No fixture needed. |
| 2026-10-06 19:30Z | Camellia (JP&Co) | Camellia Apartments | team_notice_email | supervised review | Deb Self in Unit 505 at Camellia Apartments has completed their lease renewal. Their new rent is $1,150/mo on a 12-month term. | approved and sent | review mode by design | BY DESIGN | shouldSpeak (module Supervised lookup): COVERED | Already frozen by Day 0 tap 8 (Supervised lookup). No fixture needed. |
| 2026-10-07 04:39Z | Western Slope | Northridge Apartments | clara_prospect_email | first-email review (rerun after first_email_check_failed) | Good to hear from you again! The home at 565 Redcliff Cir, unit 585-202 is still available, and it lines up well with your November 1st move-in. | expired unreviewed | provenance gate stripped real facts | OPEN | bt-a row 1: checks compare against our records; Turn rebuilt layer 5: PARTLY | Day 0 fixture on the #22483 envelope: frozen held draft, assert the draft names the real fact (slot, rent) and passes the check. Positive control: the writer's own first draft passes. XFAIL until #22485. |
| 2026-10-11 23:45Z | Western Slope | Pomona Park Townhomes | maintenance_resident_sms | supervised review | A plain thread reply on the POMO-6061 card was read as a rejection, so the work-order text never went | rejected by mistake | review mode by design | FIXED+TESTED | bt-e review door: PARTLY | |
| 2026-10-20 15:00Z | Western Slope | 2841 Morningside Court | clara_prospect_email | supervised review | Hey Spencer, I wasn't able to lock in 9:30 on my end , our system needs a bit more notice for same-day changes, so the earliest I could officially book is 10:00 | expired unreviewed | review mode by design | BY DESIGN | shouldSpeak (module Supervised lookup): COVERED | Already frozen by Day 0 tap 8 (Supervised lookup). No fixture needed. |
| 2026-10-21 16:49Z | Western Slope | Pomona Park Townhomes | clara_prospect_email | first-email review (rerun after fact_trace_untraced) | Thanks for your interest in the 3-bedroom townhome! Just want to flag that Unit 405 runs $2,495/mo, which is above the $1,800/mo budget you mentioned. | expired unreviewed | provenance gate stripped real facts | OPEN | bt-a row 1: checks compare against our records; Turn rebuilt layer 5: PARTLY | Day 0 fixture on the #22483 envelope: frozen held draft, assert the draft names the real fact (slot, rent) and passes the check. Positive control: the writer's own first draft passes. XFAIL until #22485. |
| 2026-10-23 16:33Z | Western Slope (test lead) | Lincoln Apartments | first email | guard-blocked | Cheryl (TEST APPLICANT): first email held by an outbound safety check | test lead, nothing owed | review mode by design | BY DESIGN | shouldSpeak: COVERED | |
| 2026-10-25 20:18Z | Western Slope | 2841 Morningside Court | first email | first_email_check_failed | Thanks for reaching out about 2841 Morningside Court, it's available now, a 4 bedroom, 2 bath at $2,300/mo. | held; staff were emailed 'I couldn't get a clean reply out'; Clara answered his next message 2 min later naming a different home (2980 Emerald Circle) | provenance gate stripped real facts | REGRESSED | bt-a rows 1-2 (gate, retry never ran) + Recipient-by-cause row 1: PARTLY | Day 0 fixture beside first-email-slots (#22483): new tap 'retry attempted', assert yes. Mutation: replant the skip, red. Bench episode for the home switch on the next turn. |
| 2026-10-26 13:44Z | Situs Group | Dahlia Apartments | clara_prospect_email | first-email review | Thanks for reaching out about Dahlia Apartments. We have a one bedroom, one bath home open now at 731 square feet, running $1,049 per month, and it's pet friend | still pending | review mode by design | BY DESIGN | shouldSpeak (module Supervised lookup): COVERED | Already frozen by Day 0 tap 8 (Supervised lookup). No fixture needed. |
| 2026-10-26 15:17Z | Western Slope | Ember Estates | clara_prospect_email | first-email review (rerun after first_email_check_failed) | Thanks for reaching out about Ember Estates. We have a 3-bedroom open right now, running $2,775 to $2,795 a month, around 1,289 to 1,435 square feet. I'd love t | approved and sent | provenance gate stripped real facts | REGRESSED | bt-a row 1: checks compare against our records; Turn rebuilt layer 5: PARTLY | Day 0 fixture on the #22483 envelope: frozen held draft, assert the draft names the real fact (slot, rent) and passes the check. Positive control: the writer's own first draft passes. XFAIL until #22485. |
| 2026-10-26 15:35Z | Western Slope | Northridge Apartments | first email | first_email_check_failed | I've sent the application for Northridge Apartments, it'll come from AppFolio in the next few minutes. | human replied (Kat by text, 15:38Z); application link had gone out | provenance gate stripped real facts | REGRESSED | bt-a "Half a turn" + bt-d staff took thread: NOT ADDRESSED | Fixture #22488 is the pattern; add the half-turn assert: hold record lists 'application sent', retry does not resend. Control: no application ask, no hold. |
| 2026-10-26 15:36Z | Western Slope | Pomona Park Townhomes | leasing_outreach_sms | supervised review | Hi Jessalyn, it's Clara with Pomona Park Townhomes. I've reached out a few times and want to make sure a tour of our 2-bedroom homes still interests you. If it | still pending | missing fact in prompt | OPEN | Turn rebuilt layer 3/5 + bt-b facts block: PARTLY | Bench episode on the frozen touch-4 text: assert it names only the size the renter asked for (or none). Control: a renter who asked for 2 bedrooms gets 2 bedrooms. |
26 incidents from the docs pages and memory notes since Sep 1, and 126 GitHub issues (open, plus closed since Sep 1) that touch a customer path. CI and harness issues were dropped. Customer and root cause on issues come from keyword rules on the title; treat them as rough. "Unit-only" means a merged fix with unit tests and no replay, harness or bench case.
| Kind | Id | Date | Customer | What broke | Fix PRs | Case | Root cause | Verdict | Design | Harness view |
|---|---|---|---|---|---|---|---|---|---|---|
| incident | camellia-shared-routing-revert-2026-09-07 | 2026-09-07 | Camellia (JP&Co) | A shared prompt change took call routing from 39/40 to 0/20 on the bench; reverted in 13 minutes, no real callers hit. | #7260 merged, #7263 merged | unit-test-only | other code bug | FIXED, UNTESTED | outside the turn (phone routing): NOT ADDRESSED | Not a turn case: keep as an ops runbook check, not a fixture. |
| incident | appfolio-robot-login-lockout-2026-09-10 | 2026-09-10 | internal (all three AppFolio accounts) | The browser robot that writes into AppFolio could not sign in at JP&Co, Situs and Western Slope for about a day; no customer-facing write was lost. | #7794 merged, #7796 merged, #7797 merged | unit-test-only | other code bug | FIXED, UNTESTED | PmsInbox adapter: NOT ADDRESSED | Day 0 fixture: replay the same record twice; assert tap 12 row count unchanged. Control: a new record adds one. |
| incident | aws-config-cost-2026-09-13 | 2026-09-13 | internal | Turning on self-hosted CI runners made AWS Config write ~120,000 billable records a day, and every spend alarm was blind to it; a DynamoDB write storm from the AppFolio sync was found in the same swee | #9063 merged, #9134 merged, #9144 merged, #9145 merged, #9146 merged, #9479 merged, #9480 merged, #9482 merged | unit-test-only | other code bug | FIXED, UNTESTED | outside the design (infra): NOT ADDRESSED | Not a turn case: keep as an ops runbook check, not a fixture. |
| incident | voice-outage-2026-09-16 | 2026-09-16 | internal (all customers on the shared line; Camellia and Western Slope callers hit) | A maintenance call-center forwarding change sent an empty value the voice platform rejects, killing every inbound call on the shared line and Willows; 19 failed calls. | #8882 merged, #8923 merged, #8934 merged | unit-test-only | other code bug | FIXED, UNTESTED | outside the turn (voice infra): NOT ADDRESSED | Not a turn case: keep as an ops runbook check, not a fixture. |
| incident | ws-reminder-email-flood-2026-09-16 | 2026-09-16 | Western Slope | Setting Western Slope's leasing inbox as the team-notice address also armed an hourly reminder nobody meant to turn on, sending about 100 emails. | #8926 closed, #8962 merged, #9264 merged | unit-test-only | other code bug | FIXED, UNTESTED | Recipient by cause (who hears): PARTLY | Day 0 tap 8 census: assert the failure reaches #alerts and the record only, never staff. Control: a real hand-off notifies once. |
| incident | ethan-ws-three-day-misses-2026-09-21 | 2026-09-19 | Western Slope | Ethan's email reply was never answered, the follow-up texts kept running and invited him to tour a home Clara had said was rented, his text opened a new unlinked thread, and Clara quoted a comput | #10535 merged, #10538 merged, #10544 merged, #10545 merged | replay/harness | identity/matching | OPEN | identity layer + bt-c every message is a turn: PARTLY | Day 0 fixture from the real conversation: assert tap 1 resolves the right person and home. Control: an unambiguous sender resolves as today. |
| incident | willows-test-mailbox-locked-2026-09-21 | 2026-09-21 | internal | Nightly stress tests drove one consumer Outlook mailbox hard enough that Microsoft locked it for suspected abuse. | #13827 merged, #13882 merged, #15602 merged | none | other code bug | FIXED, UNTESTED | outside the design (test infra): NOT ADDRESSED | Not a turn case: keep as an ops runbook check, not a fixture. |
| incident | ws-transfer-loop-2026-09-22 | 2026-09-22 | Western Slope | Clara transferred callers to Western Slope's main line, which forwards to Clara, so callers looped and were dropped. | #10795 merged | replay/harness | other code bug | REGRESSED | askTheTeam (hand-off to people, not numbers): PARTLY | Bench episode (voice, Day 0 has no voice in v1): assert no transfer goes to a number that rings Clara. Control: a person transfer connects. |
| incident | ws-lead-outbox-claim-race-2026-09-23 | 2026-09-23 | Western Slope | Losing a concurrent-delivery claim counted as a failed attempt, so card 2840's lead parked after 5 tries and never got a first email. | #13411 merged | unit-test-only | sync dropped a message | FIXED, UNTESTED | bt-c idempotent writes: NOT ADDRESSED | Day 0 fixture: replay the same record twice; assert tap 12 row count unchanged. Control: a new record adds one. |
| incident | camellia-joanna-screening-2026-09-24 | 2026-09-24 | Camellia (JP&Co) | The Sep 23-24 caller-ID passthrough made Clara's hand-offs ring Joanna's cell as an unknown number, which her phone screened; 8 transfers screened over ~14 days with no alert. | #11075 merged, #11100 merged, #18008 merged, #18016 merged, #21668 merged | replay/harness | other code bug | FIXED+TESTED | askTheTeam: PARTLY | |
| incident | ws-home-match-from-calls-2026-09-25 | 2026-09-25 | Western Slope | The home matcher found several candidate homes and by design picked none, the card writer only reads a single picked home, so no card was filed and the alert for it had been removed. | #10755 merged, #10903 merged, #11361 merged | none | identity/matching | OPEN | identity layer: PARTLY | Day 0 fixture from the real conversation: assert tap 1 resolves the right person and home. Control: an unambiguous sender resolves as today. |
| incident | ws-cancel-tour-cross-property-2026-09-28 | 2026-09-28 | Western Slope | The text thread sat under Ember Estates and the tour under the same house's own property id, so cancel_tour found "no upcoming tour" and the tour and a follow-up text sequence stayed ar | #13286 merged, #13288 merged | unit-test-only | identity/matching | FIXED, UNTESTED | identity layer (what turn is this): PARTLY | Day 0 fixture from the real conversation: assert tap 1 resolves the right person and home. Control: an unambiguous sender resolves as today. |
| incident | ws-first-email-compose-timeout-2026-09-28 | 2026-09-28 | Western Slope | Clara's first-email compose ran inside a 60 s cron, was killed mid-compose, and the stale claim was then finished as "held, reply by hand" with no retry (card 2968). | #13333 merged, #13625 merged | unit-test-only | other code bug | FIXED, UNTESTED | Recipient by cause, engineering-error row: COVERED | Day 0 tap 8 census: assert the failure reaches #alerts and the record only, never staff. Control: a real hand-off notifies once. |
| incident | ws-error-email-to-client-2026-09-28 | 2026-09-28 | Western Slope | The held-first-email notice sent Clara's own failures to the customer's team inbox ("nothing sent, please reply directly"). | #13343 merged | unit-test-only | other code bug | FIXED, UNTESTED | Recipient by cause, engineering-error row: COVERED | Day 0 tap 8 census: assert the failure reaches #alerts and the record only, never staff. Control: a real hand-off notifies once. |
| incident | guest-card-created-by-unknown-2026-09-28 | 2026-09-28 | Western Slope + Camellia (JP&Co) | A failed or skipped "who created this card" read was stored once and every retry trusted it, so cards 2955/2966/2968 (WS) and 152/158 (JP&Co) were never assigned to Clara. | #13314 merged, #13598 merged | replay/harness | sync dropped a message | FIXED+TESTED | PmsInbox adapter: PARTLY | |
| incident | ws-promise-misattributed-2026-09-28 | 2026-09-28 | Western Slope | After Clara transferred a resident to the maintenance call center, the promise scanner read the human agent's words as Clara's and emailed Western Slope a first-person note signed Clara. | #13422 merged, #13801 merged | unit-test-only | other code bug | FIXED, UNTESTED | askState (is it with the team?): PARTLY | Day 0 fixture on the real turn; assert the shown failure is gone. Positive control from the same corpus. |
| incident | mac-mini-disk-2026-09-28 | 2026-09-28 | internal | About 175 repo checkouts (mostly open-PR worktrees no reaper touches) filled the 460 GB drive despite five cleanup rounds. | none | other code bug | OPEN | outside the design (infra): NOT ADDRESSED | Not a turn case: keep as an ops runbook check, not a fixture. | |
| incident | ember-two-lists-2026-09-29 | 2026-09-29 | Western Slope | AppFolio keeps a clean listings list and a messy unit list; Clara read one or the other depending on how the question was phrased (4 homes vs 26 "vacant"), and filed tours under the communit | #13837 merged | bench | wrong fact in KB | REGRESSED | bt-b facts block, one home per fact: PARTLY | Bench episode on the frozen first reply: assert one home, its own numbers, no counts. Control: single open home quotes its own numbers. |
| incident | camellia-nobody-to-connect-2026-09-29 | 2026-09-29 | Camellia (JP&Co) | The Western Slope routing chain that went live Sep 25 removed Camellia's person destination, so 7 of 11 person-asks got "nobody can pick up" for ~7.5 days. | #21671 merged | replay/harness | other code bug | FIXED+TESTED | askTheTeam: PARTLY | |
| incident | same-day-tour-offers-2026-10-09 | 2026-10-09 | Western Slope + Camellia (JP&Co) | Code and prompt both started the slot search at tomorrow when no day was named, so 48 of 48 first replies skipped today (two WS prospects offered Saturday only on a wide-open Friday). | #18279 merged, #18280 merged, #18268 merged, #18270 merged, #18275 merged, #18297 merged, #18285 merged, #18290 merged | replay/harness | missing fact in prompt | FIXED+TESTED | Turn rebuilt layer 5 (tour windows as facts): COVERED | |
| incident | camellia-transfer-loop-2026-10-13 | 2026-10-13 | Camellia (JP&Co) | With caller-ID passthrough, a transfer to Camellia's main line looked like an outside call and JP&Co's forward sent it straight back to Clara; applicant Victoria asked for a person four | #21636 merged, #21668 merged, #21670 merged, #21671 merged, #21682 merged, #21705 merged | replay/harness | other code bug | FIXED+TESTED | askTheTeam: PARTLY | |
| incident | lincoln-leak-call-2026-10-18 | 2026-10-18 | Western Slope | The phone lookup never read a unit's own street address so a leaking-disposal call got no emergency page, and Jay's reply from the shared leasing mailbox matched a placeholder "JOHN M. | #20946 merged, #20949 merged | unit-test-only | identity/matching | FIXED, UNTESTED | identity layer: PARTLY | Day 0 fixture from the real conversation: assert tap 1 resolves the right person and home. Control: an unambiguous sender resolves as today. |
| incident | ember-first-reply-overshare-2026-10-22 | 2026-10-22 | Western Slope | After a 120 s engine timeout, a rewrite step blind to the main rules emailed Ronald units 361/363/364 plus "opening Nov 2"; earlier emails said "27 3-bedrooms open now" from a roll | #21806 merged, #21814 merged, #21815 merged, #21808 merged, #21830 merged, #21885 merged, #21930 merged, #21886 merged | replay/harness | other code bug | FIXED+TESTED | Turn rebuilt layer 5 ("one home, no counts"): COVERED | |
| incident | situs-first-reply-findings-2026-10-22 | 2026-10-22 | Situs Group | On 119 real Situs inquiries Clara re-asked a size the lead had given (13/30) and handled "asked home is gone" wrong (5/8), and 6 leads were told "no 1-bedrooms open" with five on t | #22061 merged, #22071 closed | replay/harness | missing fact in prompt | OPEN | bt-b facts block: PARTLY | Bench episode on the frozen first reply: assert one home, its own numbers, no counts. Control: single open home quotes its own numbers. |
| incident | ws-former-tenant-reply-2026-10-23 | 2026-10-23 | Western Slope | A Pomona Park former tenant's text woke Clara because one unrelated prospect on the same number made the thread a "prospect" thread; Clara replied from her Twilio line, emailed the team | #22058 merged | unit-test-only | identity/matching | OPEN | shouldSpeak (unknown sender lookup) + identity: PARTLY | Day 0 fixture from the real conversation: assert tap 1 resolves the right person and home. Control: an unambiguous sender resolves as today. |
| incident | bench-door-tour-alert-flood-2026-10-24 | 2026-10-24 | Western Slope (internal cause) | A Clara Bench ids-only loop against the production bench door read tour availability; the read-only guard refused the alert dedupe write, the dedupe read that as an outage and failed open, and every c | #22324 merged, #22327 merged, #22329 merged | replay/harness | other code bug | FIXED+TESTED | Recipient by cause (who hears): COVERED | |
| issue | #12399 | 2026-09-25 | unknown | Leasing desk: staff-booking SMS confirmation fans out to household parties without their own consent | #13313 merged, #13535 merged | replay/harness | identity/matching | FIXED+TESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | |
| issue | #13413 | 2026-09-28 | Western Slope | Promise scanner attributes a transferred-to human agent's promise to Clara and emails the client a first-person 'Clara told the caller…' notice (WS, Kayla Masukawa, 2026-09-28) | #13422 merged, #13801 merged | replay/harness | other code bug | FIXED+TESTED | page silent (keyword, not read): NOT ADDRESSED | |
| live | tour 9dc6f8eb | 2026-10-26 | Western Slope | Lori Ruiz, Pomona Park tour 4:30 PM: hour-before text refused by the AppFolio text door (party not in thread; AppFolio shows her as Unknown Number), fallback email asked for a phone we hold, her 4:14 PM "where do we meet you?" reached only the no-reply notification mailbox; showing never written (tour has no unit, 49 tries) | conv 99e5e0f8 / bdaed90a | Root cause (Oct 26 night): our robot created the card with the phone stored as "(970) ***-****" (machine format, sent verbatim); staff corrected it to "(970) ***-****" Mon 2:58 PM, AppFolio read that as a foreign code and unlinked the texting thread 32 min before the reminder. Her text then arrived as an unknown number; our mirror received it with the right phone but left it pending (no_match: the matching rule needs a name to agree). Fixes: write phones to AppFolio in its own display format at create and fill; file an unknown-number text whose phone belongs to one person; stop ingesting AppFolio no-reply notification emails as conversations Oct 27: Fede: one root cause, one fix; robot PR appfolio-browser-agent#633 (display format at create and fill) merged and proven at Willows (card 49 written as (404) 285-9387, read back, restored, Oct 27 02:12Z); no-reply notifications fix #22616 open and armed (30-day replay: 5 records would be dropped, all notifications, 0 real people); Willows proof after deploy; unknown-number filing #22612 and Outlook bounce #22613 filed, not built. | ROOT CAUSE FOUND | slice K | Fede corrected the phone on the card Oct 26 night | |
| issue | #13363 | 2026-09-28 | Western Slope | Engine first email: AppFolio unit label ("Unit 313") leaks into prose for a single-family home, and deposit is hedged "typically one month's rent" | #13471 merged, #13568 merged, #17375 merged | replay/harness | sync dropped a message | FIXED+TESTED | PmsInbox adapter, bt-c idempotent writes (keyword, not read): NOT ADDRESSED | |
| issue | #13009 | 2026-09-28 | unknown | Renewals review: Clara's text replies to residents are not held while the company review switch is on | #13629 merged, #13682 merged, #13690 merged, #13704 merged | replay/harness | hold handling (review door) | FIXED+TESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | |
| issue | #13541 | 2026-09-29 | test/internal | Retire clara-guest-card-first-email-compose.ts (the composer) for guest-card first email | #13519 merged | replay/harness | other code bug | FIXED+TESTED | page silent (keyword, not read): NOT ADDRESSED | |
| issue | #13900 | 2026-09-29 | Camellia (JP&Co) | First-email reply held by the content check: draft named no home and asked for no tour while homes were open (Willows proof 2026-09-29 21:42Z) | #13908 merged | replay/harness | provenance gate stripped real facts | FIXED+TESTED | bt-a row 1 (checks compare against our records) (keyword, not read): PARTLY | |
| issue | #13989 | 2026-09-30 | Western Slope | record_tour_notes can't save a reply to the check-in Clara just sent (matter saved without inquiryId) | #14004 merged, #14005 merged, #14063 merged | replay/harness | provenance gate stripped real facts | FIXED+TESTED | bt-a row 1 (checks compare against our records) (keyword, not read): PARTLY | |
| issue | #13991 | 2026-09-30 | Western Slope | Staff desk: confirmation texts read the whole note back and add an 'or is that already set up outside this system?' re-ask | #14004 merged, #14005 merged, #14063 merged | replay/harness | tours and staff-desk logic | FIXED+TESTED | page silent (keyword, not read): NOT ADDRESSED | |
| issue | #13990 | 2026-09-30 | Western Slope | book_showing_for re-asks staff for what the data knows: single-unit home rejected, second address not resolved | #14005 merged, #14020 merged, #14031 merged | replay/harness | hold handling (review door) | FIXED+TESTED | page silent (keyword, not read): NOT ADDRESSED | |
| issue | #18465 | 2026-10-10 | test/internal | voice maintenance: water dripping through the ceiling near a light fixture was filed without escalate_to_human (the post-call sweep paged anyway) | #19137 merged, #19368 merged | replay/harness | other code bug | FIXED+TESTED | page silent (keyword, not read): NOT ADDRESSED | |
| issue | #19077 | 2026-10-12 | Camellia (JP&Co) | Available-units list shows past vacancy dates as future hold dates | #19122 merged, #19109 merged, #19111 merged, #19138 merged | replay/harness | hold handling (review door) | FIXED+TESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | |
| issue | #19314 | 2026-10-13 | unknown | Tour hold: a failed calendar read is reported as 'slot no longer open' | #19309 merged | replay/harness | hold handling (review door) | FIXED+TESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | |
| issue | #19315 | 2026-10-13 | Western Slope | No-tour-without-a-name: SMS real-path E2E right after deploy (hold → name → confirm) | #19309 merged | replay/harness | hold handling (review door) | FIXED+TESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | |
| issue | #20966 | 2026-10-19 | unknown | staff desk: a first name matches a stranger whose display name is an email, and the booking goes on that person | #21008 merged, #20978 merged, #21012 merged | replay/harness | tours and staff-desk logic | FIXED+TESTED | bt-b facts block (keyword, not read): PARTLY | |
| issue | #20980 | 2026-10-19 | Western Slope | eval(real-turns): scripted desk world cannot reproduce 4 adversary defects (unit-needed refusal, reschedule confirm, list_units, homes/knowledge keys) | #21012 merged | replay/harness | other code bug | FIXED+TESTED | page silent (keyword, not read): NOT ADDRESSED | |
| issue | #20967 | 2026-10-19 | unknown | staff desk: a card note for a named home lands on another home's open check-in tour | #21041 merged, #20978 merged, #21012 merged, #21051 merged | replay/harness | provenance gate stripped real facts | FIXED+TESTED | bt-a row 1 (checks compare against our records) (keyword, not read): PARTLY | |
| issue | #20968 | 2026-10-19 | unknown | staff desk: lockbox texts with no code (a question, a moved lockbox) are sent to the code tool and Clara asks staff for a code | #21020 merged, #20978 merged, #21012 merged | replay/harness | tours and staff-desk logic | FIXED+TESTED | page silent (keyword, not read): NOT ADDRESSED | |
| issue | #20969 | 2026-10-19 | unknown | staff desk: a multi-unit home with no unit given loops on 'which unit', or Clara picks a unit herself and books it as taken | #21047 merged, #20978 merged, #21012 merged, #21060 merged | replay/harness | tours and staff-desk logic | FIXED+TESTED | page silent (keyword, not read): NOT ADDRESSED | |
| issue | #20970 | 2026-10-19 | unknown | staff desk: 'put her back to the original time' is refused once for missing time words, though Clara read the original time | #21059 merged, #20978 merged, #21012 merged, #20798 merged | replay/harness | tours and staff-desk logic | FIXED+TESTED | page silent (keyword, not read): NOT ADDRESSED | |
| issue | #21625 | 2026-10-21 | unknown | Supervised hold bypassed when the topic classifier labels a renewal-tool turn "other" | #21646 merged, #22334 merged | replay/harness | hold handling (review door) | FIXED+TESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | |
| issue | #12177 | 2026-09-25 | unknown | Two inactive guest cards of one person still trade the pointer every tick (#12141 review) | #12182 merged | unit-test-only | identity/matching | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #12450 | 2026-09-26 | unknown | First email: a stuck-prep takeover should cancel the tour the dead engine turn booked | #13625 merged, #17318 merged | unit-test-only | tours and staff-desk logic | FIXED, UNTESTED | page silent (keyword, not read): NOT ADDRESSED | Day 0 fixture on the real record; assert the reported failure is gone. Positive control from the same corpus. |
| issue | #13395 | 2026-09-28 | Western Slope | Lead outbox: a consumer that loses the concurrent-delivery claim parks the lead after 5 attempts instead of re-queueing (WS 2840 Jolynn never got a first email) | #13411 merged, #13448 closed, #18757 merged, #21057 merged | unit-test-only | other code bug | FIXED, UNTESTED | page silent (keyword, not read): NOT ADDRESSED | Day 0 fixture on the real record; assert the reported failure is gone. Positive control from the same corpus. |
| issue | #13659 | 2026-09-29 | unknown | Email-lead engine reply (#13658) sends via sendClaraProspectEmail, not the held first-email pipeline | #13658 merged, #13711 merged, #13714 merged | unit-test-only | hold handling (review door) | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #14681 | 2026-10-01 | unknown | Merge hold: claude.yml's @claude re-review posts review=success without the hold gate (must land before deleting auto-merge-all) | #14688 merged, #14689 closed, #14700 merged | unit-test-only | hold handling (review door) | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #14642 | 2026-10-01 | unknown | heavy lane: closed culprit issues are not matched, so a reopened red files a duplicate | #17447 merged, #17446 merged | unit-test-only | identity/matching | FIXED, UNTESTED | page silent (keyword, not read): NOT ADDRESSED | Day 0 fixture on the real record; assert the reported failure is gone. Positive control from the same corpus. |
| issue | #15247 | 2026-10-02 | unknown | Robot/fastlane/rig test callers unrecognized by isTestSender , real AppFolio guest card created (card 2963, 2939/2940) | #15248 merged | unit-test-only | identity/matching | FIXED, UNTESTED | identity layer / PmsInbox (keyword, not read): PARTLY | Day 0 fixture from the real card: assert tap 1 resolves the right person and card. Control: a clean sender resolves as today. |
| issue | #15561 | 2026-10-02 | test/internal | Flaky test: src/__tests__/sync-renewal-voice-prompt.test.ts | #17595 merged | unit-test-only | sync dropped a message | FIXED, UNTESTED | PmsInbox adapter, bt-c idempotent writes (keyword, not read): NOT ADDRESSED | Day 0 fixture: replay the record twice and after a parked write; assert tap 12 rows and one retry. Control: a new record adds one. |
| issue | #15962 | 2026-10-03 | unknown | Held-text approve re-run drops the staffAsked flag, so the staff-requested application text never goes | #16054 merged, #15983 merged | unit-test-only | hold handling (review door) | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #16337 | 2026-10-04 | unknown | team-notify SMS quote: focusClause drops a statement that holds the antecedent | #16343 merged, #16329 merged | unit-test-only | hold handling (review door) | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #16912 | 2026-10-06 | unknown | tours: booking pick falls back to any-free-agent when the tour grid read fails (#16891 follow-up) | #16923 merged | unit-test-only | tours and staff-desk logic | FIXED, UNTESTED | page silent (keyword, not read): NOT ADDRESSED | Day 0 fixture on the real record; assert the reported failure is gone. Positive control from the same corpus. |
| issue | #16927 | 2026-10-06 | unknown | video Clara: a silent turn is saved as a visible '<silent>' Clara message (from #16901 review) | #16948 merged, #17016 merged | unit-test-only | hold handling (review door) | FIXED, UNTESTED | Recipient by cause + bt-a "Silent holds" (keyword, not read): NOT COVERED | Day 0 tap 8 census cell: every held or failed turn has exactly one hold record with a check name plus an #alerts line. Mutation: drop the record write, red. |
| issue | #17351 | 2026-10-07 | Western Slope | A first reply held by a draft check is never re-drafted and only our own inbox is told | #17408 merged, #22060 merged | unit-test-only | provenance gate stripped real facts | FIXED, UNTESTED | bt-a row 1 (checks compare against our records) (keyword, not read): PARTLY | Day 0 fixture on the #22483 envelope: frozen draft, assert the check passes the real fact. Control: the writer's first draft passes. |
| issue | #17748 | 2026-10-08 | Western Slope | A main-line booking's guest card never gets created: the create still names the front-door placeholder | #17753 merged | none | identity/matching | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #17749 | 2026-10-08 | Western Slope | A note waiting on a guest card never sends after the card is created | #17753 merged | none | identity/matching | FIXED, UNTESTED | identity layer / PmsInbox (keyword, not read): PARTLY | Day 0 fixture from the real card: assert tap 1 resolves the right person and card. Control: a clean sender resolves as today. |
| issue | #18335 | 2026-10-09 | Western Slope | Tenant calls and texts bump their old, converted guest card to the top of Prospects | #18343 merged, #18384 merged, #21631 merged | unit-test-only | identity/matching | FIXED, UNTESTED | identity layer / PmsInbox (keyword, not read): PARTLY | Day 0 fixture from the real card: assert tap 1 resolves the right person and card. Control: a clean sender resolves as today. |
| issue | #18526 | 2026-10-10 | unknown | Office-notice hold: fallback timer stops on in_flight and gives up after 30 s (lease is 3 min) | #19112 merged | unit-test-only | identity/matching | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #18573 | 2026-10-10 | unknown | Renewal text with no outbound line fails silently as dispatched_failed | #19304 merged, #19080 merged, #19300 merged | unit-test-only | other code bug | FIXED, UNTESTED | Recipient by cause + bt-a "Silent holds" (keyword, not read): NOT COVERED | Day 0 tap 8 census cell: every held or failed turn has exactly one hold record with a check name plus an #alerts line. Mutation: drop the record write, red. |
| issue | #19101 | 2026-10-12 | test/internal | Held-item follow-up posts for test/sandbox companies still reach #alerts (withdraw/expire paths) | #19142 merged | none | hold handling (review door) | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #19211 | 2026-10-13 | unknown | pms-mirror: handler-level controls for the mirrored wake/hold/unreadable wiring (#19161 follow-up) | #19222 merged | unit-test-only | sync dropped a message | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #19324 | 2026-10-13 | Western Slope | Text and email tour bookings never request an AppFolio guest card and stay filed on the company main line | #19325 merged | none | identity/matching | FIXED, UNTESTED | identity layer / PmsInbox (keyword, not read): PARTLY | Day 0 fixture from the real card: assert tap 1 resolves the right person and card. Control: a clean sender resolves as today. |
| issue | #19326 | 2026-10-13 | test/internal | PMS mirror: hold, don't drop, an inbox thread whose party has no property | #19338 merged, #19349 closed, #19374 merged | unit-test-only | sync dropped a message | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #19405 | 2026-10-14 | unknown | pms-mirror: a cold AppFolio session pauses the inbox watch silently (no alert) | #19410 merged, #19485 merged, #19668 merged, #20072 merged | unit-test-only | sync dropped a message | FIXED, UNTESTED | Recipient by cause + bt-a "Silent holds" (keyword, not read): NOT COVERED | Day 0 tap 8 census cell: every held or failed turn has exactly one hold record with a check name plus an #alerts line. Mutation: drop the record write, red. |
| issue | #19494 | 2026-10-14 | unknown | Renewals Supervised: guard blocks a correct reply that repeats the tenant's own rent/term ask; held draft becomes "I've noted your request on your renewal." (X-35) | #19498 merged | unit-test-only | provenance gate stripped real facts | FIXED, UNTESTED | bt-a row 1 (checks compare against our records) (keyword, not read): PARTLY | Day 0 fixture on the #22483 envelope: frozen draft, assert the check passes the real fact. Control: the writer's first draft passes. |
| issue | #19573 | 2026-10-14 | unknown | Showing write with no home: delete the 'please add it in AppFolio' staff email, hold-and-retry instead of parking | #19579 merged, #19645 merged, #19750 merged, #19794 merged | unit-test-only | sync dropped a message | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #19595 | 2026-10-14 | unknown | pms-mirror: on a one-company AppFolio database, a row with no property is held instead of going to that company's front door | #19602 merged, #19605 merged | unit-test-only | sync dropped a message | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #19596 | 2026-10-14 | unknown | PMS mirror: AppFolio inbox adapter drops the main party, holding tenant texts as no_property | #19605 merged | unit-test-only | sync dropped a message | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #19635 | 2026-10-14 | test/internal | PMS mirror: a guest-card prospect's text is held no_property on a two-company database | #19662 merged, #19668 merged | unit-test-only | sync dropped a message | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #19636 | 2026-10-14 | test/internal | PMS mirror: unresolvable holds are silent (no engineering alert) | #19668 merged | unit-test-only | sync dropped a message | FIXED, UNTESTED | Recipient by cause + bt-a "Silent holds" (keyword, not read): NOT COVERED | Day 0 tap 8 census cell: every held or failed turn has exactly one hold record with a check name plus an #alerts line. Mutation: drop the record write, red. |
| issue | #19610 | 2026-10-14 | unknown | pms-mirror: #19602 and #19605 disagree on unknown_phone_number, so unknown-number texts stay held on a one-company database | #19900 merged | unit-test-only | sync dropped a message | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #19713 | 2026-10-14 | test/internal | guest-card sync: an INACTIVE card still rewrites the linked person's name (Willows battle test, card 216) | #19901 merged | unit-test-only | sync dropped a message | FIXED, UNTESTED | PmsInbox adapter, bt-c idempotent writes (keyword, not read): NOT ADDRESSED | Day 0 fixture: replay the record twice and after a parked write; assert tap 12 rows and one retry. Control: a new record adds one. |
| issue | #19847 | 2026-10-15 | unknown | Approved list row joins a still-running offer job and sends nothing, silently | #19861 merged | unit-test-only | hold handling (review door) | FIXED, UNTESTED | Recipient by cause + bt-a "Silent holds" (keyword, not read): NOT COVERED | Day 0 tap 8 census cell: every held or failed turn has exactly one hold record with a check name plus an #alerts line. Mutation: drop the record write, red. |
| issue | #20202 | 2026-10-16 | Western Slope | pms-mirror: unmatched prospect email filed into a new conversation on the placeholder property | #20210 merged | unit-test-only | identity/matching | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #20371 | 2026-10-16 | test/internal | text door: staff and vendor texts are held with no_pms_thread when the AppFolio door is on , they must always use Clara's line | #20384 merged, #20982 merged | unit-test-only | sync dropped a message | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #20385 | 2026-10-16 | test/internal | text door: two manager replies (confirmation-review and mass-message confirm) are not marked as going to staff, so they would be held when the AppFolio door is on | #20410 merged | unit-test-only | sync dropped a message | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #20687 | 2026-10-17 | unknown | Fair-housing gate passes voucher-holder screening on full rent and credit score | #20695 merged | unit-test-only | hold handling (review door) | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #21337 | 2026-10-20 | unknown | A company list reviewer can approve a per-offer Supervised renewal hold from the admin page | #21341 merged, #21430 merged | unit-test-only | hold handling (review door) | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #21366 | 2026-10-20 | test/internal | lead.received terminalState says 'replied' when the first email was held compose-failed | #21371 merged, #21614 merged | unit-test-only | missing fact in prompt | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #21362 | 2026-10-20 | unknown | Office 'not renewing' email is held under Supervised and never composed after a 24h expiry; a staff note should always go out | #21426 merged | unit-test-only | hold handling (review door) | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #21360 | 2026-10-20 | unknown | Per-offer renewal hold: can a customer company admin approve on the admin page, or only the Slack card / PropFlow admin? | #21430 merged | unit-test-only | hold handling (review door) | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #21365 | 2026-10-20 | test/internal | A new lead whose first email fails to compose 3 times is held for good: no later retry, no draft to release, lead stays silent | #21446 merged | unit-test-only | hold handling (review door) | FIXED, UNTESTED | Recipient by cause + bt-a "Silent holds" (keyword, not read): NOT COVERED | Day 0 tap 8 census cell: every held or failed turn has exactly one hold record with a check name plus an #alerts line. Mutation: drop the record write, red. |
| issue | #21504 | 2026-10-20 | test/internal | Approved held reply that fails to send is dead and its recorded tenant intent is lost | #21507 merged, #21522 closed | unit-test-only | sync dropped a message | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #21535 | 2026-10-20 | Situs Group | Prospects list shows "No source recorded" for guest cards with a real AppFolio source (Situs 28 of 68) | #21536 merged | none | identity/matching | FIXED, UNTESTED | identity layer / PmsInbox (keyword, not read): PARTLY | Day 0 fixture from the real card: assert tap 1 resolves the right person and card. Control: a clean sender resolves as today. |
| issue | #21926 | 2026-10-22 | unknown | Text door holds a booked-tour text as no_pms_record when Clara's create_card resolved to an existing card (no af.propertyId written back) | #21930 merged | unit-test-only | sync dropped a message | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #21938 | 2026-10-22 | Western Slope | renewals: outreach-started renewals save no 'respond by' day, so a Western Slope packet holds on it | #21981 merged, #22051 merged | unit-test-only | hold handling (review door) | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #21944 | 2026-10-23 | unknown | new-lease prepare rung: tell the office when a packet is parked or held (#21941 follow-up) | #21977 merged, #22187 merged | unit-test-only | hold handling (review door) | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #22014 | 2026-10-23 | test/internal | New-lease packet: a second application on the same prospect is silently dropped (workflow id is per prospect, not per application) | #22017 merged, #22444 merged | unit-test-only | sync dropped a message | FIXED, UNTESTED | Recipient by cause + bt-a "Silent holds" (keyword, not read): NOT COVERED | Day 0 tap 8 census cell: every held or failed turn has exactly one hold record with a check name plus an #alerts line. Mutation: drop the record write, red. |
| issue | #22020 | 2026-10-23 | unknown | Tour-time guard holds a correct refusal that states the tour hours ("tours run 9:00 AM to 6:00 PM") | #22024 merged, #22018 merged, #22036 merged | unit-test-only | provenance gate stripped real facts | FIXED, UNTESTED | bt-a row 1 (checks compare against our records) (keyword, not read): PARTLY | Day 0 fixture on the #22483 envelope: frozen draft, assert the check passes the real fact. Control: the writer's first draft passes. |
| issue | #22034 | 2026-10-23 | unknown | Tour-time guard holds a correct reply when the incoming-text tour check (not a Clara tool) found the alternatives | #22036 merged, #22485 open | unit-test-only | provenance gate stripped real facts | FIXED, UNTESTED | bt-a row 1 (checks compare against our records) (keyword, not read): PARTLY | Day 0 fixture on the #22483 envelope: frozen draft, assert the check passes the real fact. Control: the writer's first draft passes. |
| issue | #21987 | 2026-10-23 | unknown | Renewal packet: hold when the respond-by day falls on or after the new term's start | #22051 merged | unit-test-only | hold handling (review door) | FIXED, UNTESTED | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #22054 | 2026-10-23 | Camellia (JP&Co) | Application sync: our copy shows Camellia application 119 as approved, AppFolio says Decision Pending | #22133 merged, #22134 merged, #22118 merged | unit-test-only | sync dropped a message | FIXED, UNTESTED | PmsInbox adapter, bt-c idempotent writes (keyword, not read): NOT ADDRESSED | Day 0 fixture: replay the record twice and after a parked write; assert tap 12 rows and one retry. Control: a new record adds one. |
| issue | #19866 | 2026-10-15 | unknown | List email reply: a term change with no model instruction is silently dropped on approve | none | sync dropped a message | OPEN | Recipient by cause + bt-a "Silent holds" (keyword, not read): NOT COVERED | Day 0 tap 8 census cell: every held or failed turn has exactly one hold record with a check name plus an #alerts line. Mutation: drop the record write, red. | |
| issue | #21114 | 2026-10-19 | test/internal | AppFolio sync: every desk-booked showing at The Willows unit 102 fails (showing picker unit search returns nothing), runs 7-11 | none | sync dropped a message | OPEN | PmsInbox adapter, bt-c idempotent writes (keyword, not read): NOT ADDRESSED | Day 0 fixture: replay the record twice and after a parked write; assert tap 12 rows and one retry. Control: a new record adds one. | |
| issue | #21113 | 2026-10-19 | test/internal | staff desk: duplicate-card question uses the full name and runs over 120 chars with long names | none | tours and staff-desk logic | OPEN | page silent (keyword, not read): NOT ADDRESSED | Day 0 fixture on the real record; assert the reported failure is gone. Positive control from the same corpus. | |
| issue | #21542 | 2026-10-20 | unknown | Tours: probe the host once and delete the old 'confirmed, no calendar to sync' arm (follow-up to #21540) | #21540 merged | none | sync dropped a message | OPEN | PmsInbox adapter, bt-c idempotent writes (keyword, not read): NOT ADDRESSED | Day 0 fixture: replay the record twice and after a parked write; assert tap 12 rows and one retry. Control: a new record adds one. |
| issue | #21537 | 2026-10-20 | test/internal | Tour yes by email is never recorded: no host confirm text, staff line shows no confirmation | none | tours and staff-desk logic | OPEN | page silent (keyword, not read): NOT ADDRESSED | Day 0 fixture on the real record; assert the reported failure is gone. Positive control from the same corpus. | |
| issue | #21514 | 2026-10-20 | unknown | Held text: Twilio line gets a real send-once marker so a throw with unknown carrier outcome can be retried safely | none | hold handling (review door) | OPEN | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. | |
| issue | #21512 | 2026-10-20 | unknown | Held reply supersede: a sent-then-bounced email hold must not be relabelled 'replaced, not sent' | none | hold handling (review door) | OPEN | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. | |
| issue | #21506 | 2026-10-20 | unknown | Approved held EMAIL reply whose send fails has no retry and no action floor | #21507 merged | unit-test-only | hold handling (review door) | OPEN | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #21472 | 2026-10-20 | unknown | A long AI outage on first-email compose is silent for up to 24 h (no ops card while deferring) | none | other code bug | OPEN | Recipient by cause + bt-a "Silent holds" (keyword, not read): NOT COVERED | Day 0 tap 8 census cell: every held or failed turn has exactly one hold record with a check name plus an #alerts line. Mutation: drop the record write, red. | |
| issue | #21470 | 2026-10-20 | unknown | /review feed still shows Approve/Reject on per-offer renewal holds to company admins; every click 403s | none | hold handling (review door) | OPEN | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. | |
| issue | #21455 | 2026-10-20 | test/internal | Parked PMS write-backs (assign card to Clara, record 'Email Sent') are never retried | none | sync dropped a message | OPEN | PmsInbox adapter, bt-c idempotent writes (keyword, not read): NOT ADDRESSED | Day 0 fixture: replay the record twice and after a parked write; assert tap 12 rows and one retry. Control: a new record adds one. | |
| issue | #21454 | 2026-10-20 | test/internal | Releasing a prep-held first email writes a fake inbound message on the conversation | none | hold handling (review door) | OPEN | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. | |
| issue | #21453 | 2026-10-20 | test/internal | A refused first text in the follow-up cadence is never retried and raises no alert | none | sync dropped a message | OPEN | Recipient by cause + bt-a "Silent holds" (keyword, not read): NOT COVERED | Day 0 tap 8 census cell: every held or failed turn has exactly one hold record with a check name plus an #alerts line. Mutation: drop the record write, red. | |
| issue | #21450 | 2026-10-20 | unknown | A tenant who is also a vendor contact still loses a renewal reply sent to Clara's own line (door off) | none | identity/matching | OPEN | identity layer / PmsInbox (keyword, not read): PARTLY | Day 0 fixture from the real card: assert tap 1 resolves the right person and card. Control: a clean sender resolves as today. | |
| issue | #21442 | 2026-10-20 | unknown | A failed Temporal signal leaves a permanent 'delivery failed' flag on an offer hold even after the text is delivered | none | hold handling (review door) | OPEN | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. | |
| issue | #21440 | 2026-10-20 | test/internal | Renewal offer text renders 'coming up on your lease end date' when the lease end is blank | none | other code bug | OPEN | page silent (keyword, not read): NOT ADDRESSED | Day 0 fixture on the real record; assert the reported failure is gone. Positive control from the same corpus. | |
| issue | #21364 | 2026-10-20 | test/internal | Renewal reader asks 'hold or send as listed?' when a hold is followed by 'leave it as is for now' | #21370 merged | unit-test-only | hold handling (review door) | OPEN | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #21627 | 2026-10-21 | test/internal | Supervised hold card for a tenant's "no" omits "and tells the office" | none | hold handling (review door) | OPEN | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. | |
| issue | #21618 | 2026-10-21 | unknown | Guest-card notes policy (a): every Clara contact and every outcome lands on the AppFolio guest card, the same way at every customer, no setting | #21622 merged, #21630 merged, #21637 merged, #21656 merged | replay/harness | identity/matching | OPEN | identity layer / PmsInbox (keyword, not read): PARTLY | Day 0 fixture from the real card: assert tap 1 resolves the right person and card. Control: a clean sender resolves as today. |
| issue | #21604 | 2026-10-21 | unknown | Reasoning-preamble guard runs before the history-stamp strip and leaves no trace on the redraft path | none | provenance gate stripped real facts | OPEN | bt-a row 1 (checks compare against our records) (keyword, not read): PARTLY | Day 0 fixture on the #22483 envelope: frozen draft, assert the check passes the real fact. Control: the writer's first draft passes. | |
| issue | #21603 | 2026-10-21 | unknown | Reasoning-preamble guard: narrow the tools and third-person markers; fix the tú boundary | none | provenance gate stripped real facts | OPEN | bt-a row 1 (checks compare against our records) (keyword, not read): PARTLY | Day 0 fixture on the #22483 envelope: frozen draft, assert the check passes the real fact. Control: the writer's first draft passes. | |
| issue | #21597 | 2026-10-21 | unknown | Tour reminders: a failed reminder email raises no engineering alert (never-silent rule, follow-up to #21595) | none | tours and staff-desk logic | OPEN | Recipient by cause + bt-a "Silent holds" (keyword, not read): NOT COVERED | Day 0 tap 8 census cell: every held or failed turn has exactly one hold record with a check name plus an #alerts line. Mutation: drop the record write, red. | |
| issue | #21596 | 2026-10-21 | unknown | Self-guided tours: Clara still says "text me when you get there, I'll send the code" after the code goes out one hour before (#21589) | none | missing fact in prompt | OPEN | page silent (keyword, not read): NOT ADDRESSED | Day 0 fixture on the real record; assert the reported failure is gone. Positive control from the same corpus. | |
| issue | #21577 | 2026-10-21 | Camellia (JP&Co) | Lead marked not_interested and guest card set inactive from a STOP that a later START reversed | none | identity/matching | OPEN | identity layer / PmsInbox (keyword, not read): PARTLY | Day 0 fixture from the real card: assert tap 1 resolves the right person and card. Control: a clean sender resolves as today. | |
| issue | #21848 | 2026-10-22 | Western Slope | Cadence close-out text garbles two Emerald Circle addresses ("2980 Emerald Circle -2978 Emerald Circle is no longer available") | none | missing fact in prompt | OPEN | bt-b facts block (keyword, not read): PARTLY | Bench episode on the frozen reply: assert only facts on our records, one home. Control: a clean home quotes its own numbers. | |
| issue | #21813 | 2026-10-22 | unknown | Tour held for the renter's name and confirmed by update_prospect is not texted to an email lead's number | #21815 merged | unit-test-only | hold handling (review door) | OPEN | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #21812 | 2026-10-22 | test/internal | Email-reply tour booking attached to another person's guest card that still holds the shared number in AppFolio | #21815 merged, #21886 merged, #21930 merged | unit-test-only | identity/matching | OPEN | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #21798 | 2026-10-22 | unknown | Follow-up #21784: approved email hold can be dropped on re-run (self-guided delay note / leave check-in) | none | provenance gate stripped real facts | OPEN | bt-a row 1 (checks compare against our records) (keyword, not read): PARTLY | Day 0 fixture on the #22483 envelope: frozen draft, assert the check passes the real fact. Control: the writer's first draft passes. | |
| issue | #21745 | 2026-10-22 | unknown | First AppFolio-door text to an email lead is held forever when the guest card does not yet carry the number; never retried, nobody told | none | identity/matching | OPEN | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. | |
| issue | #21744 | 2026-10-22 | unknown | Tour reminders text any number we hold with no consent check, including numbers found in an email signature (Oct 19 decision says found numbers get no texts) | none | provenance gate stripped real facts | OPEN | bt-a row 1 (checks compare against our records) (keyword, not read): PARTLY | Day 0 fixture on the #22483 envelope: frozen draft, assert the check passes the real fact. Control: the writer's first draft passes. | |
| issue | #21742 | 2026-10-22 | Western Slope | Contacts before the guest card exists never reach the card (WS card 3035: Clara's phone-number ask and the renter's three replies missing) | none | identity/matching | OPEN | identity layer / PmsInbox (keyword, not read): PARTLY | Day 0 fixture from the real card: assert tap 1 resolves the right person and card. Control: a clean sender resolves as today. | |
| issue | #21735 | 2026-10-22 | test/internal | Clara invents a reason ("occupied") when a unit is held by an approved application; her two availability tools disagree about a held unit | none | missing fact in prompt | OPEN | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. | |
| issue | #21728 | 2026-10-22 | test/internal | Lead card can hold a listing-level placeholder as its apartment (appfolio-45-2-bedroom), which then becomes a booked tour's unit | none | identity/matching | OPEN | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. | |
| issue | #21726 | 2026-10-22 | test/internal | Showing write retries without end when the unit has no AppFolio id; should park like the no-home hold | none | sync dropped a message | OPEN | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. | |
| issue | #21724 | 2026-10-22 | test/internal | Text booking does not carry the unit the renter chose (tour saved without a unit, or with a listing placeholder) | none | identity/matching | OPEN | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. | |
| issue | #21673 | 2026-10-22 | unknown | Renewal packet at 'prepare': page wording, held-vs-failed colour, version skew, park-by-ref tests (follow-up to #21672) | #21672 merged | unit-test-only | missing fact in prompt | OPEN | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #22122 | 2026-10-23 | unknown | Learner: same.last_document scores the household's last FILLED value but the fact is named fact.lastDocument.*; pick one meaning before the park supplies it (score.ts:128, from #22117 review) | none | identity/matching | OPEN | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. | |
| issue | #22103 | 2026-10-23 | unknown | Booking confirmation names two homes and calls a held unit 'occupied' | none | missing fact in prompt | OPEN | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. | |
| issue | #22045 | 2026-10-23 | Western Slope | A held first reply (guard block) at a live customer should raise an alert, not sit silent | none | provenance gate stripped real facts | OPEN | Recipient by cause + bt-a "Silent holds" (keyword, not read): NOT COVERED | Day 0 tap 8 census cell: every held or failed turn has exactly one hold record with a check name plus an #alerts line. Mutation: drop the record write, red. | |
| issue | #22021 | 2026-10-23 | unknown | new-lease packet: pin the run to the approved application from the writer, and hold-path card per run (follow-up to #22017) | none | hold handling (review door) | OPEN | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. | |
| issue | #21961 | 2026-10-23 | unknown | Clara-sends: a company with two or more renewal recipes in use holds every packet | none | hold handling (review door) | OPEN | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. | |
| issue | #22248 | 2026-10-24 | unknown | PMS text door: an unreadable guest-card list is treated as 'no card yet' and waits silently up to 6 h | none | sync dropped a message | OPEN | Recipient by cause + bt-a "Silent holds" (keyword, not read): NOT COVERED | Day 0 tap 8 census cell: every held or failed turn has exactly one hold record with a check name plus an #alerts line. Mutation: drop the record write, red. | |
| issue | #22230 | 2026-10-24 | test/internal | Tour day check: hold a Clara reply whose tour day disagrees with the booked record | #22233 merged | none | provenance gate stripped real facts | OPEN | bt-a row 1 (checks compare against our records) (keyword, not read): PARTLY | Day 0 fixture on the #22483 envelope: frozen draft, assert the check passes the real fact. Control: the writer's first draft passes. |
| issue | #22432 | 2026-10-25 | test/internal | First-email tour-times check reads only English and Spanish, so a correct French reply is held; and the writer's one redraft did not run | #22483 open, #22485 open, #22488 open | none | provenance gate stripped real facts | OPEN | bt-a row 1 (checks compare against our records) (keyword, not read): PARTLY | Day 0 fixture on the #22483 envelope: frozen draft, assert the check passes the real fact. Control: the writer's first draft passes. |
| issue | #22420 | 2026-10-25 | unknown | Four unstamped engineering-failure exits in the agent loop still end in the team hand-off email | #22417 merged | unit-test-only | other code bug | OPEN | page silent (keyword, not read): NOT ADDRESSED | Day 0 fixture on the real record; assert the reported failure is gone. Positive control from the same corpus. |
| issue | #22419 | 2026-10-25 | unknown | Reply clock exits on guardSilencedBy before reading turnOutcome, so a draft_check_held hold never pages | #22417 merged | unit-test-only | provenance gate stripped real facts | OPEN | bt-a row 1 (checks compare against our records) (keyword, not read): PARTLY | Day 0 fixture on the #22483 envelope: frozen draft, assert the check passes the real fact. Control: the writer's first draft passes. |
| issue | #22376 | 2026-10-25 | unknown | Rejecting a stale first-email hold cancels a tour the email already booked | none | hold handling (review door) | OPEN | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. | |
| issue | #22375 | 2026-10-25 | unknown | Stale held-draft copy points reviewers to Reject, which pauses the building's rollout | none | hold handling (review door) | OPEN | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. | |
| issue | #22360 | 2026-10-25 | Western Slope | Conversation turnOutcome.why says replied_not_sent after a first email was sent | none | missing fact in prompt | OPEN | page silent (keyword, not read): NOT ADDRESSED | Day 0 fixture on the real record; assert the reported failure is gone. Positive control from the same corpus. | |
| issue | #22326 | 2026-10-25 | test/internal | Held-reply review page calls a prospect's reply "Text to a resident" | none | missing fact in prompt | OPEN | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. | |
| issue | #22325 | 2026-10-25 | unknown | Conversation page shows no held marker for a Supervised hold | none | hold handling (review door) | OPEN | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. | |
| issue | #22310 | 2026-10-25 | unknown | Outage page still says customers got the holding note (text and email now get nothing) | none | missing fact in prompt | OPEN | Recipient by cause + bt-a "Silent holds" (keyword, not read): NOT COVERED | Day 0 tap 8 census cell: every held or failed turn has exactly one hold record with a check name plus an #alerts line. Mutation: drop the record write, red. | |
| issue | #22301 | 2026-10-25 | unknown | Availability check: on a held reply, say it was held, not that words were removed | #22296 merged | replay/harness | provenance gate stripped real facts | OPEN | bt-a row 1 (checks compare against our records) (keyword, not read): PARTLY | Day 0 fixture on the #22483 envelope: frozen draft, assert the check passes the real fact. Control: the writer's first draft passes. |
| issue | #22481 | 2026-10-26 | Situs Group | Held first-touch email hold should carry the conversation id when one exists | #22482 merged | none | hold handling (review door) | OPEN | bt-e review door (one hold record, option B) (keyword, not read): NOT ADDRESSED | Day 0 tap 10 render cell on the hold: assert one card shape, four actions, no default mentions. Then a production screenshot. |
| issue | #13396 | 2026-09-28 | Western Slope | First email home facts: unit number leaks into the street number ("1240 Elm" → "1250 Elm", "565 Redcliff" → "575 Redcliff"; WS 2738/1365) | none | other code bug | BY DESIGN | page silent (keyword, not read): NOT ADDRESSED |
Every case that is not fixed and tested carries its own line in the tables above. They fall into six fixture shapes, all inside the Day 0 harness or the bench:
Holds: every EscalationHold row in production (453, one query on the entity index, no sampling), the first-email intent rows each hold points to, and 598 Slack messages matching "Held for review" or "FIRST EMAIL HELD". Issues: open issues and those closed since Sep 1, filtered by label and title words, with the PR that closed or referenced each. Incidents: pages under docs with incident, RCA or root cause in the title, and the memory incident notes. Test case: a merged fix counts as tested only when a replay, harness or bench case names it. Verdict REGRESSED means the same signature came back after a merged fix. The root cause list adds one bucket, "writer quality (prompt rules)", for the Sep 23 recomposes, "hold handling (review door)" for bugs in the hold path itself, "tours and staff-desk logic", and "other code bug" for issues that fit none. The same event can appear as a hold, an issue and an incident (Trang is in all three), so totals across the tables overlap. First-email intents have no index, so a held intent with neither a hold row nor a Slack card is not in this ledger; the battle test found held turns with no hold row, so there are likely more. Open issues that a merged PR only references stay OPEN. Raw ledger: real-life-ledger.json, one object per case.
Proposed, pending review. Read-only: no edits, no sends, no production writes. Code read at origin/main 137f883e7f (Oct 26 15:41Z). Merged after that snapshot and folded in below: #22485 (16:11Z, the first email's tour slots count as this turn's calendar read), #22483 (its fixture), #22482 (a held first touch is an alert, not an outage), #22425 (the maintenance relay records when it did not send). Open and relevant: #22487 (Supervised holds every resident message, emergencies included). Window for counts: Sep 26 to Oct 26. "The three customers" are Western Slope, Situs Group and JP&Co (Camellia); test companies (Willows, Fairhaven, Yale) are left out of every count. Long dashes in quoted log strings are shown as hyphens.
| Gates that can stop, hold or silence a message | about 255 distinct (284 rows, about 25 repeat across sections), in 15 families |
| Rewrite layers still on main | about 60 that change words (24 in the main loop: voice and video, internal-text strips, staff-facing swaps, hand-off rewrites and salvage; 26 side writers; 10 at the send doors), plus 5 cosmetic transport steps. 11 more were removed Oct 13 to 25. |
| Left evidence of firing at the three customers, 30 days | about 22 gates and 7 rewrite layers (several of those layers have since been removed) |
| Fired only at test companies, or not at all where we can see | most of the rest; about 30 gate types and about 17 rewrite layers leave no record or log at all, so their count is unknown, not zero |
| Holds at the three customers | 127 hold rows (124 to a renter), 110 of them expired with nobody looking; 5 first emails still held on the intent row; 3 parked replies; 103 held-item cards in customer channels |
| Sampled right / wrong, all gates | SAMPLE of 24 across 6 reason families, not 10 per gate: 13 wrong (the message was fine), 4 right, 7 unclear |
| Sampled, policy holds only (Supervised) | SAMPLE of 12: 10 wrong, 0 right, 2 unclear. "Wrong" here means fine words held by a setting that did what it says; the harm is the expiry. |
| Sampled, correctness gates only (fact and provenance) | SAMPLE of 12: 3 wrong, 4 right, 5 unclear. All 3 wrong are provenance gates on a first email. |
Data gaps that carry into every count: the shouldSpeak decision line exists only since Oct 26 04:52 (2 lines); turn snapshots only since Oct 24 (10 Western Slope turns marked held, only about 4 match a record); the first-email intent row keeps only its current state and never the check name, so a hold later sent shows only on its Slack card; no record was found for draft salvage or the bounced-email text fallback; 3,353 agent traces could not be tied to a company; the inbound-processor logs start Sep 26 16:37Z. Labels are one reader's judgment.
Each row is a family; its values apply to every gate listed in it. The appendix has every gate on its own row with its file:line, release path and the literal log string to count it by. Decides by: FACT checks our own records; PROVENANCE checks how the writer learned it; POLICY is a company setting, the clock, or a role. Master page verdicts use the page's own words (COVERED, PARTLY, NOT COVERED, NOT ADDRESSED) and point to clara-coworker-loop-v3-2026-10.
| # | Family and gates | Where (origin/main) | What it checks | Decides by | When it fires | Who releases, from where | Added, and why | 30-day fires at the three customers (source) | Sampled right / wrong | (a) Master page section | (b) Fact protected, verdict | (c) Fixtures: true fire / no false fire |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| H1 | Supervised reply holds: rules R1 to R7, the text and email row lines, the email 'hook never asked' fallback, the four routed backstops, router answers with no turn (maintenance acks), team-answer hold, first email (R5), unthreaded prospect email | supervised-reply-rule.ts:130-147; should-speak.ts:100, :152; process-envelope.ts:621, :828, :926; supervised-reply-hold.ts:111, :199; process-inbound-email.ts:2033; process-pms-email.ts:461; inbound-processor handler.ts:1011; send-clara-prospect-email.ts:397 | The module this reply belongs to (by topic, by recipient being a prospect, or by the router's final module) is set to Supervised at the building. R2: a renewal action was deferred this turn. R6: the topic check gave no answer, so it fails closed. | POLICY (module mode); R2 PROVENANCE; R6 fail-closed | Silent hold WITH record: EscalationHold (clara_reply_sms or clara_prospect_email), card in the company channel, reviewer email. A tenant gets one fixed 'we got it' text. | App /review, /admin/escalation-holds, /held-messages; Slack THREAD reply 'approve N' (relayed by Agent Smith; the Slack buttons only rate); reviewer email reply; rollout auto-release. Auto-closed by expiry, supersede, teammate replied. | #13906 Sep 30 (drafts mode); #16514 Oct 5 (Supervised email replies went out with nobody approving); #16644 Oct 5 (routed maintenance reply refused at the door and lost); #22450, #22473 Oct 26 (all eight callers behind shouldSpeak) | Clara-written reply holds at the three customers: 5 prospect emails (Western Slope 4, Situs 1). Source: EscalationHold. The shouldSpeak log line exists only since Oct 26 04:52 (2 lines, both reply). The 72 Western Slope 'reply row withheld' log lines are a normal step before the first-email decision, not holds. | 2 sampled: 1 wrong (Emelyn, Situs: fine draft with tour times, still pending), 1 unclear (Spencer: 'I'll let the team know' with nothing behind it). Policy holds: a 'wrong' here means the words were fine, the hold did what the setting says. | #inv-interfaces shouldSpeak: COVERED (one place, one log line; 'module Supervised' is one of the four thin lookups). #machine-gates-h rows 3 to 6 and 9. | Protects no fact; it is Fede's choice to see messages first. Verdict: KEEP AS A CHECK (thin layer). | True fire: Day 0 tap 8, hold decision at all 8 callers, 5,515 frozen cells (exists). No false fire: the same tap's Live-building and urgent-repair cells return reply (exists). |
| H2 | Automatic maintenance and renewal texts held (work-order-created notice, rate-your-repair ask, vendor relay, handyman texts, renewal offer, reminder, change-term, lease-ending warning, monthly batch park) | maintenance-text-review.ts:63, :161; maintenance/rating.ts:854; handyman-text-review.ts:24; renewal-text-review.ts:73; renewal-email-review.ts:57; renewal-reply-review.ts:58, :95; renewal-auto-start.ts:2690; renewal-batch/batch-gate.ts | The module is Supervised and the repair is not emergency or urgent (renewals: or the month's list is not approved). | POLICY | Silent hold WITH record: EscalationHold (maintenance_resident_sms, maintenance_vendor_sms, renewal_*), Slack card. Approve re-runs every check. | Same doors as H1; renewal list approval releases parked renewal texts. | #12977 Sep 28 (renewals); #16508 Oct 5, #16855, #16919 Oct 6 (maintenance); #20060 Oct 16 (handyman, Gera's Oct 15 ask) | 118 renter-facing maintenance holds (Western Slope 73, Situs 45): 106 expired unreviewed, 6 approved, 5 pending, 1 discarded. 1 vendor text. Renewals: 0 at real customers in the window. Source: EscalationHold; 86 Slack cards (some holds got no card or a grouped card). | 10 sampled: 9 wrong (fixed template notices for work orders that exist, rating asks on completed jobs), 0 right, 1 unclear (rating ask on a job still dispatched). | #rll-holds: 'the largest real-life gap in this ledger'. #bt-e review door: PARTLY. #tenants: hold card in the property's Slack. | Protects no fact: the words are a fixed template and the facts behind them (work order exists, job completed) are in our records. Verdict: KEEP only as Fede's Supervised choice; the open widening to emergencies (#22487) goes the other way. | True fire: Day 0 tap 8 maintenance caller (exists). No false fire / expiry: none today; the ledger names 'an expired resident text raises one owner notice; control: an approved card raises none'. |
| H3 | Hold lifecycle closers: 24h expiry, newer draft supersedes, teammate took the thread, stale draft (4h or a past slot), approved but not sent (no time zone, renewal gone), approved waits for quiet hours, one held-reply ack | held-message-rule.ts:64, :95; held-message-max-age.ts:17; held-text.ts:1007, :1375; bake-gate.ts:464, :1938, :2662; held-reply-ack.ts | How old the hold is; whether a newer reply, a staff reply or a past tour slot makes the words stale; whether the building has a clock. | POLICY (time) for expiry; FACT for supersede, teammate, past slot | Hold closed unsent WITH record (resolvedBy system:draft-expired, system:draft-superseded, system:human-replied); thread note 'Expired, not sent'. The renter hears nothing. | Nobody after it closes; a person writes by hand. | #11417 Sep 24 (Western Slope stale drafts Sep 22 to 23); #14241 Sep 30 (supersede); #16608 Oct 5 (teammate); #17249 Oct 7 (no time zone) | 110 of 124 renter-facing holds at the three customers expired with nobody looking (106 maintenance texts, 3 prospect emails: Robert, Spencer, Morgan; 1 vendor). Source: EscalationHold resolvedBy. | Not sampled on its own; the expired set includes the 9 wrong maintenance texts above and Robert and Morgan (unclear). | #rll-holds; #bt-d (held draft closes when a person takes the thread): PARTLY; #bt-e review door: NOT ADDRESSED for expiry. | Supersede, teammate and past slot protect a fact (are these words still true) and stay. Expiry protects nothing about the words. Verdict: KEEP the fact closers; expiry DEFER TO NEW ENGINE (review door). | True fire for teammate closer: staff-took-thread fixture (open PR, #22488). Expiry: none today. |
| H4 | First-email draft checks (one writer retry, then hold): no tour times offered, one price for all sizes, unit label on a house, hedged money, implies prior contact, size question with no tour ask, dash, 'gone' claim on an unknown home, unbacked promise, names no home, fact trace, how-much-is-open, PII, bare hand-off body, deferred ask not backed | guest-card-first-email-draft-checks.ts:462 to :483; guest-card-engine-turn.ts (firstEmailDeterministicFindings, detectNoHomeFactBody, fact trace, findAvailabilityDisclosures); clara-guest-card-engagement-prep.ts (deferred ask) | Text of the draft against what the turn was handed: did it offer the slots it had, name a home, quote the right rent, trace every fact to the listing or the knowledge base. | FACT for most; no-tour-times reads requireTourTimes (set from the turn's slot read); fact trace is PROVENANCE (did the sentence come from a known source) | Silent hold WITH record on the intent row only (CLARA_FIRST_EMAIL_INTENT heldReason first_email_check_failed, no_home_named, fact_trace_untraced; the check name is not stored), card in the company channel, #alerts line. No EscalationHold row unless rerun into review. | Rerun script only (scripts/guest-card-rerun-one.ts); a rerun at a Supervised building lands in review (H1). | #12074 Sep 25 (fact trace); #13351 Sep 28 (170-card replay: no tour times); #21085 Oct 19; #22178 Oct 24 (dash); #22249 Oct 24 (block only, one retry) | Western Slope: 4 'held after retry' log lines, 7 first-email held cards (Anthony, Trang, Patrick, Carmen, Morgan, Robert, Cheryl); 5 intent rows still held. Before Oct 24 the same checks redrafted: 17 draft-check redrafts, 15 no-home redrafts, 5 fact-trace strips. Sources: CloudWatch, intent rows, Slack. | 6 sampled: 3 wrong (Anthony Oct 25, Trang Oct 26: fine drafts, retry never ran; Patrick Oct 26: 'I've sent the application' was true), 1 right (Carmen: 'I'm not able to pull up live tour times'), 2 unclear (Robert, Morgan). | #bt-a rows 1, 2, 4: PARTLY / PARTLY / NOT COVERED (silent holds); #bt-changes items 1 to 4; 'Turn rebuilt' layer 5 hands in the facts. | Protects: a real slot, the home's own rent and size, an action that really ran. Verdict: KEEP the text checks as block-only; fact trace and the tour-times requirement DELETE WHEN DATA LANDS (compare to our records, not to the source). | True fire and no false fire for tour times: first-email-slots fixture (#22483, merged today; a time not in the list still fires). Patrick: none today (ledger: tap 11 shows the application intent, tap 8 the hold). Retry ran: none today (ledger: new tap 'retry attempted'; mutation: replant the skip). |
| H5 | Tour time not from this turn's calendar read (main loop and first email) | agents/clara/lib/agent/tour-time-this-turn-gate.ts:249, :316; conversation-manager.ts:14820, hold :14866 | Every tour time in the reply must appear in a calendar read made this turn (tool rows, times offered in the last half hour, the tour pipeline's openings, and since 16:11Z today the first email's handed-in slots). | PROVENANCE | Retry with 'do not name any tour time', then park (main loop: msg_held row, subReason tour_time:not_in_this_turn_read, no Slack page). On a first email the retry's draft then fails no-tour-times (H4). | Main loop: nobody. First email: rerun script. | #20338 Oct 16; widened #21497 Oct 20 (stale times); #22036 Oct 23 (pipeline slots, issue #22034); #22485 Oct 26 16:11Z (first email slots) | Direct log: 0 at real customers (1 Fairhaven, 1 test property), visible only through DecisionFallback lines. Through H4: 2 Western Slope first emails (Anthony, Trang). Source: CloudWatch, intent snapshots. | 2 of 2 wrong (both drafts offered real slots). | #bt-a row 1: PARTLY ('the machine map still lists it and the page never says it is deleted'). | Protects: a real open slot. Verdict: DELETE WHEN DATA LANDS (check the time against the slot list in our records). | First-email-slots fixture (#22483): true fire on a time outside the list, no false fire on the handed-in windows (exists). |
| H6 | Main-loop post-draft checks (one writer retry, then park): weekday/date, unbooked tour confirmation, work-order claim, unit count, outbound policies (promise backing, booking promise, availability disclosure), work-order confirmation missing, PII, no dashes, hallucination guard, final send gate, anomaly gate (tool failure or timeout) | conversation-manager.ts:14744 to :16085 (weekday :14744, unbooked :14899, WO claim :14960, unit count :15029, policies :15088, WO confirmation :15254, PII :15336, dashes :15409, hallucination :15461, send gate :15933, anomaly :15967); guards/hallucination-guard.ts:861; turn-integrity/anomaly-review-gate.ts:296 | The final text against our records (weekday, counts, offer figures, tour on file) or against what the turn did (a booking, a filing, a backed promise). | FACT (weekday, unit count, PII, dash, hallucination vs the offer); PROVENANCE (unbooked tour, WO claim, WO confirmation, promise backing) | Retry once, then park WITH record (message row blockedByGuard, msg_held or msg_blocked); Slack page for some; hallucination opens an automatic hand-off. The renter hears nothing. | Only three park kinds can be released (fair_housing_hold, weekday_date_guard, dash_in_reply) through a Slack thread reply that runs a script (texts only). Every other park: nobody; a person writes by hand. | #4061 Jul 17 (anomaly gate, after a Camellia tour incident that day: 006, git archaeology Oct 26); #14348 Oct 1 (empty reply sends nothing, Fede Sep 30: 006); #485 Apr 27 (hallucination); #12543 Sep 26 (wrong weekday to a Western Slope prospect); #14353 Sep 30 (Camellia 'See you at 1' after both bookings failed); #21145 Oct 19 (no counts); #22236 Oct 24 (dashes); #22296 Oct 25 (block only) | Parked at the three customers: 3 (Western Slope 1 anomaly, JP&Co 1 send gate 'unbacked promise in retry', JP&Co 1 hallucination). Trace blocks: booking promise 7 (6 cleared by the regenerate, 1 not), anomaly 1. Turn snapshots since Oct 24: 10 Western Slope turns marked held, only about 4 match a record. Sources: message rows, AgentTrace, TurnSnapshot. | 3 sampled: 2 right (Will Sep 30: '1 PM works' when the tour was 4:45 PM; Ember Oct 22: empty draft after a timeout), 1 unclear (Ibrahim Oct 26). | #turn-rebuilt: 'Checks that only block' COVERED in principle; #bt-a row 4 silent holds NOT COVERED (no release path); askState replaces the 'it's with the team' promise check. | Protects: the date, the tour on file, the offer, a filing. Verdict: fact checks KEEP AS A CHECK; provenance ones DELETE WHEN DATA LANDS (askState and the turn's action ledger answer them from records). | Promise without backing: goldmine repro 04 (exists). Others: none today. |
| H7 | In-loop forced-tool retries: first-turn leasing tool gate, availability claim, tour-time claim, typed tool call | conversation-manager.ts ~12547, :12717 to :12860, ~12870; availability-claim-gate.ts:294; tour-time-claim-gate.ts:328 | The reply names availability or tour times, or ends turn 1, without the matching lookup tool this turn. | PROVENANCE | Redraft with a forced tool call; if that fails the degraded reply is SENT anyway (never holds). | None needed. | Apr 8 (first-turn gate); #10579 Sep 21 (Camellia: units named with no lookup); #14132 Sep 30 (Western Slope tour times with no read) | Tour-time claim: Western Slope 5 turns, JP&Co 4. Availability claim: Western Slope 4, JP&Co 2. Source: CloudWatch. | Spot-read, not tallied (the pre-retry text is not kept): mostly right or unclear. | #turn-rebuilt: 'Goes away: the forced call-3-lookup-tools-first gate'. COVERED. | Protects: the facts named come from a lookup. Verdict: DELETE WHEN DATA LANDS (facts block hands them in). | None today. |
| H8 | Turn silencers before or around the model: runaway ceiling, muted person, tapback, farewell loop, bare closer, escalated thread (human takeover), team-answer takeover, model chooses end_conversation, video abandoned, opted-out person, team-inbox silence, conversation ended, staff sender, resident scope | guards/runaway-ceiling.ts:170; guards/tapback-reaction.ts:222; guards/farewell-loop.ts:138; guards/closer-skip.ts:426; conversation-manager.ts:1665 (escalated gate), :7618 (mute), :16641 (team inbox), :17106 (opted out); tools/end-conversation.ts | The shape of the inbound (reaction, goodbye, bare thanks), who owns the thread, consent, or the model's own choice to stay quiet. | FACT (text shape, consent); PROVENANCE (who owns the thread); POLICY (mute) | Nothing sent, nothing said. Escalated and runaway write a status change; mute writes one ops card; the rest log only (opted-out: a trace step only). | Staff unmute, release the thread, or the person texts START; otherwise none needed. | #5047 Jul 30 (Clara traded goodbyes with another machine); #6519 Aug 30 (closers); #6526 Aug 28 (mute); #5985 Aug 20 (team inbox); #21302 Oct 20 (opted out) | end_conversation Western Slope 5, JP&Co 1; bare closer 2 / 2 / 2 (WS / Situs / JP&Co); conversation ended WS 3, JP&Co 3; staff sender WS 14 lines in 1 conversation; resident scope JP&Co 3; escalated 13 lines with no company id. Source: CloudWatch. | Not sampled. | #inv-interfaces rulings: opt-out is a thin lookup; 'is a person already on it' is reasoned by the model, never timed (COVERED). Tapback, farewell, closer: NOT ADDRESSED. | Opt-out, mute, tapback protect consent and sense. Verdict: KEEP AS A CHECK; the escalated-thread gate DEFER TO NEW ENGINE (the model reads the staff thread). | Silence after hand-off: goldmine class1 and its green control (exists). Staff took the thread: #22488 (open). |
| H9 | Send-door refusals: module off, Supervised with no approval (text and email doors), company not live, STOP / consent, test or stress recipient, canary, AppFolio code numbers, flipped line, contact ceiling, AppFolio text door (media, over 910 characters), dispatch policy skips (thread escalated, human replied) | twilio/client.ts:904, :1111 to :1249; company-live-gate.ts:~400 to :495; off-means-off.ts; dispatch-envelope.ts:247, ~570; appfolio-inbox-text.ts | Last line before the carrier: is this module on, approved, the company live, the person consenting, the recipient real. | POLICY (module, live, test scope); FACT (consent) | Nothing sent. Module refusals write a MODULE_REFUSAL row and page Sentry ('an upstream lane missed its hold'); email writes an EMAIL_LOG BLOCKED row; text-door test-scope, not-live and dispatch skips log only. | Nobody (turn the module on, go live, the person texts START). The AppFolio text door re-drives itself. | #1100 May 17 (dispatch policy); #6831 Sep 3 (text door routing; compliance origin unconfirmed: 006, git archaeology Oct 26); #9177 Sep 17 (company-live email hold, one seam: 006); #9988 Sep 19 (38 lanes, zero readers of the module flag); #12142 Sep 25; #14235 Sep 30; #16495 Oct 5; #19463 Oct 14 | MODULE_REFUSAL at real customers: 0 (17 rows, all local). Company-not-live: Western Slope 5 lines. Test/stress: test companies only. Sources: DynamoDB, CloudWatch. | Not sampled (no customer fires to sample). | #machine-gates-h rows 7 to 11; #inv-interfaces: shouldSpeak replaces 'the human replied policy skip' (COVERED). | Consent, test scope and live state are one-right-answer lookups. Verdict: KEEP AS A CHECK. | Day 0 tap 8 door callers (text.door, email.door) exist for the Supervised branch. Others: none today. |
| H10 | Lead intake and first-email consumer skips: company not live, leasing off, Clara's own card or home, backfill import, seen before the source was on, arrived before go-live, already applied, source not in scope (Situs list), already engaged, no email, staff person, folded card, historical lead, mailbox lane made first touch, Zillow relay never answered by email, relay with no renter address | guest-card-reply-intake.ts; lead-scope.ts; leads/consumers/clara-first-email-consumer.ts; clara-guest-card-reply-composer.ts; clara-guest-card-engagement-prep.ts:445; email/decide-email-send-mode.ts:150, :170 | Whether this card is one Clara should answer at all. | POLICY (live, scope); PROVENANCE (who made the card, when it was seen); FACT (applied, no email) | Nothing sent, nothing said; log line only (company not live at prep writes heldReason org-not-live with no alert). Going live does not re-drive skipped cards. | Nobody. | #9633 Sep 18; #14504 Sep 30; #16461 Oct 5 ('He already applied. Leave it alone.'); #19004 Oct 12; #20019 Oct 15 (Situs list); #8896 Sep 16 (Zillow relay) | JP&Co: 13 first touches made by the mailbox lane (a routing decision). Other skips not separately counted. Source: intent rows, CloudWatch. | Not sampled. | #bt-c (PmsInbox, one row per message, idempotent writes): NOT ADDRESSED for the skips; #rll 'sync dropped a message' 30 cases. | Protects: answering only real, in-scope, new leads. Verdict: KEEP AS A CHECK; the staff-created-card rule Fede gave on Oct 23 is not in code (see below). | None today; the ledger names a Day 0 replay of a historical inquiry twice (control: a fresh inquiry gets one). |
| H11 | Prospect cadence and reminder gates: follow-up stop, company not live, identical text already sent, unit-type check, quiet hours, reminder freshness | temporal/activities/prospect-outreach.ts:1809; leasing follow-up gate; leasing_outreach hold (unit-type) | Whether the next follow-up still fits (a reply came, the company is live, the text is new, the home type matches the property). | FACT (unit type, duplicate); POLICY (live, quiet hours) | Touch skipped, log only; the unit-type check writes an EscalationHold (leasing_outreach_sms). | Unit-type hold: review doors as H1. Others: nobody. | #2470 Jun 18 (quiet hours); 62dbb95391 Oct 1, Supervised holds the cadence text (006, git archaeology Oct 26) | Follow-up stop: Western Slope 3. 'Cadence touch held: company not live': 34 (company not split). Identical text: 5. Unit-type hold: 1 (Jessalyn, pending). Source: renewal worker / Temporal logs, EscalationHold. | 1 sampled: right (Jessalyn: '2-bedroom homes' at Pomona Park, where every home is 3 or 4 bedrooms). | NOT ADDRESSED by the page beyond 'checks that only block'. | Protects: the home type on file. Verdict: KEEP AS A CHECK. | None today. |
| H12 | Fair housing | compliance/outbound-safety.ts:344 (ALERT_ONLY_POLICY_IDS); first-email safety pipeline (outbound-safety-pipeline.ts) | A model judge reads the reply for fair-housing risk. | POLICY (model judge) | Reply loop: flags and SENDS, Slack flag (since Oct 19). First email: holds (guard fair_housing_hold) on the intent row. | First email: rerun script, or a Slack thread reply on the review card. | #21159 Oct 19 (reply loop stopped holding) | 1 first-email hold at JP&Co (Will, Oct 4, later sent). Source: intent row, Slack card. | 1 sampled: unclear (text reads fine; the 'familial status' trigger looks like a false positive). | 'Stays: the fair-housing judge.' COVERED. | Protects a legal line, not a fact. Verdict: KEEP AS A CHECK; the reply loop and the first email disagree on what it does. | False block: goldmine repro 03 fair-housing-false-block (exists). True fire: none today. |
| H13 | Hold could not be written (text and prospect email) | renewal-reply-review.ts:351; send-clara-prospect-email.ts:415 | The hold write threw, or there is no building, conversation or person to key it. | FACT | Nothing sent; #alerts post WITH the words; critical log. If the post fails too, the words are lost. | Nobody; a person replies by hand. | #13906 Sep 30 (driver ruling: never sent live, never dropped silently); #11269 Sep 23 | Not counted in this pull. | Not sampled. | #notice-engine-rule: an engineering error goes to #alerts and the record only. COVERED. | Verdict: KEEP (it is the alarm for a broken hold). | None today. |
| H14 | Quiet hours (renewal outreach, prospect cadence, maintenance pages, collections, application sends, voice callbacks, month-to-month warning) | quiet-hours.ts:226; renewal.ts:897, :1377, :2202, :2609; prospect-outreach.ts:1809; maintenance-comms.ts:1440; voice-callback.ts:162 | 8:00 to 21:00 at the property (per handyman for pages). | POLICY (time) | Waits and retries, except renewal outreach, which returns 'dispatched' without sending (the touch may be lost; not traced). | Automatic at window end. | #1001 May 16 (Camellia tenant voice-called at 10:21 PM, May 15); #11366 Sep 23 (time zone fix) | Not counted (renewal outreach did not run at real customers in the window). | Not sampled. | NOT ADDRESSED. | Verdict: KEEP AS A CHECK; the renewal no-op needs a look before anyone relies on it. | None today. |
| H15 | Voice gates: outbound call door, never transfer to a number that rings Clara, settings refuse a looping catch-all, muted caller with no destination, missed-call note gate | voice/initiate-outbound-call.ts:278; properties/clara-line-loop.ts:156, :175; api/settings/call-routing/route.ts:221; voice/personalization/route.ts:4246; promises/guards/missed-call-note-gate.ts:196 | Consent, test scope, module, Supervised and contact budget for calls; whether a transfer destination loops back to Clara. | FACT + POLICY | Call not placed or destination dropped; a looping catch-all is refused in settings. The outbound call door has no quiet-hours or not-live branch (each caller owns it). | Fix the destination or the setting. | #6526 Aug 28; #13381 Sep 28 (Western Slope forwarded main line looped, Sep 22); #14995 Oct 1; #21636 Oct 21 (bounce alert) | Not counted in this pull. | Not sampled. | #rh-d2 'Voice in v1?' is an open decision. NOT ADDRESSED. | Verdict: KEEP AS A CHECK. | None today. |
Rules that are not in code.
Fede, Oct 22: "nothing edits Clara's message after she writes it; checks only block; fix the writer instead." Text and email replies in the main loop now retry the writer once and then hold; the rest below still edits words. "Still on main" is stated in the When-it-fires column.
| # | Family and gates | Where (origin/main) | What it checks | Decides by | When it fires | Who releases, from where | Added, and why | 30-day fires at the three customers (source) | Sampled right / wrong | (a) Master page section | (b) Fact protected, verdict | (c) Fixtures: true fire / no false fire |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| W1 | Internal-text strips: decision-why marker, reasoning preamble, history sent-time stamp, typed tool-call markup, internal-prompt leak (can replace the whole reply with 'Hey there! I'm Clara. What can I help you with?'), first-email prompt-leak strip at send | conversation-manager.ts:14313, :14338, :14373, :14407, :14459; safety/internal-prompt-strip.ts:37, :70; outbound-safety-pipeline.ts:227 | Removes text the model wrote that is not for the renter. | FACT (a pattern in the text) | Strip; substitute a canned line when nothing is left. Still on main (left for 'PR D', not started). | Trace step on most; the first-email strip at send logs nothing. | #629 May 5 (prompt leak); #5257 Aug 2 (typed tool calls); #6159 Aug 23; #14874 Oct 1 (Western Slope reply opened with the stamp); #21599 Oct 21 (SMS opened with the model's plan) | History-stamp strip: Western Slope 4, JP&Co 2. Prompt-leak strip: 0 at customers. Source: AgentTrace. | Not tallied (pre-strip text not kept). | NOT ADDRESSED by name; 'checks that only block' implies block and retry. | Protects: no internal text reaches a renter. Verdict: KEEP AS A CHECK (block and retry), delete the strip part. | None today. |
| W2 | Draft salvage and empty-final fallback | conversation-manager.ts:14214 (intermediate-draft-drop-guard.ts:261), :14256 (empty-final-fallback.ts:62) | Merges a link or price from the pre-lookup draft back into the reply; replaces an empty final reply with earlier text. | FACT | Append / substitute. Still on main. | Trace step (salvage); log only (fallback). | #6278 Aug 25 (Camellia: the application link sat in the undelivered draft); #18626 Oct 10 | No record found at customers: unmeasured, not zero. | Not sampled. | NOT ADDRESSED. | Protects: a promised link or price is not dropped. Verdict: DEFER TO NEW ENGINE (one writer call per turn removes the second draft). | None today. |
| W3 | Same-turn hand-off rewrites: unreached-forward promise line, first-touch promise-clause strip, template replacement | conversation-manager.ts:16661 (:3064), :16726 (:2693), :16757 (:2600, :1453) | When Clara forwarded to the team this turn, her 'the team will follow up' sentence is swapped, cut, or the whole reply replaced by a template. | PROVENANCE (did the forward reach anyone) + POLICY | Rewrite / strip / substitute. Still on main (named as left out by the main-loop no-rewrite change). | Log only; template replies carry kind escalated_thread_ack. | #5640 Aug 10; #7119 Sep 5; #11980 Sep 24 (hand-off leak); #21395 Oct 20 | Hand-off promise rewrites: Western Slope 2, JP&Co 1. Source: AgentTrace (promise_backing). | 2 read: both right (unbacked 'I've flagged this to the property team right away' became the standard line). | askState(thread): 'before I say it's with the team, is it?' COVERED as a check. | Protects: a promise is backed by a real ask. Verdict: DEFER TO NEW ENGINE (askState blocks instead of rewriting). | Promise without backing: goldmine repro 04 (exists). |
| W4 | Voice and video rewrite mode: redraft shown its draft, video WO-claim cut, video WO-confirmation append, PII redact and redraft, hallucination canned line, anomaly holding line, outbound-policy rewrites (promise, booking, availability), blank-replacement fallback with an em dash and an unbacked promise, spoken releaser | conversation-manager.ts:14568, :15010, :15318, :15385, :15584, :16021, :14193; compliance/outbound-safety.ts:82, :523; policies/promise-backing-policy.ts:316; booking-promise-policy.ts:85; availability-disclosure-policy.ts:51 | On calls and video a failed check edits the words instead of holding (a call cannot wait). | FACT and POLICY | Redraft / strip / substitute / append. Kept on purpose. | Trace steps; booking-promise logs. | #9635 Sep 18; #14348 Oct 1 (zoom.us/j/[REDACTED] reached a prospect); #14353 Sep 30; #15679 Oct 2; #5921 Aug 19 (blank fallback) | Booking-promise regenerate: Western Slope 3, JP&Co 3 (not split by channel). Source: CloudWatch. | Spot-read: mostly right. | #rh-d2 'Voice in v1?' open. NOT ADDRESSED. | Verdict: DEFER TO NEW ENGINE; the blank fallback's own copy breaks two of Fede's rules today. | None today. |
| W5 | Staff-facing rewrites: turnover fabrication, turnover async honesty, turnover edit honesty, staff-desk ref echo and write-claim rewrite | conversation-manager.ts:16219, :16253, :16401, :16412, :16425; turnover-edit-honesty-guard.ts:466 | Replaces a reply to a property manager that claims work no tool did. | PROVENANCE | Substitute a fixed line. Still on main. Staff, not renters. | Record on the row; critical log. | #2254 Jun 11 (Clara told a PM '5 work orders created' that never existed); #2331, #2431 Jun; #20602 Oct 17 | Not counted (staff only). | Not sampled. | NOT ADDRESSED. | Verdict: DEFER TO NEW ENGINE. | None today. |
| W6 | Email template: greeting prepend, HTML greeting, internal staff wrapper, greeting moved to the top, English or Spanish shell, footer, signature and preheader, markdown strip | agents/clara/lib/email/clara-reply-template.ts:56, :142, :223 to :238, :298 to :372, :414; strip-markdown.ts:28 | Wraps Clara's email in a fixed shell and adds or moves the greeting. | FACT + POLICY | Append / reorder / strip. Still on main; the greeting move into the writer is an open issue (#22299). | Nothing logged; the saved row is the writer's text, so the row and the sent email differ. | Mar 20 (threaded HTML email); #393 Apr 24 ('Hi Sydneylavigne727,'); #5057 Jul 30 (markdown reached prospects); #11371 Sep 23 ('Hi Alexis... Hi Alexis'); #17500 Oct 7 ('Hi' on top of 'Hola') | Runs on every Clara email; no log, so no count. | Not sampled. | NOT ADDRESSED. | Verdict: greeting moves to the writer as already planned; footer and signature are the company's fixed words and can stay. | None today. |
| W7 | Email-bounce text fallback | ndr-bounce.ts:337 to :347, :533 | A bounced email is cut to about 500 characters and re-sent by text inside fixed wording, with 'Reply STOP to opt out.' | PROVENANCE + FACT + POLICY | Strip / truncate / append. Still on main. | Message kind bounce_fallback_sms. | #6010 Aug 20 (a Zillow lead's full Gmail inbox) | No record found at customers: unmeasured, not zero. | Not sampled. | NOT ADDRESSED. | Verdict: DEFER TO NEW ENGINE. | None today. |
| W8 | Side writers to renters: maintenance relay dash redraft then template, staff-answer rephrase then verbatim, staff-answer scrub, cadence writer failure template, cadence Subject and sign-off cuts, renewal held-text re-render, reviewer Slack feedback redraft, tour-arrival translation, crash fallback lines | temporal/activities/maintenance-comms.ts:440; escalation/reply-release.ts:2563; escalation/decision-request.ts:1382, :1541; leasing/generate-followup-message.ts:411, :741; renewal-batch/release-render.ts:155; escalation/review-redraft.ts:98; i18n/translate.ts:92; messaging/fail-open-fallback-reply.ts:134 | A second writer, a template or a scrub between Clara's words (or staff's) and the renter. | POLICY / PROVENANCE | Redraft / substitute / strip. Still on main. The maintenance relay dash redraft was ADDED on Oct 24, two days after the no-rewrite rule. | Mostly nothing; a few log lines. | #2106 Jun 10; #5841 Aug 18 (staff sign-offs reached residents); #6193 Aug 24; #9948; #10419 Sep 24; #22236 Oct 24 | Not counted (most have no log). | Not sampled. | NOT ADDRESSED. | Verdict: DEFER TO NEW ENGINE (each becomes a check on its own send door). | None today. |
| W9 | Side writers to the PMS and staff: work-order judge rewrite, dispatch summary, call-note narration swap, note-shape fallback, card-note fallback, message-taking strips, missed-call note trims, two-sentence tour note, knowledge scrub, neighborhood notes, team-notice subject fallback | maintenance/work-orders/judge.ts:452; maintenance/dispatch-summary.ts:268; voice/postcall-call-note.ts:95; pms/guest-card-note-shape.ts:156; card-notes/compose-card-note-live.ts:90; handle-capture-unknown-caller-note.ts:253; promises/missed-call-note.ts:820; prospects/guest-card-core-facts.ts:122; escalation/teaching-capture.ts:927; leasing/neighborhood-notes.ts:223; team-notify/summarize.ts:442 | Rewrites what Clara files in AppFolio or tells staff and vendors. | POLICY | Rewrite / truncate / substitute. Still on main. Not renter-facing. | Some record fields (adjustments, dispatchSummary); most nothing. | #1230 May 21 (WO judge); #6792 (a tenant's apology paragraph paged to a tech); #10892 Sep 22 (narration, fires on 301 of 310 summaries) | Not counted. | Not sampled. | NOT ADDRESSED. | Verdict: DEFER TO NEW ENGINE. | None today. |
| W10 | Transport cosmetics: HTML escaping, splitting long texts, 'Re:' subject, phone links, Graph or SendGrid body choice | clara-reply-template.ts:39, :270; twilio/sms-segments.ts:71; decision-request.ts:1297 | Encodes the same words for the channel. | FACT | Encode / split (words kept). Still on main. | Status callbacks per part. | #19424 Oct 14 (a 701-character Spanish reply refused by the carrier) | Every send. | n/a | n/a | Cosmetic, not a rewrite. Verdict: KEEP. | n/a |
Removed already: bare hand-off recovery, after-trace redraft, held-email redraft and greeting restore (#21992, Oct 23); name-the-home redraft and every first-email redraft and cut (#22249, Oct 24); main-loop text and email redrafts, cuts and canned lines (#22296, Oct 25); cadence template swaps (#22306); call-note failure cuts and stale held recomposes (#22321); the observe-only output gate (Oct 22); the policy-gate repair (Oct 13).
| Gate A | Gate B | What happened | Seen (30 days) | Status |
|---|---|---|---|---|
| Tour time from this turn (H5) | First-email no-tour-times (H4) | The retry was told 'do not name any tour time'; its draft then failed for having no times and was held. | 2 (Anthony Oct 25 20:18Z, Trang Oct 26 14:41Z) | Closed at 16:11Z today (first email's slots count as read); unit-covered and byte-identical on Day 0, no live run yet |
| Writer-call capture (the one retry) | Every first-email check (H4) | The capture skips any round with a tool call and every first email ends with one, so the retry never ran and the first finding became a hold. | 3 of 3 real draft-check holds (retried=false) | Open |
| Send-application tool | Fact trace (H4) | The application really went out at 15:35:30Z; the fact trace did not count its own turn's tool and held 'I've sent the application'. Half a turn: link sent, words held. | 1 (Patrick Oct 26) | Open |
| Unit-count redraft | Bare hand-off body guard (H4) | The redraft that removed the count left a body the next guard read as a bare hand-off. | 1 (Cheryl Oct 23) | Open |
| Implies-prior-contact redraft | Fair-housing hold (H12) | The redraft passed its own check, then the fair-housing judge held it. | 1 (Will, JP&Co Oct 4; later sent) | Open |
| Fact-trace strip | No-tour-times redraft | Two layers rewrote the same first email on the same turn (before Oct 24, when both still edited). | 2 (Western Slope Oct 11, Oct 18) | Both now block only |
| First-email hold, rerun | Supervised review (H1), then 24h expiry (H3) | A held first email was rerun into review, nobody looked, and it expired unsent. | 2 expired (Robert, Morgan); 1 rescued by hand (Trang) | Open |
| Hold record write | Intent consumer | The consumer read the intent while it was still 'preparing' and logged an error. | 2 (Trang, Patrick) | Open |
| Maintenance Supervised hold (H2) | 24h expiry (H3) | Template notices held for review expired with nobody looking. | 106 of 118 | Open |
| Shared retry (H6) | Final send gate (H6) | The retry fixed one finding, the send gate re-checked with no retry left and held on a second one, so the record names the second check. | 1 (Ibrahim, JP&Co Oct 26, unbacked_promise_in_retry) | Open |
Almost silent (a record exists but nobody can release it from the app):
Rewrite layers with no log line or record: the email template's greeting, shell, footer and markdown steps; the first-email prompt-leak strip at send; the maintenance relay dash redraft and template fallback; the staff-answer scrub; the call-note narration swap; the message-taking and missed-call trims; the two-sentence tour note; the cadence Subject and sign-off cuts.
| Inspection or change | Date | What it removed or added | What it missed, as shown by today's cases |
|---|---|---|---|
| Rewrite-layers measurement | Oct 22 | Replayed 78 real first emails. Found 4 dead layers to delete; kept the count check; moved tour times and name-the-home into the writer's turn. Meaning changed from the draft on 29 of 78 with every layer on. | First email only. Did not count the main loop, voice, the email template or side writers; the fact trace was not isolated, and it is the gate that held Patrick today. |
| Prompt architecture audit | Oct 22 to 23 | Census of 138 model call sites; listed 9 rewriters after the writer. Rule: rewrite layers go once replays show they are not needed; checks that only block stay. | Missed the internal-text strips, salvage, empty-final fallback, same-turn hand-off rewrites, the template, the bounce fallback, the work-order judge and the note fallbacks. |
| Stale tour times (#21497) | Oct 20 | The tour-time-this-turn check also covers turns with no calendar read. | Added a redraft (later a retry); widened the provenance gate that held Anthony and Trang. |
| Pipeline slots (#22034 / #22036) | Oct 23 | The tour pipeline's own calendar read counts as this turn's read, so a correct reply is not held. | Did not count the first email's handed-in slots; closed today at 16:11Z (#22485). |
| Dead first-email layers (#21992) | Oct 23 | Deleted 4 dead layers: bare hand-off recovery, after-trace redraft, held-email redraft, greeting restore. | Kept name-the-home and tour-time redrafts, removed next day. |
| First email block only (#22249) | Oct 24 | Every first-email check blocks; one writer retry, then hold. | The retry never ran on first emails (writer-call capture skips tool rounds): 3 of 3 real holds had retried=false. The prompt-leak strip at send stayed silent. |
| Main loop block only (#22296) | Oct 25 | Text and email: every check retries once, then holds; one final send gate. | Left same-turn hand-off rewrites, salvage, empty-final fallback, forced-tool retries, staff turnover guards and internal strips; voice and video keep rewrite mode. Parks still have no release path. |
| Cadence and call notes (#22306, #22321) | Oct 25 | Cadence copy and post-call notes block instead of rewriting. | Kept the cadence failure template and format cuts, the narration swap; wrongly called the voice PII redraft dead (its deletion issue is still open). |
| No dashes (#22236) | Oct 24 | Writer rule plus a blocking check everywhere. | Added a new redraft-then-template layer on maintenance relays. |
| Hold consolidation (#22334, #22450, #22473) | Oct 25 to 26 | One hold function, then shouldSpeak at all 8 callers, byte-identical on 5,515 Day 0 cells; the six backstops were checked and kept; the first email asks shouldSpeak too. | Covers only 'is this module Supervised'. Not the missing hold record on first-email checks, not the retry that never ran, not the 24h expiry that ate 110 holds, not the parks with no release. 'Silent' is in the type and returned by nothing. |
| Real-life ledger and battle test | Oct 26 | 453 hold rows since Aug 31, 318 cases graded against the design; five Oct 26 cases turned into design changes and fixtures. | Graded cases, not gates: no per-gate fire counts, no rewrite layers outside the first email. |
One row per gate or layer with a wrong fire in the sample or a known defect. No new mechanisms: each smallest change uses a record or alert that exists today. Nothing here is built.
| Defect | Renter harm if left (30-day count) | Smallest change in the current engine | Does the new engine make it unnecessary? | Recommendation | Why |
|---|---|---|---|---|---|
| Supervised maintenance notices expire unreviewed (H2, H3) | 106 of 118 residents in 30 days never got 'work order created' or 'rate your repair'; sample 9 of 10 were fine template texts. | Fixed-template maintenance notices skip review (or someone reviews them daily); a setting change at the customer, so it is Fede's call. | No: the new engine keeps Supervised as a thin lookup and adds a review door, not a reviewer. | Delete the gate (for fixed templates) | The words are a fixed template over facts we hold; the hold protects nothing and loses the message. The open emergency widening goes the other way. |
| Tour-time provenance gate empties first emails (H5 into H4) | 2 first emails held in 30 days (Anthony, Trang). | Already merged at 16:11Z today: the first email's slots count as this turn's read. | Yes: facts block plus 'checks compare to our records' (#bt-a row 1). | Fix now (done today; prove it) | Unit-covered and byte-identical only; needs a real first email at the Willows end to end. |
| The one retry never runs on first emails (H4) | Every first-email finding becomes a hold: 3 of 3 real holds this month. | Writer-call capture keeps the round that ends with the declare tool, so the retry exists. | Partly: the design promises 'retry once, then hold' but the page does not prove it on each path (#bt-a row 2). | Fix now | Without it every false fire is renter silence; one-line change in the capture. |
| Fact trace ignores its own turn's actions (H4) | 1 hold (Patrick, half a turn); 2 more fact-trace holds and 5 strips this month. | Count this turn's tool results (application sent, tour booked) as a traced source. | Yes: checks compare against our records and the turn's action ledger. | Fix now | Smallest change that stops holding true statements; delete the gate when the facts block lands. |
| First-email holds leave no hold record (H4) | 5 of 8 first-email holds this month visible only on a Slack card; release is a script. | Write the existing EscalationHold row for every intent hold (same record the review path already uses). | Yes: #bt-changes items 2 and 3 (one hold record, one review door). | Fix now | Uses a record that already exists; no new state. |
| Held first emails expire with no alarm (H3) | 2 prospects (Robert, Morgan) never answered. | The existing 'held first touch' alert also fires when the hold expires. | Partly: review door design (#bt-e) does not name expiry. | Fix now | Fede: 'I don't think we should leave this unresponded to for a long time.' |
| Main-loop parks have no release path (H6) | 3 parked replies at customers this month; the renter is silent until a person writes by hand. | None small; the hold record and review door are the new engine's job. | Yes: #bt-changes item 3. | Wait | Low count, and every sampled park was right or unclear. |
| Forced-tool retries (H7) | About 15 customer turns; the degraded reply still sends. | None needed now. | Yes: 'goes away' in Turn rebuilt. | Wait | No renter silence; it disappears with the facts block. |
| Fair housing: reply loop sends, first email holds (H12) | 1 first email held (Will), likely a false positive. | None; it is a deliberate judge. | No: the judge stays. | Wait | One fire, later sent; a goldmine fixture already covers the false block. |
| Same-turn hand-off rewrites (W3) | 3 rewrites at customers, both read were right. | None now. | Yes: askState blocks the sentence instead. | Wait | Breaks the no-rewrite rule, but the samples were right and the replacement is designed. |
| Internal-prompt strip can replace a whole reply (W1) | 0 fires at customers this month. | Block and retry like every other check (the planned 'PR D'). | Implied, not named. | Wait | No renter harm seen in 30 days. |
| Voice and video blank fallback (W4) | Unknown count (no customer fire found). | Its fixed copy has an em dash and an unbacked promise; change the copy. | No: voice is an open decision (#rh-d2). | Fix now (copy only) | The line itself breaks two standing rules; one constant. |
| Email template adds the greeting (W6) | Every Clara email; no log. | Already planned: the writer writes the greeting, then the template stops adding it. | No: not on the page. | Fix now (already planned) | The saved row and the sent email differ today. |
| Issue to delete the voice PII redraft is wrong (W4) | If done, voice and video lose their PII handling. | Correct or close the issue. | n/a | Fix now (the issue, not code) | The function is still called. |
| Renewal quiet-hours skip returns 'dispatched' (H14) | No renewal outreach at customers this month, so none yet. | Trace it before anything relies on it. | No. | Wait | No current harm; unverified. |
Line numbers in the leasing section (C) are approximate; grep the quoted log string to land on the exact line.
| # | Gate (plain words) | file:line (origin/main 137f883e7f) | What it checks | Decides by | When it fires + record | Who releases, from where | Added (sha, date, PR) + incident | Count by (literal) |
|---|---|---|---|---|---|---|---|---|
| A1 | Supervised rule R1: reply's topic module is Supervised | src/lib/domain/modules/supervised-reply-rule.ts:130 | Topic classifier (renewal / maintenance / other) on the FINISHED reply; module stored drafts at the building | POLICY (module mode) + model topic verdict | Silent hold with record. Text: EscalationHold source: 'clara_reply_sms', resume.kind: 'conversation_reply', reply row withheld. Email: EscalationHold source: 'clara_prospect_email' | Review page, admin hold page, Slack thread reply, reviewer email reply, rollout auto-release, 24h expiry | 321174e9a4 2026-10-05 #16514 (Maintenance/Renewals/Collections email replies went out with nobody approving) | shouldSpeak rule: "R1", holdReason: "topic"; hold reason "Clara's maintenance reply (Maintenance is Supervised at this building)" / "Clara's reply about a renewal (Renewals is Supervised at this building)" |
| A2 | R2: a renewal tool call was deferred this turn | supervised-reply-rule.ts:134 | Renewal action recorded instead of run (noteDeferredRenewalAction) and Renewals Supervised | PROVENANCE (what the turn did) + POLICY | Silent hold with record; hold carries resume.deferredActions, run on approve (deferredActionsRanAt) | Same as A1 | 73b633ec85 2026-10-21 #21646 (Willows: general-topic reply sent and renewal action ran unapproved, #21625) | rule: "R2", holdReason: "topic"; hold reason suffix " - Approving also: …" |
| A3 | R3: the router sent the turn down a Supervised module | supervised-reply-rule.ts:136 | Provisional routed module is Supervised | FACT (router state) + POLICY | Silent hold with record (same rows) | Same as A1 | 779d00abb1 2026-10-05 #16644 (routed maintenance reply called "general" reached the door and was refused, lost and paged) | rule: "R3", holdReason: "routed" |
| A4 | R4: general reply on a renewal thread / open renewal cycle | supervised-reply-rule.ts:137 | Thread is conv-renewal-* or tenant has an open cycle, topic other, Renewals Supervised | FACT + POLICY | Silent hold with record | Same as A1 | 7f60b7dc84 2026-10-13 #19302 (Fairhaven: "looping in your property manager" line read as general, sent unapproved) | rule: "R4", holdReason: "topic" |
| A5 | R5: reply to a prospect while Leasing is Supervised | supervised-reply-rule.ts:138 | Recipient resolved as prospect | FACT (role) + POLICY | Silent hold with record | Same as A1 | 321174e9a4 2026-10-05 #16514 | rule: "R5", holdReason: "prospect"; hold reason "Clara's reply to a prospect - Leasing is Supervised at this building" |
| A6 | R6: topic check gave no verdict | supervised-reply-rule.ts:139-147 | Classifier failed; if thread lane unknown and anything Supervised, hold | POLICY (fail closed) | Silent hold with record, unclearTopic | Same as A1 | a2979107ec 2026-10-05 #16612; refined after Western Slope 2026-10-20 (timed-out check held a prospect tour reply that never went out) | rule: "R6", holdReason: "no-verdict", topic: null; hold reason "Clara's reply - we couldn't tell what it is about, and this building is Supervised, so it waits for approval" |
| A7 | Urgent / emergency work-order carve-out (anti-gate) | src/lib/domain/maintenance/approval-carve-out.ts (skipsSupervisedHold), used at supervised-reply-rule.ts:127, reply-hold-decision.ts:196 | Work order priority emergency/urgent, recipient not prospect | FACT | Releases the hold: sends | n/a | UPDATE 10 (Sentry 7779239068, 2026-10-07) | shouldSpeak verdict: "reply", holdReason: "urgent-carve-out" (door) or rule: "R1"/"R6" with verdict: "reply" |
| A8 | Text row line (asks A1-A6) | agents/clara/lib/messaging/process-envelope.ts:621 (hook at 571-646, hold write at 979-1013) | Above, at the deferred-row slot of the agent loop; requireLive: false (holds at a pre-launch company too); staff never held | POLICY | Silent hold with record: EscalationHold clara_reply_sms (holdDraftReplyText, renewal-reply-review.ts:176). Resident hears nothing (D13 ack excepted) | Same as A1 | 01cb1b523e 2026-09-30 #13906 (gk-drafts-mode 11.1) | shouldSpeak callerId: "text.row-line"; <source> Envelope "<module> drafts mode: held Clara's reply for envelope <id> (hold eschold_…) - nothing sent yet." |
| A9 | Email row line, all three email lanes + team answer | src/lib/domain/modules/supervised-reply-hold.ts:111 (scope 157); call sites process-inbound-email.ts:1892 (ses), process-pms-email.ts:423 (pms), lambda/inbound-processor/handler.ts:740 (outlook), team-answer-supervised-hold.ts:79 | Same rule; only at a claraLive company (else org-not-live owns it) | POLICY | Silent hold with record: EscalationHold clara_prospect_email, resume.kind: 'conversation_reply' (or prospect_email_unthreaded when no conversation, A16) | Same as A1 | 321174e9a4 2026-10-05 #16514 | callerId: "email.row-line", lane: ses/pms/outlook/team-answer; Email Ingest "Held Clara's reply for review (hold <id>, conversation <id>)" |
| A10 | Email "hook never asked" fallback | supervised-reply-hold.ts:199 | If the loop never asked the row-line hook (pre-empt or router answer), hold whenever ANY module is Supervised | POLICY (fail closed) | Silent hold with record (A9 row); no shouldSpeak line for it | Same as A1 | 321174e9a4 2026-10-05 #16514 | No shouldSpeak line; only the Email Ingest "Held Clara's reply for review" line. Gap: not separately countable |
| A11 | Text routed backstop (after the loop) | process-envelope.ts:926 (909-972) | FINAL routed module Supervised, residents only, not urgent | FACT + POLICY | Silent hold with record; loop already wrote the row, so row is marked heldForApproval first (markReplyRowHeld); hold replyRowWritten: true | Same as A1 | 779d00abb1 2026-10-05 #16644 | callerId: "text.routed-backstop", rule: "routed"; "<module> drafts mode: held Clara's reply routed to <module> for envelope …"; warn "Routed hold for envelope <id>: the loop named no reply row, so none is marked held." |
| A12 | SES / PMS / Outlook routed backstops | process-inbound-email.ts:2033; process-pms-email.ts:461; lambda/inbound-processor/handler.ts:1011 | Same as A11 for email | FACT + POLICY | Silent hold with record (clara_prospect_email); PMS lane first refuses (not holds) if a teammate replied (humanRepliedRefusal, process-pms-email.ts:110) | Same as A1 | 779d00abb1 / #16644 family (supervised email net 5b, 2026-10-05) | callerId: "email.ses.routed" / "email.pms.routed" / "email.outlook.routed", rule: "routed" |
| A13 | Maintenance acks answered without a Clara turn | process-envelope.ts:828-866 | dispatchReason state_first_rating / state_first_resident_answer at a Maintenance-Supervised building | FACT + POLICY | Silent hold with record (clara_reply_sms, module maintenance). No shouldSpeak line (reads module state directly) | Same as A1 | a2979107ec 2026-10-05 #16612 ("maintenance acks held not lost") | "maintenance drafts mode: held the state_first_rating acknowledgement for envelope <id> (hold <id>) - nothing sent yet." (also state_first_resident_answer) |
| A14 | Handyman texts Supervised (reply to in-house handyman) | src/lib/domain/maintenance/handyman-reply-hold.ts:41; used process-envelope.ts:592 and :872 (divert) | Company "Handyman texts" mode = supervised and recipient is the crew/tech phone; any priority | POLICY + FACT | Silent hold with record (clara_reply_sms, module maintenance). Vendor-facing, not renter | Same as A1 | b420c00ab3 2026-10-16 #20238 (Gera 2026-10-15: hold every handyman text) | "handyman texts Supervised: held the in-house comms reply for envelope <id> (hold <id>) - nothing sent yet."; warn HandymanReplyHold "building <id> tech phone unreadable; the module's own rule decides" |
| A15 | Hold could not be written (text) | renewal-reply-review.ts:351 escalateUnholdableDraftReply; called from process-envelope.ts:858, 900, 961, 1006 | Hold write threw, or no building/conversation/person to key it | FACT | Nothing sent to renter. Alert to Slack #alerts WITH the words; logCritical without them. Row marked not sent if it had been marked held (markReplyRowNotSent(..., 'send_refused')) | Nobody can release; a person must reply by hand | 01cb1b523e 2026-09-30 #13906 (Driver ruling 2026-09-29: never sent live, never dropped silently) | logCritical('DraftReply', 'A Supervised <Maintenance/Renewals/Leasing/Collections> message could not be held and was NOT sent'); Slack title "Supervised reply could not be held - not sent"; if the post fails: "The #alerts post for a drafts reply that was not sent also FAILED; the reply's words are lost, open the conversation" |
| A16 | Prospect email with no conversation (unthreaded) | src/lib/domain/messaging/send-clara-prospect-email.ts:397-414 | Review mode on, no conversationId | FACT | Silent hold with record: EscalationHold resume.kind: 'prospect_email_unthreaded' keyed (org, recipient, subjectHash) | Same as A1 | 2eed66375d 2026-09-23 #11269 (#11211: unthreadable draft fell through the gate and went out unheld) | hold reason "… - no conversation could be resolved for it"; Email Ingest "Held Clara's reply for review (hold <id>, no conversation)" |
| A17 | Prospect email hold could not be written | send-clara-prospect-email.ts:415-431 | Hold write threw | FACT | Nothing sent; Slack alert (alertUnheldableProspectEmail) with recipient | Nobody; person replies by hand | 2eed66375d 2026-09-23 #11269 | logError('Email Ingest', 'Review mode is on and this reply could NOT be held (…) - dropping it unsent (property <id>, subject <hash>).'); result error "review hold could not be created - not sent" |
| A18 | Newer draft replaces older held draft | src/lib/domain/escalation/held-text.ts:1007 (called renewal-reply-review.ts:225) | Another pending clara_reply_sms hold on the same conversation | FACT | Older hold rejected (nothing sent for it), held row marked not sent | Nobody (by design) | a837f94f8f 2026-09-30 #14241 (gk-drafts-mode 11.3) | resolvedBy: "system:draft-superseded"; HeldText "Held text <id> withdrawn: a newer draft on this conversation replaced it." |
| A19 | Text door: module OFF | src/lib/integrations/twilio/client.ts:1111-1196 | Module declared by the send is stored OFF (or no company named) | POLICY | Nothing sent nothing said to renter. MODULE_REFUSAL# row; page to Sentry for maintenance or replies | Nobody (turn module on) | 3c9968d5db 2026-09-25 #12142 (refusal row); page a839379b5c 2026-10-07 #17560 | Twilio "Outbound SMS to <masked> held - module <m> is not enabled for this company (<m>_module_off)"; logCritical OFF_REFUSED_PAGE "Outbound text refused: <Module> is Off at this building"; module-refusal "channel=sms module=<m> reason=module_off …"; MODULE_REFUSAL reason: module_off / module_unresolved |
| A20 | Text door: Supervised drafts backstop | twilio/client.ts:1198-1249 | Send at a Supervised building carries no draftApprovalId (a lane missed its hold); waived for the exact held-reply ack text (isHeldReplyAckText) and urgent carve-out | POLICY | Nothing sent nothing said. MODULE_REFUSAL# row, Sentry page | Nobody | 2bad0cc8ef 2026-09-30 #14235 | callerId: "text.door", rule: "door", holdReason: "module_drafts"; logCritical Twilio "Outbound text refused: <Module> is in drafts at this building and the send carried no approval - an upstream lane missed its hold"; MODULE_REFUSAL reason: module_drafts |
| A21 | Email seam step 2: module OFF | src/lib/integrations/email/company-live-gate.ts:~400-453 | Declared module not enabled | POLICY | Nothing sent. EMAIL_LOG# BLOCKED row + MODULE_REFUSAL# row | Nobody | 0b596ceb7f 2026-09-19 #9988 (audit: 38 lanes, zero readers of the module flag) | "Held: module <m> is not enabled (<reason>) for org <id> …"; MODULE_REFUSAL channel: email, reason: module_off / module_unresolved |
| A22 | Email seam step 2b: Supervised drafts backstop | company-live-gate.ts:455-495 | draftApprovalId === null at a Supervised building | POLICY | Nothing sent. EMAIL_LOG BLOCKED, Sentry page, MODULE_REFUSAL | Nobody | 9924d6ba50 2026-10-05 #16495 | callerId: "email.door"; EMAIL_LOG errorMessage: "<module>_module_supervised"; logCritical "Outbound email refused: <Module> is Supervised at this building and the send carried no approval - an upstream lane missed its hold"; gate result reason: "module_supervised" |
| A23 | First email Supervised hold | should-speak.ts:152 (caller clara-guest-card-first-email-send.ts:758) | Leasing drafts at a claraLive company (R5) | POLICY | Silent hold with record (see leasing table row A65) | Review page, admin hold page, Slack thread reply | 961cf34d93 2026-10-26 #22473 (moved into shouldSpeak) | callerId: "email.first-touch", rule: "R5" |
| A24 | Proactive renewal texts held (offer, reminder) | src/lib/domain/leasing/renewal-orchestration/renewal-text-review.ts:73; callers src/lib/temporal/activities/renewal.ts:988, 1519, 2297 | Renewals drafts at the building, or the lease's monthly batch not approved | POLICY | Silent hold with record: EscalationHold renewal_outreach_sms / renewal_reminder_sms, workflow_signal resume; may be parkedForBatch | Same as A1; batch list approval | 1b66316ad2 2026-09-28 #12977 | hold reason "The renewal offer text (Renewals is Supervised at this building)" / "(waiting for next month's renewal list)" / "(on this month's renewal list)"; refused kinds "approved hold not found", "hold is <state>, not approved", "approved hold is for a different send" |
| A25 | Renewal no-phone email held | renewal-email-review.ts:57 | Same, email | POLICY | Silent hold with record renewal_outreach_email | Same | 3272c60b88 2026-10-02 #15085 | reason "The renewal offer email, with the renewal letter (…)"; warn RenewalEmailReview |
| A26 | Renewal change-term text, offer/portal text, after-call recap | renewal-reply-review.ts:58, 95 | Renewals Supervised | POLICY | Silent hold with record renewal_change_term_sms / renewal_offer_text_sms | Same | 8a3c61885a 2026-10-02 #15077; ffd8ede127 2026-10-05 #16501 | reason "Confirms the renewal term the resident changed to (Renewals is Supervised at this building)" |
| A27 | Lease-ending / month-to-month warning text | src/lib/domain/leasing/renewal-auto-start.ts:2690 | Renewals Supervised | POLICY | Silent hold with record renewal_expiry_warning_sms | Same; next renewal scan sends after quiet hours | 533f4dbd8f 2026-10-02 #15083 | source: renewal_expiry_warning_sms |
| A28 | Automatic maintenance texts/emails held (rating ask, WO notices, vendor relay, forward/stand-down) | src/lib/domain/maintenance/maintenance-text-review.ts:63, 161; rating.ts:854 | Maintenance Supervised and not emergency/urgent | POLICY + FACT | Silent hold with record maintenance_resident_sms / maintenance_vendor_sms / maintenance_resident_email / maintenance_vendor_email, maintenance_rerun resume (approve re-runs every check). Hold write failure: refused HOLD_UNWRITABLE | Same as A1 | 79a0d35abf 2026-10-05 #16508; 3719bbe139 2026-10-06 #16855; fa0a2a56ac 2026-10-06 #16919 | source values above; refused reasons "approved hold is for a different address", "no company to hold the handyman text against" |
| A29 | Handyman automatic texts | src/lib/domain/maintenance/handyman-text-review.ts:24 | Handyman texts mode supervised | POLICY | Silent hold with record (maintenance_vendor_sms) | Same | 45252d9726 2026-10-16 #20060 | source: maintenance_vendor_sms |
| A30 | Zillow relay lead never answered by email | agents/clara/lib/email/decide-email-send-mode.ts:150 (used process-inbound-email.ts:1997) | Sender is Zillow-family relay and the listing-site email setting is off | FACT (sender) + POLICY | Nothing sent nothing said (inbound recorded) | Nobody | 17a47ee2e7 2026-09-16 #8896 (Fede 2026-09-15) | send result error: "zillow_relay_never_answered_by_email" |
| A31 | Relay lead with no renter address | decide-email-send-mode.ts:170 | Relay notification has no renter email to compose to | FACT | Nothing sent nothing said | Nobody | dcbe07af7f 2026-08-09 #5578 | error: "relay lead with no renter address - refusing to email the relay mailbox" |
| A32 | Always-on reply override (anti-gate) | process-envelope.ts:974-977; supervised-reply-hold.ts:224 replyNeverHeld | A hold landed on emergency / team_alert / staff_reply | FACT | Sends anyway | n/a | 01cb1b523e #13906; email 7912f1bde9 2026-10-11 #18736 | logCritical <source> Envelope "Drafts hook withheld an always-on (<kind>) reply for envelope <id> - sending it anyway; its conversation row was not written." |
SuppressingGuard union at CM:5941)| # | Gate (plain words) | file:line | What it checks | Decides by | When it fires | Who can release, from where | Added | Literal tag to count fires |
|---|---|---|---|---|---|---|---|---|
| 1 | Runaway ceiling | guards/runaway-ceiling.ts:170 (MAX_CONSECUTIVE_AUTO_REPLIES = 40, line 59); CM:7457, set 7553 | 40 auto-replies in a row on one thread | FACT (count) | The inbound is saved. Status becomes escalated with escalatedBy:'clara'. An activity log entry is written (id prefix log_runaway, type escalated). Silent hold with a record. | Staff set status active with PATCH /api/conversations/[id] | 583fccb86b 2026-06-13 #2341. Origin: runaway conversation loops with machine senders. | logCritical('Agent:runaway-ceiling', 'Runaway auto-reply ceiling reached - stopping outbound on this thread', {guard:'runaway_ceiling', consecutiveAutoReplies, ceiling}). suppressedBy:'runaway_ceiling' |
| 2 | Muted person | CM:7618 (checkSuppression muted===true), set 7628 | The person is muted. PM senders are exempt. | POLICY (staff mute) | The inbound is saved. One ops card per mute episode (type muted_inbound, qualifier NO REPLY SENT). Nothing sent. | Staff unmute the person | b6ad311807 2026-08-28 #6526. Origin: person-level mute v1. | logInfo('Agent:person-mute', "Muted person on conversation ${id} - inbound persisted, no reply to them, no PM escalation email; the team gets one ops-feed card per mute episode."). suppressedBy:'person_muted' |
| 3 | Tapback / reaction | guards/tapback-reaction.ts:222 (recognizeReaction), 347 (findReactionTarget); CM:7727, set 7746 | The inbound is a phone reaction and the message it reacts to is found | FACT (text shape) | The reaction is attached to (or removed from) the quoted message. No reply row. Nothing sent, nothing said. | None needed. If the target is not found, the text is treated as an ordinary message. | d088b46994 2026-08-29 #6552. Origin: Clara replied to tapbacks. | logInfo('Agent:tapback', "Reaction ${emoji} attached to message ${msgId} on conversation ${id} - no reply turn.") (also a "REMOVED from" form). suppressedBy:'tapback_reaction'. Not-found case: "Reaction-shaped inbound ... handling as an ordinary message." |
| 4 | Farewell loop | guards/farewell-loop.ts:138 (FAREWELL_PRIOR_CLOSINGS_REQUIRED = 2); CM:7793, set ~7824 | A closing message after 2 or more closing replies | FACT (text shape + history) | The inbound is saved. Nothing sent, no escalation. | None. The next real inbound is answered. | b1d0b958ee 2026-07-30 #5047. Origin: Clara traded goodbyes with another machine. | logInfo('Agent:farewell-loop', "Farewell exchange complete on conversation ${id} - inbound closing after ${n} closing replies; suppressing (no escalation)."). suppressedBy:'farewell_loop' |
| 5 | Bare closer skip | guards/closer-skip.ts:426; CM:7809, set ~7824 | A bare "thanks" / "ok" / "bye" / glyph | FACT (lexicon) | The inbound is saved. Nothing sent. | None | e153c55541 2026-08-30 #6519. Origin: replying to bare thank-yous. | logInfo('Agent:closer-skip', "Bare closer on conversation ${id} (rule=${rule}) - ${reason}; suppressing (no escalation)."). suppressedBy:'closer_skip' |
| 6 | Escalated-thread gate (human takeover) | CM:1665 (decideEscalatedGate); CM:7959 (gateCouldHold), verdict 8078, set 8248 / 8533 | The thread is human-owned (escalatedBy is not clara, or manuallyEscalatedAt is set) and the inbound is not a separate topic. A Haiku topic classifier can release a separate-topic inbound. | PROVENANCE (who owns the thread) + POLICY | The inbound is saved. A matter is opened (openMatterRecordOnly). The team is told by email plus an ops card (notifyTeamOfGatedInbound, writerId escalated-gate.held-inbound-notice), except for a team-monitored inbox. A tour confirmation can still go out (runEscalatedTourBookingOnlyTurn). Hand-off to a person. | Staff PATCH status active; a matter resolving (unmuteConversationIfResolved); a staff reply | 9f687a9529 2026-08-04 #5388 (team notice 9b6f71603b 2026-08-19 #5874). Origin: Clara went silent on human-owned threads. | logInfo('Agent:escalated-gate', "Conversation ${id} is escalated (human-owned) - reason=${reason}; no agent loop, no tools, no reply to the tenant. Capturing inbound and notifying the team.") and the variant "...held-inbound notice suppressed (team-monitored inbox, Decision 9)." Reasons: not_taken_over, pm_sender, not_escalated, nothing_open, separate_topic, ambiguous_low_risk, held. suppressedBy:'escalated_thread' |
| 7 | Takeover hold on a team-answer relay | CM:8550, set 8561 | A human holds the thread when Clara would relay the team's answer | PROVENANCE | Nothing sent. Log only. | Staff release the takeover | 076dae30bd 2026-10-03 #15597. Origin: the team-answer relay seam. | logInfo('Agent:team-answer-turn', "Team-answer relay held on conversation ${id}: a human (${escalatedBy}) holds this thread - the relay does not talk over a takeover."). suppressedBy:'escalated_thread' |
| # | Gate | file:line | What it checks | Decides by | When it fires | Release | Added | Tag to count |
|---|---|---|---|---|---|---|---|---|
| 8 | First-turn leasing tool gate | CM ~12547 (isFirstTurnLeasingViolation) | Leasing turn 1 ended with no tool calls | PROVENANCE (no lookup) | Redraft with a reminder. If the retry still has no tool calls, the degraded reply is sent. | None | ce3a18093d 2026-04-08 (no PR #). Origin: Mike Tappan turn 1 with no lookup. | [FirstTurnViolation] Leasing turn 1 ended without tool calls ... - retrying with reminder. Then [FirstTurnViolation] Retry succeeded or [FirstTurnViolation] Retry STILL produced no tool calls - proceeding with degraded reply |
| 9 | Availability-claim gate | availability-claim-gate.ts:294; CM:12717-12860 | The reply names unit availability with no get_available_units call this turn (reply turns too) | PROVENANCE | Redraft with forced tool_choice. If the retry fails, the degraded reply is sent. | None | ae6f8bd76e 2026-09-21 #10579 (widened #10564). Origin: Camellia conv 749affaa. | [AvailabilityClaimGate] Turn N named unit availability with no lookup (units=...) ... - retrying with forced tool_choice, then forcedToolRan=, then Retry STILL called no grounding tool - proceeding with degraded reply or Retry call failed (...) - keeping the original response; proceeding with degraded reply. Trace name claude-${channel}-availabilityclaim-retry |
| 10 | Tour-time claim gate (first response) | tour-time-claim-gate.ts:328; same CM block | The reply declines or offers tour times with no calendar read (get_tour_slots / check_availability) | PROVENANCE | Redraft with forced tool_choice. The unit gate wins when both fire. If the retry fails, the degraded reply is sent. | None | 75056437c3 2026-09-30 #14132 (first symbol commit). Origin: Western Slope conv 7ddd98a6. | TourTimeClaimGate. stepName tourtimeclaim. Detail ${kind} with no calendar read (kind declined_time or offered_times) |
| 11 | Typed tool call in text | CM ~12870 | The model typed tool-call markup as text | FACT (text shape) | Redraft with forced tool_choice. If that fails, the markup is stripped (see #19). | None | 245dfabf15 2026-10-01 #14348 | [TypedToolCall] ... retrying with forced tool_choice |
| 12 | Duplicate work order (tool block) | CM, trace safety:duplicate-wo | A matching work order is already open | FACT | Blocks the tool, not the message. The model replies without a new work order. | None | de5c191cb1 2026-04-04 (no PR #) | checkName duplicate_wo, blockedBy safety:duplicate-wo |
| 13 | Emergency guard (tool block) | CM, trace safety:emergency-guard | A safety emergency was detected | FACT / POLICY | Blocks the tool | None | de5c191cb1 2026-04-04 | checkName emergency_guard. Blocking ${tool} - safety emergency detected |
| 14 | Staff write gate | capabilities/staff-write-gate.ts:183 (stripStaffTurnWriteTools) | Staff-desk turn | POLICY | Write tools are removed. The reply can only claim what landed (see #33). | None (code) | b633d3a419 2026-08-19 #5900 | No log. The tool is absent. |
| 15 | Video read-only gate | capabilities/video-read-only-gate.ts:129 (strip), 153 (refuseOnVideoReadOnlyTurn), 141 (isVideoReadOnlyRefusal) | Video turn calls a write tool | POLICY | The tool is refused with error not_available_on_video (VIDEO_READ_ONLY_REFUSAL_ERROR, line 134) | None | 73c6f92750 2026-10-02 #15342 (#15255) | Tool result error not_available_on_video |
| 16 | end_conversation (model chooses silence) | tools/end-conversation.ts; set CM:13517/13521 | The model asks to end. It is refused while a duty is undischarged. | POLICY (model) | Approved: nothing sent, conversation resolved. Refused: the loop continues. The self-serve check (gateAllowsSelfServe, end-conversation.ts:432, CM:17298) only sets a dashboard flag, so it is NOT a message gate. | None | c434ac9c29 2026-06-24 #2625 (ADR-0070) | logInfo('EndConversation', "REFUSED silence on ${id} - duty undischarged: ...") and "Silence approved on ${id}... Marking resolved.". suppressedBy:'conversation_ended' |
| 17 | Video turn abandoned | CM ~14029, 17279 | Nobody will hear the video turn | FACT | Nothing sent, nothing recorded | None | 2a80fc95a6 2026-10-03 #15884 (#15487) | logInfo('VideoClaraTurn', 'abandoned turn stopped; nothing recorded'). suppressedBy:'video_abandoned' |
| # | Gate | file:line | What it checks | Decides by | When it fires | Release | Added | Tag to count |
|---|---|---|---|---|---|---|---|---|
| 18 | Empty reply | CM:14287-14289 | The model returned no text | FACT | Nothing sent. Alert through alertNoReplySent. Video and scheduled-wake <silent> replies send nothing and raise NO alert. | None | 245dfabf15 2026-10-01 #14348 | noReplyAlert('empty_reply', 'the model returned no text') |
| 19 | Strip steps that can empty the reply (strip) | CM:14362 (reasoning preamble), 14393 (history stamp), 14443 (typed tool call), internal-prompt strip, DecisionWhyLeak | Internal text in the reply | FACT | Strip. When nothing is left: nothing sent plus an alert. The internal-prompt strip falls back to a fixed line. | None | 245dfabf15 2026-10-01 #14348 | noReplyAlert('empty_reply','the reply was only the model\'s own reasoning'), ...'only an internal sent-time stamp', noReplyAlert('typed_tool_call_only', ...). Trace safety:reasoning-preamble-strip, safety:history-stamp-strip, safety:typed-tool-call-strip, safety:internal-prompt-strip. logCritical context ReasoningPreamble, TypedToolCall |
| 20 | Intermediate-draft salvage | intermediate-draft-drop-guard.ts; CM:14214 | The final reply dropped facts an earlier draft in the turn had | FACT | Additive only (blocked:false). It never holds. draftLeftBehind feeds #36. | n/a | a63c716246 2026-10-10 #18626 (fallback log) | [IntermediateDraftDrop] final reply dropped N mechanical fact(s).... Trace safety:intermediate-draft-drop. empty_final_fallback |
| 21 | Weekday / date mismatch | CM:14744, hold 14783-14804 | The weekday named does not match the date | FACT | Redraft (weekday-redraft). If still wrong: hold with record, guard weekday_date_guard, audit head WEEKDAY-DATE-HELD wrong weekday, Slack page (checkLabel date check). | No in-app release | 9c5b35feee 2026-09-26 #12543 | logCritical('Agent','Weekday/date guard held a reply for human review - nothing was sent'). Trace safety:weekday-date-guard / weekday_date_guard |
| 22 | Tour time not from this turn's calendar read | tour-time-this-turn-gate.ts:249 (detectOffTurnTourTimes), 316 (correction); CM:14820, hold 14866-14884 | Offered times that this turn's calendar read did not return | PROVENANCE | Redraft (tour-time-this-turn-redraft). If still wrong: hold with record, guard anomaly_review_gate, subReason tour_time:not_in_this_turn_read. NO Slack hold page; reportFallback goes to #alerts. | No in-app release | ce62f94a74 2026-10-16 #20338 (hardened #22020, #22034). Origin: Willows 2026-10-16 and 10-20. | Trace safety:tour-time-this-turn / tour_time_this_turn. Detail redrafted: offered X, ... or held: offered X, ..., and the redraft did not fix it. reportFallback surface clara-tour-time-this-turn. Content prefix [HELD-FOR-REVIEW anomaly:tour_time:not_in_this_turn_read] |
| 23 | Unbooked tour confirmation | CM:14899, hold 14931-14949 (unbackedTourConfirmation) | The reply confirms a tour no booking tool booked | PROVENANCE | Redraft (unbooked-tour-rerun). If still wrong: hold with record (anomaly_review_gate, subReason tour_booking:unbacked_confirmation) plus alertUnbackedTourConfirmation. | No in-app release | ad65443706 2026-09-30 #14353. Origin: JP&Co Camellia 2026-09-30. | Trace safety:tour-booking-outcome / tour_booking_outcome. Detail held: "<phrase>" confirmed a tour nothing booked, and the re-run did too. logWarn Unbooked-tour re-run failed - holding the reply |
| 24 | Work-order claim (renewal threads) | CM:14960, hold 14987 | The reply claims a work order was filed with no tool filing it | PROVENANCE | Redraft (work-order-claim-redraft). Then hold (subReason work_order_claim:unbacked, checkLabel work order check). Voice gets the claim stripped. | No in-app release | 2a9c46ed1b 2026-10-01 #14864 | 'Work-order claim check held a reply for human review - nothing was sent'. Trace safety:work-order-claim |
| 25 | Unit count stated | CM:15029, hold 15054 | The reply states how many units are open | FACT / POLICY | Redraft (unit-count-redraft). Then hold (subReason availability:how_much_is_open). | No in-app release | 4545dd190f 2026-10-19 #21145. Origin: WS bc17f805. | [guard:${UNIT_COUNT_GUARD_ID}] held a reply for human review - nothing was sent . Trace safety:unit-count-stated |
| 26 | Outbound policy: promise backing | outbound-safety.ts:307; CM:15088, hold 15186 | A promise with no backing action | PROVENANCE | Redraft (outbound-policy-promise-backing, correction "The send check stopped your reply: ${reviewReason}."). Then hold (subReason outbound_policy:promise-backing). blockOnly: textOrEmailTurn turns a rewrite verdict into a hold. | No in-app release | bc642b99fc 2026-10-25 #22296 (hold semantics) | 'Outbound-safety policy held a reply for human review - nothing was sent'. Trace safety:promise-backing / promise_backing |
| 27 | Outbound policy: booking promise | same | A booking promised but not done | PROVENANCE | Same as #26 (outbound_policy:booking-promise) | same | same | Trace safety:booking-promise / booking_promise |
| 28 | Outbound policy: availability disclosure | same | Over-sharing availability | POLICY | Same as #26 (outbound_policy:availability-disclosure) | same | same | Trace safety:availability-disclosure / availability_disclosure |
| 29 | Fair-housing check | same; ALERT_ONLY_POLICY_IDS = {'fair-housing'} (outbound-safety.ts:344) | Fair-housing risk | POLICY | Alert only: recorded, SENT, flag posted to Slack (postFairHousingFlagBounded). It never holds on main. | n/a | c2558720f9 2026-10-19 #21159 (dropped the hold) | logWarn('Fair-housing check flagged a reply - recorded, the reply was sent, and the flag was posted to Slack', {guard:'fair_housing_flag', mode:'alert'}). Trace safety:fair-housing / fair_housing. Audit head FAIR-HOUSING-HELD for review survives only on old rows and the first-email path (#40). |
| 30 | Work-order confirmation missing | CM:15254, hold 15298 | A repair the intake filed is not confirmed in the reply | PROVENANCE | Redraft (work-order-confirmation). Then hold (subReason work_order_confirmation:missing). | No in-app release | b6c304783c 2026-10-04 #16358 | 'Work-order confirmation check held a reply for human review - nothing was sent'. Trace safety:work-order-confirmation |
| 31 | Sensitive number (PII) | CM:15336, hold 15370 | A sensitive number in the reply | FACT | Redraft (pii). Then hold (subReason pii:sensitive_number). Voice and video: redacted-redraft; if still withheld, nothing is said plus an alert. | No in-app release | bc642b99fc 2026-10-25 #22296 (hold form) | 'PII check held a reply for human review - nothing was sent'. Trace safety:pii-strip (type pii_strip). noReplyAlert('redacted_after_redraft', ...) |
| 32 | No dashes | CM:15409, hold 15429 | An em or en dash in the reply | FACT | Redraft (no-dashes). Then hold (guard dash_in_reply, audit head DASH-HELD dash in reply). | No in-app release | 995c9f204d 2026-10-24 #22236 | 'No-dashes check held a reply for human review - nothing was sent'. Trace safety:no-dashes / dash_in_reply |
| 33 | Hallucination guard | guards/hallucination-guard.ts:861; CM:15461, held 15568. Runs only with an offer or tour snapshot. | Renewal offer figures or tour-time follow-through not backed by data or tools | FACT / PROVENANCE | Redraft (hallucination-guard). Then block with record (prefix msg_blocked, head GUARD-BLOCKED hallucination) and an automatic hand-off: renewal_escalate, else notify_leasing_team, else escalate_to_human. Text and email: empty and silenced. Voice: canned line or GUARD_NO_HANDOFF_REPLY. | The person the hand-off reaches; no in-app resend | a786a23caf 2026-04-27 #485. Origin: legal risk on live renewal outreach. | logCritical('Agent','Hallucination guard blocked an outbound message'). Not reached: 'Hallucination guard auto-fire did not reach a human - blocked thread may have no owner' with reason tool_reported_failure / no_recipient_configured / no_recipient_reached. Trace safety:hallucination-guard / hallucination_guard |
| 34 | Send gate (final re-check) | CM:15933, hold 15947 | Re-runs every deterministic check on the final text, plus unbacked_promise_in_retry | FACT / PROVENANCE | No retry. Hold (subReason send_gate:<finding>). Findings: weekday_date, tour_time_this_turn, unbooked_tour, work_order_claim, unit_count, outbound_policy:<id>, work_order_confirmation, pii, dash, hallucination_guard, unbacked_promise_in_retry. | No in-app release | bc642b99fc 2026-10-25 #22296 | 'The send gate held a reply - nothing was sent'. Trace safety:send-gate / send_gate. Content [HELD-FOR-REVIEW anomaly:send_gate:...] |
| 35 | Anomaly Review Gate | src/lib/agent/turn-integrity/anomaly-review-gate.ts:296; CM:15967, set 16027/16085 | A fatal tool failure or a turn timeout | FACT | Hold with record (anomaly_review_gate plus root-cause signature). Text and email: no holding line. guardAction edited / withheld. On timeout: alert. If the gate itself fails: silence. Repeats are deduped. | No in-app release | e1017d39d6 2026-07-17 #4061 (ADR-0101) | logCritical(..., 'Anomaly review gate held an outbound message', {cause, deduped, outageMode}). Trace safety:anomaly-review-gate. noReplyAlert('turn_timeout', ...) and ...; the review gate itself failed |
| 36 | Turnover fabrication guard | CM:16114 (detectsTurnoverWorkClaim) | A turnover reply claims dispatch work no tool did | PROVENANCE | Block with record (TURNOVER-GUARD-BLOCKED fabrication), replaced by a canned honest line | n/a | 373867f6fd 2026-06-11 #2254 (fixed a4b2baa110 #4883) | 'Turnover fabrication guard blocked an outbound message'. Trace safety:turnover-fabrication-guard |
| 37 | Turnover async-honesty guard | CM:16245 | The reply says async work orders are complete | PROVENANCE | Record (TURNOVER-ASYNC-HONESTY-GUARD-REWROTE completion framing) and a rewrite. Counts as a block plus replacement. | n/a | 2cad9b301f 2026-06-12 #2331 | 'Turnover async-honesty guard rewrote an outbound message' |
| 38 | Turnover edit-honesty guard | turnover-edit-honesty-guard.ts:466; CM:16353 | A claimed edit with no edit tool | PROVENANCE | Block with record (TURNOVER-EDIT-GUARD-BLOCKED hallucinated edit) plus a canned line | n/a | d633e43e2e 2026-06-16 #2431 | 'Turnover edit-honesty guard blocked an outbound message'. Trace safety:turnover-edit-honesty-guard |
| 39 | Same-turn escalation outcome | CM:2836 (decideSameTurnEscalationOutbound); CM:16549 | An escalation fired this turn. What the reply becomes. | POLICY | Team-monitored inbox: silenced with record (TEAM-INBOX-SILENCED bare handoff, silenceTeamInboxDraft). First touch: strip promise clauses. Others: template replacement or release. | n/a | e0c4e6ff40 2026-08-10 #5640; silence daf0e67de5 2026-08-20 #5985 | logInfo('Agent:escalated-gate', ... "reason=same_turn_escalation_team_inbox_silence"). Trace safety:team-inbox-silence (guardAction withheld). Other reasons: same_turn_escalation_substantive_release, same_turn_escalation_undelivered_promise, same_turn_escalation_first_touch_strip, same_turn_escalation, same_turn_escalation_released. suppressedBy:'team_inbox_silence' (17873) |
| 40 | Supervised reply hold hook | CM:16874 (replyRowHoldIf, heldBySupervisedHook) | A Supervised module or the reply-topic classifier says hold | POLICY | Supervised hold (see top). Decided before #41. If the hook throws, the reply is released. | Approve on /review or /admin/escalation-holds/[id] | 82ab514237 2026-09-29 #13629 (#13009) | logWarn Review-mode hold decision failed ... - releasing |
| 41 | Autonomous team-note backstop | CM:17003 | n/a (side effect only) | - | Not a gate. Skipped when the reply was held. | - | ac928e64e4 2026-09-05 #7119 (approx.) | AutonomousTeamNoteBackstop |
| 42 | Opted-out person | opted-out-reply.ts:27; CM:17106 | The person opted out of everything | FACT (consent) | The turn runs and the reply is withheld. Trace step only, NO row. | The person texts START | a300105ff5 2026-10-20 #21302 (#21279) | logInfo('Agent:person-opted-out', "Opted-out person (scope=all) on conversation ${id} - inbound filed, turn ran, reply withheld, nothing sent."). Trace safety:person-opted-out / person_opted_out. suppressedBy:'person_opted_out' |
| # | Gate | file:line | What it checks | Decides by | When it fires | Release | Added | Tag to count |
|---|---|---|---|---|---|---|---|---|
| 43 | Supervised maintenance acks | PE:828 | First rating or first resident-answer ack | POLICY | Supervised hold (state_first_rating, state_first_resident_answer) | /review approve | 01cb1b523e 2026-09-30 #13906 | PE log ${module} drafts mode: held Clara's reply for envelope ... (hold ${id}) - nothing sent yet. |
| 44 | Handyman texts divert | PE:872; src/lib/domain/maintenance/handyman-reply-hold.ts:41 | A reply to the in-house handyman with the "Handyman texts" setting | POLICY | Supervised hold | /review approve | b420c00ab3 2026-10-16 #20238 | holdsHandymanReply (same drafts log) |
| 45 | Routed-module backstop (shouldSpeak) | PE:921; src/lib/domain/modules/should-speak.ts:100 | The module verdict is hold | POLICY | The row is marked held (markReplyRowHeld, hold id prefix eschold) plus a Supervised hold | /review approve | 6eafef0c9e 2026-10-08 #17564 | eschold hold ids |
| 46 | Drafts module hook | PE:979; renewal-reply-review.ts:176 | The module is in Supervised (drafts) mode | POLICY | Supervised hold. If the reply cannot be held: NOT sent plus a critical page. Module off: not sent. Always-on module: SENT anyway. | /review approve; rollout release | 01cb1b523e 2026-09-30 #13906 | "${module} drafts mode: held Clara's reply ...". logCritical('DraftReply','A Supervised Maintenance message could not be held and was NOT sent'). "Clara's reply not sent: Maintenance is Off at the resident's building". "Drafts hook withheld an always-on (...) reply ... - sending it anyway". "Rollout released hold ..." |
| 47 | applyOutboundPolicy | DE:247 | Stress or test sender; thread escalated; a human replied; SMS disabled (non-replies only) | PROVENANCE / POLICY | Skipped. Log only, no row. Throws ThreadOwnershipUnreadableError when status cannot be read. | Staff release the thread | 6ae64094ee 2026-05-17 #1100 | reasons policy:stress_sender, policy:test_sender, policy:thread_escalated (logWarn 'applyOutboundPolicy' "Skipped outbound on escalated conversation ... - thread is human-owned."), policy:human_replied, policy:sms_disabled |
| 48 | SMS consent (dispatcher) | DE ~570 (non-replies) | STOP on file | FACT | Dropped. Log only. | START | 6ae64094ee 2026-05-17 #1100 | logWarn('SMS Consent', "Outbound to ... blocked - recipient opted out (STOP). Reason: ... Message dropped."). Trace envelope:consent_check |
| 49 | Text door compliance gate | TW:904 (runOutboundTextComplianceGate) | Canary string; STOP / consent; AppFolio OTP number; stress sender; test-scope recipient; flipped line; module off or unresolved; Supervised backstop shouldSpeak hold | FACT / POLICY | Blocked. Module refusals write a record (recordModuleRefusal, reasons module_off / module_unresolved / module_drafts). Some page Slack. An unresolved module read SENDS anyway. | Fix the setting or consent | aad072c182 2026-09-03 #6831 (OFF page a839379b5c #17560; backstop page 7ac239ef5e #14900) | logError('CANARY', 'System prompt canary detected in outbound ... - BLOCKING SEND'). logWarn('Twilio', "Outbound SMS to ... blocked - ${reason}") (refusal opted_out). '... AppFolio OTP gateway ... - BLOCKING SEND (reply-loop regression)' plus alert kind appfolio-otp-reply-loop. '... refused - ${reason}: a test building or sandbox company may text only a number listed in config/phone-registry.json'. '... held - module ${module} is not enabled for this company'. OFF_REFUSED_PAGE[module], DRAFTS_BACKSTOP_PAGE[module]. '... SENT despite an unresolved module read' |
| 50 | AppFolio inbox text door | src/.../adapters/appfolio-inbox-text.ts | The PMS text door is on; media is attached | FACT | Held on the outbox (carrier:pms_text_held, refused:attachments_unsupported). Re-driven by drainPmsTextDoors. | Automatic re-drive | 3615858879 2026-10-14 #19463 | logCritical PMS text door got a text with media: held whole, nothing sent. logWarn text held at the AppFolio door: ${reason} |
| 51 | Guest-card first email pipeline | outbound-safety-pipeline.ts | First email body: fair housing, policy, hallucination | FACT / POLICY | Returns {ok:false, guard, reason}. Not sent. | - | (not looked up) | guards first_email_body_guard, fair_housing_hold, outbound_policy_hold, hallucination_guard |
| # | Gate (plain words) | file:line (origin/main) | What it checks | Decides by | When it fires + record | Who can release, from where | Added | Literal log tag / record field / reason string |
|---|---|---|---|---|---|---|---|---|
| 1 | Company not live (intake) | L/guest-card-reply-intake.ts (mode skip) | firstEmailMode = off because claraLive false | POLICY | Every card at a dark company. LOG only | Nobody. Turning the company live does not re-run skipped cards | bcaec8cd66 2026-09-18 #9633 | guest_card_company_not_live (logInfo 'guest-card-reply-intake') |
| 2 | Leasing module off (intake) | L/guest-card-reply-intake.ts | module = off | POLICY | LOG only | Nobody (no re-drive) | 1c842aedf1 2026-10-19 #20994 | guest_card_leasing_off |
| 3 | Card made by Clara | L/guest-card-reply-intake.ts | card creator is Clara's own write | PROVENANCE | LOG only | None (by design) | 75d4871430 2026-09-25 #12201 | guest_card_created_by_clara |
| 4 | Home added by Clara | L/guest-card-reply-intake.ts | interested-home row was Clara's write | PROVENANCE | LOG only | None | fa14f54c54 2026-10-03 #15832 | guest_card_home_added_by_clara |
| 5 | Imported as backfill | L/guest-card-reply-intake.ts | card came from a history import | PROVENANCE | LOG only | None | #9633 | guest_card_imported_as_backfill |
| 6 | Seen before lead source was turned on | L/guest-card-reply-intake.ts | first-seen < source enabled-at | PROVENANCE | LOG only | None | #9633 | guest_card_first_seen_before_lead_source_enabled |
| 7 | Arrived before go-live | L/guest-card-reply-intake.ts | card time < company go-live | PROVENANCE | LOG only | None | d954ad91e4 2026-10-12 #19004 | guest_card_arrived_before_go_live |
| 8 | Prospect already applied | L/guest-card-reply-intake.ts | person has an application ("He already applied. Leave it alone.") | FACT | LOG only | None | ef3ea6f1bc 2026-10-05 #16461 | guest_card_prospect_already_applied |
| 9 | Lead source not in scope | L/guest-card-reply-intake.ts via leadScopeSkip (L/lead-scope.ts) | settings.leadScope {mode:'listed',sources} excludes the source (Situs list) | POLICY | LOG only | Edit company leadScope; no re-drive of skipped cards | 8edfcc5cb7 2026-10-15 #20019 | guest_card_source_not_in_scope |
| 10 | Already engaged (message-less card) | L/guest-card-reply-intake.ts | card has no inquiry text and the person is already in a conversation | FACT | LOG only | None | #16461 | guest_card_prospect_already_engaged |
| 11 | Consumer: no email on the lead | src/lib/domain/leads/consumers/clara-first-email-consumer.ts | lead has no email | FACT | LOG only | None (a later email update re-emits lead.received) | 7aed0e1092 2026-09-20 #10317 | clara_first_email_consumer_verdict {outcome:'skipped', reason:'no_email'} |
| 12 | Consumer: unsupported record / bad ref | same | record kind or ref shape not recognized | FACT | LOG only | None | #10317 | unsupported_record_kind, unrecognized_record_ref_shape |
| 13 | Consumer: person is staff | same | resolved person is a staff member | PROVENANCE | LOG only | None | e7574007d9 2026-10-15 #19819 | staff_person |
| 14 | Consumer: identity settle miss | same | person identity not settled | FACT | LOG + ALERT | Engineer | (consumer) | person_identity_settled_miss |
| 15 | Consumer: application member never minted | same | group member row missing | FACT | LOG only | None | (consumer) | application_group_member_never_minted |
| 16 | Consumer: card folded into another | same | card was folded into an existing lead | PROVENANCE | LOG only | None | 18aff74472 #20090 | GUEST_CARD_FOLDED_REASON constant |
| 17 | Consumer: historical lead, no inquiry row | same | old lead with no inquiry row | PROVENANCE | LOG + OPS | None | 9c2ede05f8 #12212 | historical_lead_without_inquiry_row; ops cause first_email_stale_skip |
| 18 | Consumer: answered elsewhere / no home | same | per-inquiry event without a home, or already answered on another lane | FACT | LOG only | None | 59d8d52ebb #11585 | per_inquiry_event_without_home, answeredElsewhere |
| 19 | Consumer: already handled / claimed | same | intent row exists or claim held | FACT (idempotency) | LOG only | n/a | (consumer) | already_handled, already_claimed, stale_*_claim_reported |
| 20 | Consumer: vendor id on the lead | same | lead id resolves to a vendor | PROVENANCE | LOG + ALERT | Engineer | (consumer) | alertOnSilentSkip cause lead_received_terminal_skip |
| 21 | Consumer: compose kept failing for 24h | same | transient compose failure past the 24h retry window | FACT | INTENT heldReason compose-failed + notice + OPS | scripts/guest-card-rerun-one.ts only | 89e43e6e66 2026-10-20 #21446 | compose_failed_expired |
| 22 | Mailbox lane already made first touch | L/clara-guest-card-reply-composer.ts (claimLeadFirstTouch) | the mailbox lane holds the first-touch claim | FACT | INTENT heldReason | None (by design, a reply already went) | e80a5485af 2026-09-30 #14504 | 'First touch already made by the mailbox lane - recorded, nothing sent'; heldReason first-touch-claimed-by-mailbox |
| 23 | No intent row after prepare/send | L/clara-guest-card-reply-composer.ts (outcomeFor) | prep wrote nothing | FACT (bug) | LOG | None | (composer) | 'No ClaraFirstEmailIntent row after prepare/send - holding' |
| 24 | Org lookup failed at prep (fail closed) | L/clara-guest-card-engagement-prep.ts ~L440 | org read threw | FACT | INTENT heldReason org-not-live | Rerun script | #20994 | 'Organization lookup failed for the first-email intent hold check - holding (fail-closed)' |
| 25 | Company not live at prep | L/clara-guest-card-engagement-prep.ts L445 | mode === 'off' (orgNotLive) | POLICY | INTENT heldReason org-not-live; NO alert, NO ops card | Nobody is told. Rerun script only; going live does not resend | #20994 | heldReason org-not-live |
| 26 | Engine turn failed | L/guest-card-engine-turn.ts | routedTo 'failed' → GuestCardEngineTurnFailedError | FACT | INTENT compose-failed / compose-rate-limited; OPS first_email_compose_failed (not for rate-limited) | Consumer retry within 24h, then rerun script | f887f80f00 2026-10-24 #22249 | 'Compose failed while preparing the first-email intent - holding without a draft' |
| 27 | Engine turn produced no reply | L/guest-card-engine-turn.ts | engine routed elsewhere, no text | FACT | INTENT no-reply; OPS first_email_no_reply; ALERT | Rerun script | #22249 | 'Engine turn produced no reply for this guest card - recording no-reply'; noReply.reason engine turn produced no reply (routedTo: X) |
| 28 | Bare hand-off body (redraft) | L/guest-card-engine-turn.ts firstEmailDeterministicFindings | body is only "the team will reach out" | FACT | redraft once, then INTENT first_email_check_failed; OPS first_email_held; ALERT | Rerun script | aa1807eaef 2026-09-26 #12546 (moved 50b15daf70 #21003) | check bare_handoff |
| 29 | Draft checks (redraft) - container | L/guest-card-engine-turn.ts | runs detectFirstEmailDraftDefects (rows 30-38) | FACT | redraft once, then INTENT first_email_check_failed | Rerun script | #22249 | check draft_checks, detail = kinds joined |
| 30 | No tour times offered (redraft) | L/guest-card-first-email-draft-checks.ts L462 | requireTourTimes && !draftOffersSlot (open home, slots on hand; off for self-guided "any time" and returning-unbooked) | FACT | as row 29 | Rerun script | 4944552756 2026-09-28 #13351 (170-card replay: unsized cards got no times) | kind no-tour-times; snippet 'open home with tour times on hand, none offered' |
| 31 | One price for all sizes (redraft) | same L466 | draft quotes one rent while distinctRents >= 2 | FACT | as row 29 | Rerun script | #13351 | one-price-for-all-sizes |
| 32 | Unit label on a single home (redraft) | same L469 | "Unit 313" on a single-family house | FACT | as row 29 | Rerun script | b96313e12f 2026-09-28 #13471 | unit-label-on-one-home |
| 33 | Hedged money fact (redraft) | same L471 | "around/about $X" for a known rent/deposit | FACT | as row 29 | Rerun script | #13471 | hedged-money-fact |
| 34 | Implies prior contact (redraft) | same L473 | "following up / as we discussed" with no prior contact | FACT | as row 29 | Rerun script | daae46baf7 2026-09-29 #13568 | implies-prior-contact |
| 35 | Size question with no tour ask (redraft) | same L476 | open home, no slots, draft asks size but not for a tour | FACT | as row 29 | Rerun script | #13568 (Camellia card 108) | size-question-no-tour-ask |
| 36 | Dash in draft (redraft) | same L479 (findDash) | em/en dash in body | POLICY (style) | as row 29 | Rerun script | ffc2a036a8 2026-10-24 #22178 (Fede review set #30) | dash-in-draft |
| 37 | "Gone" claim on a home of unknown status (redraft) | same L481 | says leased/unavailable while availabilityUnknown | FACT | as row 29 | Rerun script | db55121563 2026-10-19 #21058 | gone-claim-on-unknown-home |
| 38 | Unbacked promise (redraft) | same L483 | "keep you posted", "on file", "keep an eye out"; on-opening + contact-later; team-follow-up unless backed; confirm-availability unless backed | FACT | as row 29 | Rerun script | 083582f478 2026-10-19 #21085 (#13520 replay) | unbacked-promise |
| 39 | Names no home (redraft) | L/guest-card-engine-turn.ts (detectNoHomeFactBody) | no home fact in body | FACT | redraft, then INTENT no_home_named (when only check) | Rerun script | #21003 | check names_home; heldReason no_home_named |
| 40 | Fact trace (redraft) | L/guest-card-engine-turn.ts | every stated fact traces to KB/listing | FACT | redraft, then INTENT fact_trace_untraced / fact_trace_kb_unavailable (when only check); OPS; ALERT | Rerun script | 432715be12 2026-09-25 #12074 | check fact_trace, detail ${action} ${heldReason}: items |
| 41 | "How much is open" disclosure (redraft) | L/guest-card-engine-turn.ts (findAvailabilityDisclosures) | vacancy counts / unit roster | POLICY | redraft, then INTENT first_email_check_failed | Rerun script | 4aacf771d1 2026-10-22 #21806 | check how_much_is_open |
| 42 | PII (redraft) | L/guest-card-engine-turn.ts (redactSensitivePatterns) | SSN/card-like numbers or redaction marker | POLICY | redraft, then INTENT first_email_check_failed | Rerun script | #22249 | check pii, detail hits or 'redacted_marker' |
| 43 | Send-door check pre-run (redraft) | L/guest-card-engine-turn.ts | runs the send-door finding at compose time | FACT | as row 42 | Rerun script | #22249 | sendDoorCheck finding |
| 44 | Writer retry outcome (log) | L/guest-card-engine-turn.ts | one retry after any finding | - | LOG | - | #22249 | 'First-email check fired - the writer\'s one retry passed' / 'First-email check fired - held after the writer\'s one retry, nothing sent' / 'First-email check retry failed - held, nothing sent' |
| 45 | Deferred ask not backed | L/clara-guest-card-engagement-prep.ts | draft defers a question to the team with no forward; auto-backed via backUncoveredDeferredAsks when it is the only problem | FACT | INTENT deferred_ask_unbacked; ALERT; NO ops card | Rerun script | cbd78a61de 2026-09-25 #12360; backing 532c33424b #12409 | heldReason deferred_ask_unbacked |
| 46 | Safety pipeline at prep | L/clara-guest-card-engagement-prep.ts → agents/clara/lib/agent/outbound-safety-pipeline.ts | body guard + policies + hallucination guard (rows 47-58) | FACT/POLICY | INTENT guard-blocked, no draft stamped; OPS first_email_held; ALERT | Rerun script | 9dced10129 2026-09-19 #9721 | 'Outbound safety pipeline blocked the composed draft at prep - held, no draft stamped' |
| 47 | Body guard: process narration | L/first-email-body-guard.ts L141 BLOCKING_DEFECT_KINDS | "I checked our system…" | POLICY | guard-blocked (prep or send) | Rerun script | 45af5487db 2026-09-23 #11252 | guard first_email_body_guard, reason process-narration: <snippet> |
| 48 | Body guard: listing fact contradiction | same | bed/bath/rent contradicts listing | FACT | guard-blocked | Rerun script | ff8e3b434c 2026-09-24 #11409 (WS 2954, 2.5 bath) | listing-fact-contradiction: … |
| 49 | Body guard: stale tour slot | same | offered slot no longer open/past | FACT | guard-blocked | Rerun script | da234858ed 2026-09-23 #11399 | stale-tour-slot: … |
| 50 | Body guard: weekday/date mismatch | same | "Tuesday Oct 28" wrong weekday | FACT | guard-blocked | Rerun script | 50099278b1 2026-09-26 #12530 | weekday-date-mismatch: … |
| 51 | Body guard: unsupported policy claim | same | pet/deposit/etc. claim not in KB | FACT | guard-blocked | Rerun script | #11399 | unsupported-policy-claim: … |
| 52 | Body guard: bare hand-off | same | as row 28, second line of defense | FACT | guard-blocked | Rerun script | aa1807eaef #12546 | bare-handoff: … |
| 53 | Body guard: note about the prospect | same | internal note text leaked into body | PROVENANCE | guard-blocked | Rerun script | 726cdd2fd3 2026-10-25 #22314 | note-about-prospect: … |
| 54 | Body guard: no greeting / text before greeting | same | log only, ships (and marks review holds, row 61) | - | LOG | - | 7b9d7b408b #11410 | no-greeting, pre-greeting-text |
| 55 | Policy: promise backing (blockOnly) | src/lib/domain/compliance/outbound-safety.ts (blockOnly branch) | unbacked follow-up promise; "rewrite" verdict becomes a hold | FACT | guard-blocked | Rerun script | 9869235253 2026-08-19 #5915; blockOnly #22249 | guard outbound_policy_hold, reason promise-backing: Unbacked follow-up promise, held (block only) |
| 56 | Policy: booking promise | same | "you're booked" with no booking (tour replies) | FACT | guard-blocked | Rerun script | 7bf664079a 2026-09-18 #9635 | outbound_policy_hold, booking-promise: … |
| 57 | Policy: availability disclosure | same | counts/roster of open units | POLICY | guard-blocked | Rerun script | #21806 | outbound_policy_hold, [guard:availability-disclosure] reply disclosed how much is open - removed (logWarn 'outbound-safety') |
| 58 | Hallucination guard: tour time with no tool | outbound-safety-pipeline.ts | tour time stated not in tourSnapshot | FACT | guard-blocked | Rerun script | e2925564b7 2026-09-24 #11936 | guard hallucination_guard, tour_time_no_tool |
| 59 | Fair housing (alert only, does NOT hold) | outbound-safety.ts ALERT_ONLY_POLICY_IDS | FH classifier | POLICY | sends + flagged-sent card | n/a | #21159 | event clara_flagged_sent |
| 60 | Send door: no email on file | L/clara-guest-card-first-email-send.ts L382 | no address | FACT | LOG only, no record | None | 07fde39148 2026-09-18 #9425 | 'Guest card has no email on file - nothing to send yet' |
| 61 | Send door: lost claim | same L510 | another run owns the send | FACT | LOG | n/a | (send door) | 'Another run owns this first-email send - not sending twice' |
| 62 | Send door: stale tour plan | same L519 | draft from old composer carries a tour plan | PROVENANCE | INTENT compose-failed; ALERT | Rerun script | 40dbd0fd86 2026-10-19 #21005 | 'First email carries a stale tour plan from the old composer - held, nothing sent' |
| 63 | Send door: no stamped draft | same L536 | pending intent has no draft | FACT (bug) | INTENT compose-failed / no-reply / send-failed | Rerun script | (send door) | 'Pending intent has no stamped draft - this is an upstream bug, not a reason to recompose' |
| 64 | Send door: safety re-run | same L685 runOutboundEmailSafetyChecks | rows 47-58 again at send (composedAt differs, can fail after a prep pass) | FACT/POLICY | INTENT guard-blocked; booked tour cancelled; ALERT | Rerun script | #9721 | 'Outbound safety pipeline blocked the stamped draft - held, not sent' {guard, reason} |
| 65 | Supervised review hold | same L758 (forcedReview or shouldSpeakFirstEmail → hold; src/lib/domain/modules/should-speak.ts) | company claraLive AND Leasing module = 'drafts' | POLICY | ESC source clara_prospect_email, resume first_email_intent; INTENT heldReason review + reviewHoldId | Staff: /review or /admin/escalation-holds/[id] (ApproveRejectButtons), or Slack | 330cc0d61c 2026-09-19 #9948; shouldSpeak 961cf34d93 2026-10-26 #22473 | "Clara's first email touch held for review"; logInfo 'shouldSpeak' Clara ${verdict}: ${reason} |
| 66 | Review hold marked defective | same (gradeHeldFirstEmail) | held draft has no greeting / double greeting | FACT | ESC needsAttention | same as 65 | 7b9d7b408b 2026-09-23 #11410 | 'Held a first email that is defective as the reviewer will read it - the hold is marked for attention' |
| 67 | Approve door refusals | L/clara-guest-card-first-email-send.ts (approve path) | tour gone / approved text changed / approved text off the booked tour | FACT | INTENT tour-gone-at-approval / approved-text-changed / approved-text-off-booked-tour | Re-review | (approve path) | those heldReason strings |
| 68 | Review hold could not be written | same (parkForcedReviewRun) | forced-review rerun failed before the hold | FACT | INTENT review-hold-failed | Rerun script | 07b081cb62 2026-10-07 #17267 | 'Review-only first-email rerun stopped before its review hold (...) - parked, nothing sent' |
| 69 | Lost stamp before transport | same L966 | claim reclaimed by a later run | FACT | LOG | n/a | (send door) | 'This run lost its first-email send claim before the transport was called (reclaimed by a later run) - not sending' |
| 70 | Transport: company not live | src/lib/integrations/email/property-graph-sender.ts + src/lib/domain/organizations/clara-live.ts | org_not_live / org_unresolved | POLICY | INTENT org-not-live; NO alert | None (no resend on go-live) | (clara-live) | Held: company is not live yet (org_not_live) for org X (to=...) / Held: company could not be resolved for the company-live gate (org_unresolved); "Clara's first email touch held (company not live)" |
| 71 | Transport: refused | property-graph-sender.ts | opt-out, contact ceiling, budget unreadable, PMS has no card, hard bounce, module off/unresolved, test-scope recipient, module supervised | FACT/POLICY | INTENT send-refused + refusalCode; ALERT | Rerun script after the cause is fixed | 9f6efc10c2 2026-09-19 #9941; test scope 9422e08273 #15863; supervised 9924d6ba50 #16495 | "Clara's first email touch was refused - held, and the company-live gate is not why"; refusalCode opted_out / contact_limit_reached / contact_budget_unreadable / pms_no_guest_card / hard_bounce / module_off / module_unresolved / test_scope_recipient / module_supervised; Suppressed: <to> is opted out (...) - PMS leg, nothing sent; Held: <to> is at their contact ceiling (...) |
| 72 | Transport: unknown outcome | same | transport threw / ambiguous | FACT | INTENT send-unknown; ALERT | Manual check, rerun script | #9941 | "Clara's first email touch ended with an unknown outcome - held as send-unknown (nobody knows whether it delivered)" |
| # | Gate (plain words) | file:line (origin/main) | What it checks | Decides by | When it fires + record | Who can release, from where | Added | Literal log tag / record field / reason string |
|---|---|---|---|---|---|---|---|---|
| 73 | Enrollment: identity blocked | L/outreach-trigger.ts | lead is vendor/staff/resident etc. | PROVENANCE | LOG (record kept) | None | 160b427906 #4332 | Enrollment blocked - lead is a ${reason} (cadence not started; record kept for PM visibility) |
| 74 | Enrollment: cadence turned off | same | account follow-up sequence off | POLICY | LOG | Setting | (trigger) | 'Follow-up sequence is turned off for this account - cadence not started' |
| 75 | Enrollment: exhausted / joined person track | same | cadence already ran / person-level track exists | FACT | LOG | None | (trigger) | cadence-exhausted, joined-person-track |
| 76 | Enrollment: portfolio lead at a dark company | same | org_not_live | POLICY | LOG | None (no enroll on go-live) | f8d173d799 2026-09-14 #8375 | 'Portfolio-arrived lead - cadence not started, company not yet live (org_not_live)' |
| 77 | Touch: send claim pending | PO | prior touch claim in flight | FACT | TOUCH | n/a | 9fc424f0dc #4321 | send-claim-pending |
| 78 | Touch: prospect deleted / merged | PO | record gone or merged | FACT | TOUCH, stop | None | (PO) | prospect-deleted, prospect-merged |
| 79 | Touch: lead converted | PO + outreach-stop-conditions | replied, applied, closed, tour_booked, merged | FACT | stop | None | ca341b6883 2026-06-19 #2520 | 'Outreach cadence stopping - lead converted (${stopReason})' |
| 80 | Touch: vendor/solicitor thread | PO | person is vendor | PROVENANCE | stop | None | de09e23a5c #17371 | 'Outreach cadence stopping - the person on this thread is a vendor/solicitor, not a prospect' |
| 81 | Touch: person clock | PO | personStop toured_here / applied / other; foldInto | FACT | stop | None | 3e069fd867 #18305 | personStop toured_here / applied / other |
| 82 | Follow-up gate (model look) | PO checkFollowUpGate; L/follow-up-gate.ts | current resident, asked to stop, card closed lost/leased, tour happened staff on it, staff promise pending (wait), teammate answered and no renter reply since; fails OPEN after 8s | FACT | TOUCH skip/wait | None | 8493d49a31 2026-10-10 #18625 | 'Outreach follow-up gate: ${decision} (${reason})'; reasons current_resident, asked_to_stop, card_closed_lost_or_leased, tour_happened_staff_on_it, staff_promise_pending, teammate_answered_no_renter_reply_since; follow_up_wake_look |
| 83 | Touch: first email not sent / unconfirmed | PO | intent not sent | FACT | TOUCH | None (follows first email) | f56bd3912a #17261 | first-email-not-sent, first-email-unconfirmed |
| 84 | Touch: no property / home truth or roster unavailable / home gone | PO | data reads fail or home left market | FACT | TOUCH (stop on home gone) | None | 93e033fe85 #12502; 7f7fd77f26 #10558 | no-property, home-truth-unavailable, roster-unavailable, home-gone |
| 85 | Touch: no contact / no consent / no phone | PO | missing channel or SMS consent | FACT | TOUCH | None | 63e7329220 #2825 | no-contact, no-consent, no-phone |
| 86 | Touch: asked not to be contacted | PO | suppression list | FACT | TOUCH | None | 771483ffa3 #3854 | 'Prospect asked not to be contacted - skipping touch' |
| 87 | Touch: Leasing switched off | PO | module off | POLICY | TOUCH | Setting (next touch) | 52058d95b7 #10420 | Cadence ${channel} touch held - the company has Leasing switched off , reason module_off |
| 88 | Touch: SMS company not live | PO | org_not_live | POLICY | TOUCH | Go-live (next touch) | 8cc10fcc67 #10132 | 'Cadence SMS touch held - company is not live' |
| 89 | Touch: identical text already sent | PO | repeat body on thread | FACT | TOUCH | None | (PO) | 'Cadence SMS not sent - identical text already sent on this thread' (+ email twin) |
| 90 | Generator check (redraft) | L/generate-followup-message.ts followUpFinding | dash, blast opener, pricing, policy claim, unit type, tour slot, availability:<kind>, empty | FACT/POLICY | one retry, then held {check,reason} → row 91 or 92 | see 91/92 | 934cc05acf 2026-10-25 #22306 | 'Generated copy still failed a check after one retry, holding it for a person'; labels dash, blast-opener, pricing, policy-claim, unit-type, tour-slot, availability:<kind>, empty-retry |
| 91 | Generator held, no workflow to wait on | PO | held copy outside a Temporal wait | - | dispatch-skipped, NO hold record | None | #22306 | dispatch-skipped |
| 92 | Held for a person / Supervised | PO + L/leasing-prospect-text-review.ts | check held after retry, or Leasing = Supervised | POLICY/FACT | SMSH eschold_lt_…, 24h expiry | Staff: /review, /admin/escalation-holds/[id], Slack. Expiry = never sent | 62dbb95391 2026-10-01 | 'Cadence SMS touch held for a person - the copy failed a check after one retry' / 'Cadence SMS touch held for approval - Leasing is Supervised at this building' / 'Cadence email touch held for approval - Leasing is Supervised at this building' |
| 93 | Approved text refused at send | L/leasing-prospect-text-review.ts | hold not found / other property / not approved / other workflow | FACT | TOUCH | Re-hold | 62dbb95391 | 'Approved cadence text not sent - the hold did not check out'; 'approved hold not found', 'approved hold is for another property', hold is ${state}, not approved, 'approved hold belongs to a different workflow' |
| 94 | Email: company not live (MISLABELED) | PO | transport 'suppressed' (now only company-not-live) | POLICY | TOUCH | Go-live | (PO) | 'Cadence email skipped - prospect opted out (suppressed)' - wrong words, see note |
| 95 | Email: no mailbox | PO | property has no mailbox | FACT | TOUCH | Connect mailbox | (PO) | no-mailbox |
| # | Gate | file:line | What it checks | Decides by | When + record | Release | Added | Literal |
|---|---|---|---|---|---|---|---|---|
| 96 | Tour not found | src/lib/temporal/activities/tour.ts | tour row gone | FACT | ALERT (alertTourActivityFailureSkip) | None | (tour) | tour-not-found |
| 97 | Already sent this version | same | idempotency | FACT | LOG | n/a | (tour) | tour X already sent T-24h for schedule version - no-op |
| 98 | Premise failed | same | cancelled / declined / stale anchor version | FACT | LOG | None | 720b1c384b #4274 | tour X premise failed (${premiseSkip}) - skipping reminder |
| 99 | Slot lost / weekend check-in replaces / same-day or past / stale anchor | same | time freshness | FACT | LOG | None | 1feb4d012c #13749; f7f4d54d7d #13975; 87ca56d9c9 2026-06-05 #1969; 2079a753c9 #4235 | slot-lost, weekend-checkin-replaces, same-day-or-past, tour X stale timer anchor - skipping T-24h reminder (...) |
| 100 | No prospect / test property / no phone / no outbound line | same | data | FACT/PROVENANCE | LOG | None | (tour) | prospect-not-found, no-phone, no-outbound-line |
| 101 | Supervised reminder | same | Leasing = Supervised | POLICY | SMSH | /review, admin, Slack | c22eff5f59 #15161 | reminder for tour X held for approval - Leasing is Supervised (hold ...) |
| 102 | Reminder SMS skipped / module off | same | send gate | POLICY | LOG | Setting | (tour) | SMS skipped for tour X - ..., held_module_off |
| # | Gate | file:line | What it checks | Decides by | When + record | Release | Added | Literal |
|---|---|---|---|---|---|---|---|---|
| 103 | Tour time not read this turn (redraft) | CM ~L14835 | reply states a time not in this turn's slot read | FACT | retry, then PARK | None | ce62f94a74 2026-10-16 #20338 | subReason tour_time:not_in_this_turn_read; reportFallback surface clara-tour-time-this-turn; trace safety:tour-time-this-turn |
| 104 | Unbacked tour confirmation (redraft) | CM | "you're booked" with no booking | FACT | retry, then PARK + alertUnbackedTourConfirmation | None | ad65443706 #14353 | tour_booking:unbacked_confirmation |
| 105 | Unbacked tour-time claim on first response | CM (detectUnbackedTourTimeClaim) | times claimed with no tool call | FACT | retry/hold | None | 6fe5795759 2026-09-30 #14124 | detectUnbackedTourTimeClaim |
| 106 | Work-order claim | CM | "filed a work order" without one | FACT | PARK | None | (CM) | 'Work-order claim check held a reply for human review - nothing was sent' |
| 107 | Unit count stated | CM | how many units are open | POLICY | PARK | None | #21806 | [guard:unit-count-stated?] held a reply for human review - nothing was sent; subReason availability:how_much_is_open |
| 108 | Outbound policy | CM | promise-backing / booking-promise / availability policies (blockOnly) | FACT/POLICY | PARK | None | #22249 | 'Outbound-safety policy held a reply for human review - nothing was sent'; outbound_policy:<policyId> |
| 109 | PII | CM | sensitive numbers | POLICY | PARK | None | (CM) | 'PII check held a reply for human review - nothing was sent'; pii:sensitive_number |
| 110 | Dash | CM | em/en dash | POLICY | PARK [DASH-HELD dash in reply] | Slack card thread (dash_in_reply is releasable) | 995c9f204d 2026-10-24 #22236 | 'No-dashes check held a reply for human review - nothing was sent', guard dash_in_reply |
| 111 | Send gate (every text/email reply) | CM ~L15948 sendGateFinding | weekday_date, tour_time_this_turn, unbooked_tour, work_order_claim, unit_count, outbound_policy:<id>, work_order_confirmation, pii, dash, hallucination_guard, unbacked_promise_in_retry | FACT/POLICY | PARK | weekday_date and dash only via Slack thread; others none | bc642b99fc 2026-10-25 #22296 | 'The send gate held a reply - nothing was sent'; subReason send_gate:<finding>; trace safety:send-gate |
runOutboundTextComplianceGate (src/lib/integrations/twilio/client.ts)| # | Gate | file:line | What it checks | By | Fires / record written | Release | Added | Count it by (literal) |
|---|---|---|---|---|---|---|---|---|
| 1 | Harness kill switch | twilio/client.ts:928-930 | Test harness has suppressed all real sends | POLICY | Any send while harness flag on. Fake sid returned, recordSend to harness log only | Turn harness off (env) | b1f2bb5c3f 2026-06-18 #2462 (CRUCIBLE turnover voice harness) | HARNESS_SUPPRESSED_SID in result sids |
| 2 | Prompt canary | twilio/client.ts:940-943 | Body contains system-prompt canary PF-8X2M9K (prompt leak) | FACT (body) | Leak. No row; error log | None (message dropped) | ≤9edb2c2359 2026-05-04 #600 | CANARY / System prompt canary detected in outbound ${CHANNEL} to ${to} - BLOCKING SEND; alarm:'canary_detected' |
| 3 | SMS consent / suppression (STOP) | twilio/client.ts:953-989 → checkSmsSendAllowed sms-consent-gate.ts:106-131 → checkSuppression suppression-gate.ts:200 | Revoked consent row or suppression row for the number; lookup failure fails closed for outreach | FACT | Opted-out recipient. No new row (reads existing) | Person texts START (webhook:338), or staff clears do-not-contact | 2a7745073b 2026-07-08 #3295 (consent gate + reactive opt-out); suppression f648ce4796 2026-07-15 #3859 (ADR-0092) | Outbound ${CHANNEL} to ${maskPhone(to)} blocked - ${gate.reason}; reasons SMS consent revoked at ..., Communication suppressed (opted out X, scope=Y), Legacy consent lookup failed - blocked (fail-closed for outreach), Suppression lookup failed - blocked (fail-closed for outreach); refusal:'opted_out' |
| 4 | AppFolio OTP gateway | twilio/client.ts:999-1024 | Recipient is AppFolio's OTP short code (reply loop) | FACT (number) | Loop regression. Alert emitted | None | d26b9e10f2 2026-06-03 #1893 (OTP reply loop on Test-Property line) | ... to AppFolio OTP gateway ... - BLOCKING SEND (reply-loop regression); alert kind appfolio-otp-reply-loop; alarm:'appfolio_otp_gateway_blocked' |
| 5 | Stress-harness number | twilio/client.ts:1026-1027, 484-505 | Recipient is a stress-test number | PROVENANCE (synthetic) | Stress runs. No row | None needed | 5046b0bd99 2026-07-26 #4597 (direct sends bypassed the guard) | stress-guard / ... was intentionally not sent ...; field suppressedSid |
| 6 | Test-scope recipient | twilio/client.ts:1055-1074 → test-scope-recipient-guard.ts:78 | Test building or sandbox company may text only numbers in config/phone-registry.json (login_code exempt) | POLICY | Test property texting a real number. No row | Add number to registry (PR) | 8d9148f003 2026-10-02 #15782 | Outbound ... refused - ${testScope.reason}: a test building or sandbox company may text only a number listed in config/phone-registry.json; reasons test_building, sandbox_company; refusal:'blocked' |
| 7 | Flipped line refused | twilio/client.ts:1086-1087, 867-902 | Sending line no longer authorized (portfolio flip, address revoked) | FACT | Text from a revoked line. Sentry warning + bake alert, deduped 24h | Re-authorize the line | 8d41e5fa8e 2026-10-08 #17819; page 07662167fc 2026-10-22 #21870 | Outbound text refused: the sending line is no longer authorized to send; fingerprint flipped_head_refused; alert kind flipped_line_send_refused; reason address_revoked |
| 8 | Module off | twilio/client.ts:1104-1196 | Company's module for this send (leasing/renewals/maintenance...) is off or unreadable | POLICY | Module off. Row: recordModuleRefusal({channel, module, reason}); critical page | Turn module on (company settings) | 3c9968d5db 2026-09-25 #12142 (record); gate older | Outbound ${CHANNEL} to ... held - module ${module} is not enabled for this company (${entitlement.reason}) for org ...; reasons like leasing.renewals_module_off; unresolved: Outbound ... SENT despite an unresolved module read - module ...; result heldByModule |
| 9 | Supervised drafts backstop (record only; rule excluded) | twilio/client.ts:1197-1250 | Excluded R-rule refusal at the door | POLICY | recordModuleRefusal reason:'module_drafts', DRAFTS_BACKSTOP_PAGE | Review queue | - | module_drafts; waiver isHeldReplyAckText (1216, 1a66d97cbe 2026-10-10 #18564) |
| 10 | Off means off (company not live) | twilio/client.ts:1260-1274 → organizations/off-means-off.ts | Not-live company sends no *notifications*; replies and on-the-line people allowed; login_code exempt | POLICY | Not-live org. No row | Company go-live | 4c61370c45 2026-10-20 #21381 [urgent] (Fede 10/19 "off means off") | Outbound ... refused - org not live: notification suppressed (${reason}); reasons org_not_live, org_unresolved |
| 11 | Contact ceiling | twilio/client.ts:1313-1317 → checkTouchBudget | Touch ledger over per-person ceiling; unreadable = refuse | FACT (ledger) + POLICY (ceiling) | Over budget. No row (ledger write happens only on send) | Time passes (window rolls) | 6b3a2a712f 2026-08-03 #5232 | Outbound ... held - ${budget.reason}; refusal:'contact_limit_reached' or 'blocked' |
| 12 | Carrier 21610 (reactive opt-out) | twilio/client.ts:1366-1384; status/route.ts:109 | Twilio says the number is STOPped at carrier | FACT (carrier) | Send rejected by Twilio. Writes consent revocation (source:'twilio_21610_blocked_send' / 'twilio_21610_status_callback') | Person texts START | 2a7745073b 2026-07-08 #3295 | ...rejected send ... as opted-out (21610) - recorded consent revocation; refusal:'opted_out' |
| 13 | Outbound call creation refused | twilio/client.ts:321-345 | Twilio refuses the call | FACT (carrier) | No row | None | ≤#600 | createOutboundCall to ... refused (Twilio code X) |
evaluateCompanyLiveGate + sendEmail + Graph/PMS legs| # | Gate | file:line | What it checks | By | Fires / record | Release | Added | Count it by |
|---|---|---|---|---|---|---|---|---|
| 14 | Test-scope recipient (email) | company-live-gate.ts:358-377; guard :144 | Test building/sandbox may email only our own inboxes; hello@ / hello+ shared inbox refused (#20911/#20912) | POLICY | BLOCKED row | Not releasable (by design) | 9422e08273 2026-10-03 #15863 [urgent]; hello@ d13d361820 2026-10-18 #20912 | errorMessage 'test_scope_recipient'; log Refused: ${reason} may email only our own inboxes (test_scope_recipient) ... |
| 15 | Module off (email) | company-live-gate.ts:391-453 | Module off/unreadable | POLICY | BLOCKED row + recordModuleRefusal channel:'email' | Turn module on | 98599a1e36 2026-09-17 #9177 | errorMessage = entitlement reason e.g. leasing.prospects_module_off; Held: module ${module} is not enabled (${reason}) for org ... |
| 16 | Supervised (email, record only) | company-live-gate.ts:464-495 | excluded rule | POLICY | BLOCKED row | Review queue | - | errorMessage ${module}_module_supervised; reason module_supervised; EMAIL_DRAFTS_BACKSTOP_PAGE |
| 17 | Company not live (email) | company-live-gate.ts:509-559; staff branch :331 sendWhenUnreadable | Org not live / unresolvable | POLICY | BLOCKED row | Go-live | 98599a1e36 2026-09-17 #9177; staff unreadable 4c61370c45 2026-10-20 #21381 | errorMessage org_not_live / org_unresolved; Held: company not yet live (org_not_live) for org ...; Held: company could not be resolved for the company-live gate (org_unresolved) |
| 18 | Email harness kill switch | email/client.ts:992 | Harness flag | POLICY | status:'suppressed' | env | d94ad7200d 2026-07-29 #4898 | harness_kill_switch; Suppressed by harness kill switch (no send) |
| 19 | Email canary | email/client.ts:1043 | Canary token in content | FACT | delivery row | None | d94ad7200d #4898 (reason string); canary older | canary_block; System prompt canary token detected in outbound content - blocked |
| 20 | Reserved fake domain | email/client.ts:1064, 1110; Graph inbox-client.ts:1672; PMS property-graph-sender.ts:1240-1275 | @example.test / @example.invalid with no Mailpit | PROVENANCE (synthetic) | suppressed | None | d94ad7200d #4898 | test_recipient_no_mailpit; Graph sendReply refused - test-domain recipient is never deliverable |
| 21 | Undeliverable recipient | email/client.ts:1210 | Address known undeliverable | FACT | suppressed | Fix address | d94ad7200d #4898 | undeliverable_recipients; Blocked send to undeliverable recipient |
| 22 | Prior bounce | email/client.ts:1246; inbox-client.ts:1691 | Address bounced before | FACT (bounce row) | suppressed | Clear bounce (SendGrid) | d94ad7200d #4898 | bounce_suppressed; Suppressed: ${bounceType} bounce on ...; Graph send skipped - ${to} bounced before as not existing; mapped bounce-suppressed |
| 23 | Email opt-out / mute | email/client.ts:1295; inbox-client.ts:1685; property-graph-sender.ts:458 | checkSuppression | FACT | suppressed | Person opts back in / staff | f648ce4796 2026-07-15 #3859; Graph c82955e82e 2026-07-15 #3860 | opted_out; Recipient opted out of communications (...); Graph sendReply skipped - recipient opted out (${category}). |
| 24 | Email contact ceiling | email/client.ts:~1396; inbox-client.ts:1700-1708 | Touch budget | FACT+POLICY | suppressed | Time | 6b3a2a712f 2026-08-03 #5232 | contact_limit_reached, contact_budget_unreadable; Graph sendReply held - recipient at their contact ceiling. / sendReply refused fail-closed - contact budget unreadable → budget-held |
| 25 | emailsEnabled kill switch | email/client.ts:1554 | Property/company emailsEnabled=false | POLICY | suppressed | Setting | d94ad7200d #4898 | emails_disabled; Blocked: emailsEnabled is false |
| 26 | Company-live verdict applied in sendEmail | email/client.ts:1599 | Result of row 14-17 | POLICY | suppressed | as above | - | companyLiveDecision.reason |
| 27 | Hard bounce on send | email/client.ts:~1690 | Provider rejects | FACT | failed | None | d94ad7200d #4898 | hard_bounce |
| 28 | Observer BCC dropped on test scope (not a block - hello@ never copied) | email/client.ts:1513 | Test scope | POLICY | - | - | d13d361820 2026-10-18 #20912 | Send on isTest scope: observer BCC not added |
| 29 | Graph-leg harness | inbox-client.ts:1661 | harness | POLICY | - | env | - | harness-suppressed |
| # | Gate | file:line | What it checks | By | Fires / record | Release | Added | Count it by |
|---|---|---|---|---|---|---|---|---|
| 30 | Stress / test sender | agents/clara/lib/messaging/dispatch-envelope.ts:289, 292 | Recipient is stress/test sender | PROVENANCE | Trace step only | - | 6ae64094ee 2026-05-17 #1100 | policy:stress_sender, policy:test_sender |
| 31 | Thread escalated (human takeover) | dispatch-envelope.ts:429-433; escalated-gate.ts:122 | Conversation is human-owned | FACT (escalation row) | Trace step only | Close escalation | eac606c1ae 2026-08-04 #5400; takeover at autonomous props c9064d2042 2026-10-05 #16448 | Skipped outbound on escalated conversation X ... - thread is human-owned.; policy:thread_escalated |
| 32 | Teammate replied (autoReplyHold) | dispatch-envelope.ts:464-469; auto-reply-hold.ts; process-pms-email.ts:141 | A human replied on thread → autoReplyHold='human_replied' (Fede 09-03 "IF another human replies we stop") | FACT | Hold stamped on conversation; outbound trace | Clear the hold on conversation (no UI found) | 2db2e02f0d 2026-09-03 #6870; email d18bb43283 2026-09-03 #6974 | Conversation X is now human-held - a teammate replied on it, so Clara will keep filing and processing but will not send.; ... a human teammate has replied on this thread (autoReplyHold=…), so Clara is not auto-replying on it.; policy:human_replied |
| 33 | smsEnabled kill switch | dispatch-envelope.ts:475-477 | smsEnabled=false (not applied to replies) | POLICY | trace | setting | 6ae64094ee #1100 | policy:sms_disabled |
| 34 | Synthetic / shadow origin | dispatch-envelope.ts:520-537 | Envelope origin is synthetic or shadow | PROVENANCE | trace | - | 064e8a7224 2026-05-10 #818 | origin:synthetic, origin:shadow |
| 35 | Envelope consent (STOP) | dispatch-envelope.ts:595-604 (pre-compose read :182 fails OPEN) | Opt-out (skipped for replies) | FACT | trace | START | 6ae64094ee #1100 | SMS Consent / Outbound to X blocked - recipient opted out (STOP). Reason: …. Message dropped. |
| 36 | Recordless lane (anti-gate: sends unheld) | dispatch-envelope.ts:386 | No record to hold on | - | logCritical, sends | - | - | logCritical at :386 |
| 37 | AppFolio text door hold | agents/clara/lib/messaging/adapters/appfolio-inbox-text.ts:212+ | Door refusals: media, daily ceiling, cold session, in-flight, duplicate; plus row A gate | FACT+POLICY | carrier:pms_text_held trace; alert when holding | Door recovers / retry | 3615858879 2026-10-14 #19463; media b4bb2b404f 2026-10-19 #20982 | carrier:pms_text_held; refused:attachments_unsupported; PMS text door got a text with media: held whole, nothing sent; text held at the AppFolio door: ${reason} (daily_ceiling, session_cold, in_flight, duplicate); ALERT AppFolio text door is holding Clara texts - nothing was sent by another door |
| 38 | PMS mirror wake-on-reply | src/lib/domain/pms-mirror/wake-on-reply.ts (89, 92); file-message.ts | Whether a mirrored AppFolio row wakes Clara at all | FACT + POLICY (role) | Watch log only | None | tenant 60ddb9fbf8 2026-10-21 #21563; teammate 55cedddedc 2026-10-19 #20992; role ea7b98fedd 2026-10-23 #22058 | reasons history, not_inbound, not_renter, not_text, tenant_thread, role_unknown, door_off, teammate_after_clara, clara_not_on_thread; file-message adds no_phone, wake_failed, coalesced, keyword |
| 39 | Inbound escalated gate (turn) | agents/clara/lib/agent/escalated-gate.ts escalatedGateSuppresses; conversation-manager.ts:7960 | Inbound turn on escalated thread (PM sender exempt) | FACT | No reply composed | Close escalation | 87f7f14db6 2026-08-04 #5387 | escalatedGateSuppresses (gate-registry escalated-gate 2026-08-04) |
| # | Gate | file:line | What it records | By | Fires / record | Release | Added | Count it by |
|---|---|---|---|---|---|---|---|---|
| 40 | Inbound STOP on Clara's line | src/app/api/twilio/webhook/route.ts:207-210, 333; START :338 | revokeSmsConsent + recordSuppression (suppression-gate.ts:483) + ops-feed card | FACT | consent + suppression rows | START text | ≤#600; card 3cbb5017ca 2026-08-29 #6570 | STOP keyword from ... - opt-out (Twilio handles + app DB revocation); unannounced Opt-out from ... not announced - destination number maps to no property. |
| 41 | STOP on AppFolio texting line | src/lib/domain/compliance/pms-line-opt-out.ts; pms-mirror/opt-out-keyword.ts | recordPmsLineKeyword, idempotent messageSid pms:<id>, sourceChannel PMS_TEXT_LINE | FACT | suppression row; sends nothing | START on that line | 17f3aa52da 2026-10-19 #20995; unplaced numbers 96a0282da0 2026-10-19 #21031 | sourceChannel PMS_TEXT_LINE, messageSid pms: |
| 42 | Voice opt-out (tool + post-call sweep) | handle-opt-out-of-communications.ts; voice-postcall-opt-out-sweep.ts:140, 384 | Caller asked to stop | FACT | opt-out event; bake alert on possible | Staff | 4c53c1a673 2026-07-15 #3861; flag c4f9f2b444 2026-10-17 #19767 | Recorded opt-out ${event.id} on ...; Opt-out already on file (scope=all ...) - no second event; opt-out captured by sweep, tool was missed - conversation X; alert voice_possible_opt_out |
| 43 | Person mute | src/lib/domain/compliance/person-mute.ts; suppression-gate.ts | Staff muted the person: no automated reply; deliberate human send allowed | POLICY (staff decision) | mute row | Staff unmutes | b6ad311807 2026-08-28 #6526 | muted ${identifierType} by ${actor}; Person is muted (since X) - no automated reply; Muted - deliberate human send allowed; mute stays on |
| 44 | Suppression exemptions (allow with audit) | suppression-gate.ts | Exempt category / transactional reply to own inbound | POLICY | audit log, sends | - | f09851247a 2026-07-15 #3849 | Exempt category ... - override logged; Transactional response to the person’s own inbound - audit-logged |
| # | Gate | file:line | What it checks | By | Fires / record | Release | Added | Count it by |
|---|---|---|---|---|---|---|---|---|
| 45 | Renewal outreach quiet hours | src/lib/temporal/activities/renewal.ts:897, 1377, 2202, 2609; quiet-hours.ts:226 | Outside window (property tz) unless bypassQuietHours | POLICY (time) | NOOP that returns 'dispatched' - workflow believes it sent | Next workflow tick | a892561346 2026-05-16 #1001; tz fix c261d04d97 2026-09-23 #11366 | sendOutreachSms: quiet hours - NOOPing |
| 46 | Prospect cadence quiet hours | prospect-outreach.ts:1809 | window | POLICY | workflow sleeps holdMs | auto | 5a33c8acdf 2026-06-18 #2470 | holdMs |
| 47 | Maintenance comms / handyman page | maintenance-comms.ts:1440-1457; tech-sms-dispatch.ts:677; maintenance/quiet-hours.ts | Per-handyman window default 21:00-07:00 ('none' = opt out); emergency always sends; test property → 0 | POLICY | held via signal dispatch-page:${wo.id} | auto at window end | 2f417de900 2026-06-10 #2106; c2e517dc98 2026-06-11 #2249 | eventId dispatch-page:${wo.id} |
| 48 | Collections dun window | collections/approved-dun-send-ops.ts:119; recipient-clock.ts | Property clock; refuses with no zone | POLICY | refused | sweep retries | d17dcc5808 2026-08-25 #6268 | timezone_unusable |
| 49 | PMS-generated application send | pms-generated-application-send.ts:400 | window | POLICY | {ok:false, reason:'quiet-hours'} | outbox retry | 27fd5d3229 2026-09-19 #9783 | send_application_deferred_quiet_hours |
| 50 | Voice callback | escalation/voice-callback.ts:162 | window etc. | POLICY | not placed | - | 315d9f1991 2026-08-19 #5904 | Voice callback not placed for matter X: ${decision.reason} |
| 51 | MtM warning deferral | renewal-auto-start.ts:2546 | window | POLICY | deferred | next scan | 48957836ab 2026-06-09 #2114 | MtM warning deferred for X: quiet hours (...) - next scan retries |
| # | Gate | file:line | What it checks | By | Fires / record | Release | Added | Count it by |
|---|---|---|---|---|---|---|---|---|
| 52 | 24h expiry | held-message-rule.ts:64; held-message-max-age.ts:17; held-text.ts:1375 expireStaleHolds; cron escalation-hold-sweep | Pending > 24h (unreadable createdAt = expired; parkedForBatch with unapproved batch never expires; workflow_signal holds skipped) | POLICY (time) | hold → rejected by system:draft-expired; email row markHeldEmailRowNotSent 'expired' (bake-gate.ts:2839); thread note | None after expiry | 4480301497 2026-10-05 #16510; sweep 4c296f2c33 2026-10-05 #16590 | HELD_MESSAGE_EXPIRED_BY='system:draft-expired'; Expired - not sent; Expired: a held message waits 24 hours for approval.; This waited more than 24 hours for approval, so it was not sent.; prospect Expired: a draft waits 24 hours, then the team takes the thread.; sweep line considered=… expired=…; Could not expire stale hold X |
| 53 | Approved, waits for send window | held-message-rule.ts:95 heldSendTiming; held-text.ts:836 | Approval during quiet hours → defer | POLICY | awaitingSendWindow, sendAfter; sweep sends later | auto | 4480301497 #16510 | send_now/defer/no_clock/unreadable; deferredSent= in sweep line |
| 54 | Approved but no time zone | held-text.ts (approve path); held-message-rule.ts | Building has no tz | FACT | approvedNotSent → deliveryFailed:true | Fix tz, re-send manually | 097f55d118 2026-10-07 #17249 | Held text X approved by Y - NOT sent: the building has no resolvable time zone; Property X has no time zone - an approved message is timed on the default zone ... |
| 55 | Approved but renewal gone / other approvedNotSent | held-text.ts | Renewal workflow no longer running; handyman quiet hours | FACT | deliveryFailed:true | None | 26e8fa649f 2026-09-28 #12956 | the renewal it belongs to is no longer running |
| 56 | Superseded by newer draft | held-text.ts:1040; held-reply-supersede.ts newerReplyVerdict; bake-gate.ts:3033 | Newer reply on same conversation | FACT | withdrawn by system:draft-superseded | None | a837f94f8f 2026-09-30 #14241 | Not sent: a newer draft on this conversation replaced it.; Approved email X closed unsent - a newer reply replaced it |
| 57 | Teammate took the thread | bake-gate.ts:464 (only clara_prospect_email + conversation_reply) | Staff replied since draft | FACT | rejected by system:human-replied | None | 39dad5e50a 2026-10-05 #16608 | A teammate has replied on this thread since the draft was written, so it was not sent.; Hold X approve by Y did NOT send - a teammate has replied on the thread; deferred Deferred approved email X closed unsent - a teammate has replied on the thread |
| 58 | Stale draft (4h / past slot) | bake-gate.ts:1938, 1955, 2185 | Draft older than 4h or offers past tour slot (clara_prospect_email; anchor draftComposedAt ?? createdAt) - origin WS 09-22/23 | FACT+POLICY | stays held as written (approve refused) | Reject / recompose | 26b430bb83 2026-09-24 #11417 | older-than-4h, offers-a-past-slot; Hold X approve by Y did NOT send - draft is stale (reason); it stays held as written; deferred Deferred approved email X went stale (reason) - closed unsent, not rewritten |
| 59 | Approved hold later dropped/bounced | bake-gate.ts:2662 | Provider drop after acceptance | FACT | flagged not-delivered | - | 706d482d9f 2026-10-05 #16761 | Approved hold X was dropped/bounced (via) after acceptance - … |
| 60 | Renewal batch gate | renewal-batch/batch-gate.ts | Monthly list not approved → parked | POLICY | parkedForBatch; release reason:'list approved' | Approve the monthly list | 917fee9d11 2026-10-09 #18320 | states none/approved/parked/unreadable; RenewalBatchGate / pointer read failed for lease X |
| 61 | Held-reply ack (one per thread) | held-reply-ack.ts decideHeldReplyAck | Resident texts again while reply held → one fixed ack, else silent (Gera D13) | FACT | - | - | 1a66d97cbe 2026-10-10 #18564 | Held list unreadable for conversation X; no ack this turn |
| 62 | Team-answer under Supervised hold | team-answer-supervised-hold.ts | Clara reply after staff answer goes through hold | POLICY | hold row | Review queue | 5f2ad13888 2026-10-16 #20477 | ${heldModule} Supervised: held Clara's reply for X (hold id) - nothing sent yet. |
| 63 | Handyman reply hold | maintenance/handyman-reply-hold.ts | "Handyman texts" setting (Gera 10-15) | POLICY | - | setting | b420c00ab3 2026-10-16 #20238 | building X tech phone unreadable; the module's own rule decides |
| 64 | Slack stale bump (not a block) | bake-gate.ts:2971 bumpStaleHolds | Pending 1h / 3h | POLICY | Slack bump | - | 2c51916ffa 2026-08-30 #6654 | bumped= in sweep line |
| # | Gate | file:line | What it checks | By | Fires / record | Release | Added | Count it by |
|---|---|---|---|---|---|---|---|---|
| 65 | Outbound-safety policies (block) | src/lib/domain/compliance/outbound-safety.ts; policies/*; conversation-manager.ts | promise-backing, booking-promise, availability-disclosure (counts/rosters, 10/22) | FACT (claim vs records) | parked draft | scripts/approve-held-draft.ts (SMS only) | be0b2268d6 2026-08-02 #5285 (ADR-0123); availability 4aacf771d1 2026-10-22 #21806 | guard anomaly_review_gate subReason outbound_policy:<id>; Outbound-safety policy held a reply for human review - nothing was sent; [guard:...] reply disclosed how much is open - removed |
| 66 | Fair housing (alert only now) | outbound-safety.ts ALERT_ONLY_POLICY_IDS; fair-housing.ts:1002 (classifier error fails closed) | FH classifier | FACT (content) | sends, records, Slack | - | c2558720f9 2026-10-19 #21159 | guard fair_housing_flag; Fair-housing check flagged a reply - recorded, the reply was sent, and the flag was posted to Slack |
| 67 | ONE send gate findings | conversation-manager.ts sendGateFinding | weekday_date, tour_time_this_turn, unbooked_tour, work_order_claim, unit_count, work_order_confirmation, hallucination_guard, unbacked_promise_in_retry | FACT | parked draft | approve-held-draft script | bc642b99fc 2026-10-25 #22296 | send_gate:<finding>; The send gate held a reply - nothing was sent; Work-order claim check held …; Work-order confirmation check held … |
| 68 | PII check | conversation-manager.ts (stripOutboundPII) | Sensitive number in reply | FACT | parked | script | bc642b99fc #22296 | pii:sensitive_number; PII check held a reply for human review - nothing was sent |
| 69 | No dashes | conversation-manager.ts | Em dash in reply | POLICY (style) | parked | script | 995c9f204d 2026-10-24 #22236 | guard dash_in_reply; No-dashes check held a reply for human review - nothing was sent |
| 70 | Runaway ceiling | conversation-manager.ts:7468 | Per-conversation turn ceiling (machine loops) | FACT | parked | script | 583fccb86b 2026-06-13 #2341 (guard); string 96e9da7df3 2026-07-26 #4606 | guard runaway_ceiling |
| 71 | Voice/video redacted reply | conversation-manager.ts | Reply redacted after redraft | FACT | no reply | - | - | noReplyAlert('redacted_after_redraft') |
| # | Gate | file:line | What it checks | By | Fires / record | Release | Added | Count it by |
|---|---|---|---|---|---|---|---|---|
| 72 | Guest-card intake gates | src/lib/domain/leasing/guest-card-reply-intake.ts | company not live, leasing off, created/home-added by Clara, before go-live, already applied, source not in list, already engaged | POLICY + FACT | verdict log | settings / none | bcaec8cd66 2026-09-18 #9633; sources 8edfcc5cb7 2026-10-15 #20019 | guest_card_company_not_live, guest_card_leasing_off, guest_card_created_by_clara, guest_card_home_added_by_clara, guest_card_arrived_before_go_live, guest_card_prospect_already_applied, guest_card_source_not_in_scope, guest_card_prospect_already_engaged |
| 73 | Guest-card eligibility (provenance) | guest-card-reply-eligibility.ts | backfill import, first seen before source enabled, stale at first sight, assigned to a person (staff-typed cards) | PROVENANCE | verdict log | none | 42f127ee35 2026-09-18 #9671 | guest_card_imported_as_backfill, guest_card_first_seen_before_lead_source_enabled, guest_card_stale_at_first_sight, guest_card_assigned_to_person |
| 74 | First-email consumer skips | src/lib/domain/leads/consumers/clara-first-email-consumer.ts | already claimed/handled, compose expired, no home, identity miss, staff person | FACT | verdict log | none | 7aed0e1092 2026-09-20 #10317 | clara_first_email_consumer_verdict {outcome, reason}: already_claimed, already_handled, compose_failed_expired, per_inquiry_event_without_home, person_identity_settled_miss, staff_person |
| 75 | First-email send door holds | clara-guest-card-first-email-send.ts:510, 519, 685, 841 | stale tour plan from old composer; safety pipeline blocked; another run owns send | PROVENANCE / FACT | hold | Review queue | 9dced10129 2026-09-19 #9721; claim 68dc2ca093 2026-10-01 #14711; stale plan 40dbd0fd86 2026-10-19 #21005 | First email carries a stale tour plan from the old composer - held, nothing sent; Outbound safety pipeline blocked the stamped draft - held, not sent; Another run owns this first-email send - not sending twice; Clara's first email touch held for review |
| 76 | Lead outbox park | src/lib/domain/leads/lead-outbox-park-alert.ts | attempts >= MAX_ATTEMPTS | FACT | parked row + one page per org per reason | manual re-drive | 89013248a4 2026-09-24 #11660 | alertOnParkedOutboxRows |
| 77 | Resident scope (email) | agents/clara/lib/email/process-inbound-message.ts; process-email-record.ts | Sender already lives here → no prospect reply / parked | FACT (role) | status SKIPPED | none | 47d5c0eec4 2026-08-08 #5573; open conv ca91896107 2026-10-05 #16478 | RESIDENT_SCOPE_DEMOTION_REASON (This is from someone who already lives here…); Parked for review at property X; logEmailReplyScopeSkip({gate:'queue', reason:'resident_sender'}) |
| 78 | Prospect cadence skips | prospect-outreach.ts:199; post-tour-chase.ts:1318; tour-workflow.ts:699, 730 | cooldown, claim pending, held-for-review, approval-refused, opted out; chase park 1h recheck / 14d budget | FACT+POLICY | skipped union | auto | 5a33c8acdf #2470; 09080563f0 2026-07-23 #4413 | cooldown, send-claim-pending, held-for-review, approval-refused; Email chase skipped - lead opted out (suppressed); CHASE_PARK_RECHECK_MS, CHASE_PARK_BUDGET_MS |
| # | Gate | file:line | What it checks | By | Fires / record | Release | Added | Count it by |
|---|---|---|---|---|---|---|---|---|
| 79 | Renewal fully suppressed / spine gates | renewal-auto-start.ts:329, 1031; renewal-decision-spine.ts:297; renewal-restart-gate.ts:107 | All channels suppressed; holdover lookup failure = suppressed; notice-to-vacate on file | FACT | skip | Clear suppression | 771483ffa3 2026-07-15 #3854; spine 91fad602f8 2026-07-22 #4374; ntv 8b7f5ba028 2026-07-14 #3796 | suppressed_all_channels; HOLDOVER suppression lookup failed … - treating as suppressed; HOLDOVER skip X: gate G blocked (reason); ntv_on_file |
| 80 | Supervised no per-tenant page | renewal-notify.ts:241 | Supervised renewals never page staff per tenant | POLICY | - | - | e6c4083d7b 2026-10-10 #18552 | supervised: no per-tenant page |
| 81 | MtM warning suppressed | renewal-orchestration/mtm-warning-send.ts:158 | suppression | FACT | skip | - | 533f4dbd8f 2026-10-02 #15083 | skipReason:'suppressed' |
| 82 | Collections chase suppression | src/lib/temporal/activities/collections-chase.ts | Suppressed / lookup failed (fail-closed transient) | FACT | chase receipt | - | 54f0269d2c 2026-07-29 #4848 | stopReason:'suppressed'; skipped:'suppression-unavailable'; Suppression lookup failed - skipping this dun touch (fail-closed, transient) |
| # | Gate | file:line | What it checks | By | Fires / record | Release | Added | Count it by |
|---|---|---|---|---|---|---|---|---|
| 83 | Inbound webhook dedupe + filters | src/app/api/twilio/webhook/route.ts:113, 138, 179, ~413, ~655; src/lib/platform/security/idempotency.ts:53 (fails OPEN) | Duplicate MessageSid; AppFolio OTP inbound; machine/robot/test sender; rate limit 30/60s → 429; unmapped number | FACT/PROVENANCE | no reply, no person minted | none | dedupe da50ad4955 2026-02-25; OTP d26b9e10f2 #1893; machine 583fccb86b 2026-06-13 #2341; rate b1c2a87386 2026-02-21 | Duplicate MessageSid ${messageSid}, skipping; Suppressed AppFolio OTP from ... - no envelope, no person mint; Suppressed machine/robot/test sender ... - no envelope, no person mint, no reply (runaway-loop guard); Unmapped destination Twilio number ...; DynamoDB error checking key …, allowing request through |
| 84 | Cadence send claim | src/lib/domain/leasing/outreach-send-claim.ts:104 | At-most-once per touch | FACT | claim row | - | 9fc424f0dc 2026-07-22 #4321 | outcomes claimed, already-sent, in-flight, claim-error |
| # | Gate | file:line | What it checks | By | Fires / record | Release | Added | Count it by |
|---|---|---|---|---|---|---|---|---|
| 85 | Outbound call door | src/lib/integrations/voice/initiate-outbound-call.ts:278+ | suppression/mute, test scope, module, Supervised, contact budget. No quiet-hours or not-live branch here (callers own quiet hours) | FACT+POLICY | result flags | as text door | mute 018161ebdf 2026-08-29 #6558; test 7ee23bc5f6 2026-10-03 #15849; supervised ebd05d4258 2026-10-01 #14995 | ${logLabel}: call refused - ${reason}; heldByTestScope, test_scope_${reason}; call held - module X is not enabled for this company (reason), heldByModule; call held - X is Supervised here, and Supervised places no outbound call (module_drafts); call refused fail-closed (no ceiling verdict), heldByContactBudget |
| 86 | Never transfer to a number that rings Clara | src/lib/domain/properties/clara-line-loop.ts:156, 175; handle-escalate-to-human.ts:424; escalation/human-handoff-route.ts:355; escalation/ring-list-route.ts:271; voice/personalization/route.ts:766-771 | Destination is a Clara line, prototype, or customer-forwarded number (WS 09-22) | FACT | candidate dropped | Configure a person destination | e5aef53e84 2026-09-11 #7921; a5ff64361f 2026-09-16 #8912; 67cb204665 2026-09-20 #10209 | transferredTo candidate skipped - X reaches Clara (source), so it would loop the caller back; kinds property-line, prototype, customer-forwarded; dropLoop |
| 87 | Settings refuse a looping catch-all | src/app/api/settings/call-routing/route.ts:221, 230 | Staff types a number that rings Clara | FACT | 400 | Enter another number | 06bebcb042 2026-09-28 #13381 | LOOPS_TO_CLARA |
| 88 | Muted caller, no destination | voice/personalization/route.ts:4246 | Muted person, no transfer target | POLICY | call refused | Configure destination | b6ad311807 2026-08-28 #6526 | Muted caller reached a property with NO transfer destination - call refused… |
| 89 | Hand-off bounce detector (alert, not a block) | src/lib/domain/voice/handoff-bounce.ts | Transfer came back / forwarded back / Clara called itself | FACT | bake alert | - | 14f8458a48 2026-10-21 #21636 | kinds forwarded_back, came_back, clara_called_itself; problem 'handoff_bounced'; alert voice_handoff_broken |
| 90 | Missed-call note gate | src/lib/domain/promises/guards/missed-call-note-gate.ts:196 | Hang-up notice lacks topic or identity | FACT | no note | - | f645c55bd9 2026-09-23 #11114 | missing_topic_or_identity |
| Path | Where | Releases |
|---|---|---|
PATCH /api/admin/escalation-holds/[id] | src/app/api/admin/escalation-holds/[id]/route.ts; reviewer gate reviewerDecisionBlockedReason / isChosenReviewerOfHold (1371464485 2026-10-20 #21341) | approveHeldText, rejectHeldText, approveEscalationHold, rejectEscalationHold (rows 52-60, 62, 75) |
| Hold pages | /admin/escalation-holds/[id] (ApproveRejectButtons); /held-messages/[id] (customer reviewer); /review operations page lists via GET /api/held-emails and links to the admin detail route (HeldEmails.tsx:12) | same PATCH |
| Slack thread reply | "approve N" / "reject N: why", relayed by Agent Smith to /api/slack/events → findHoldForSlackReply (c27b2e338f 2026-10-11 #18732) | escalation holds. Non-command replies go back to Smith. Slack reply in X thread Y names no hold - acting on nothing. /api/slack/interactions handles only shadow_rate: buttons, not approvals |
scripts/approve-held-draft.ts | operator script | agent-loop parked drafts (rows 65-70); SMS only, refuses this conversation has no recipient on file |
| Monthly renewal list approval | renewal batch | row 60 (reason:'list approved') |
| Gatekeeper rollout | releaseEscalationHoldByRollout bake-gate.ts:2304 (12ce383c1b 2026-10-23 #21900) | a fixed % of Supervised replies auto-released |
| Opt back in | START text (webhook:338) or staff | rows 3, 12, 23, 35, 40-42 |
| Settings | company/property settings | module (8, 15), go-live (10, 17), emailsEnabled (25), smsEnabled (33), lead sources (72), handyman texts (63) |
| Automatic | sweep cron escalation-hold-sweep (bumpStaleHolds → expireStaleHolds → sendDeferredApprovedHolds → sendDeferredCollectionsDuns); workflow sleeps | rows 45-53, 64 |
| # | Layer | file:line | What it changes (plain words) | Trigger | Effect | Who can see / undo | Added (commit, PR, why) | Still on main? | Log tag / record field that shows it fired |
|---|---|---|---|---|---|---|---|---|---|
| A1 | In-loop re-prompts (first-turn tool gate, availability-claim and tour-time-claim gates, typed-tool-call gate) | cm:12566 (FIRST_TURN_VIOLATION_REMINDER), cm:12758/12765 (AVAILABILITY_CLAIM_ / TOUR_TIME_CLAIM_VIOLATION_REMINDER), cm:12913 (TYPED_TOOL_CALL_REMINDER) | Throws away a draft written before a required lookup and asks the model again with a reminder and a forced tool call. The person never sees the first draft. | Pol + F | redraft (pre-final, new tool round) | trace; logs | ce3a18093d Apr 8 (turn-1 tool gate); ae6f8bd76e #10579 Sep 21 (Clara named units without a lookup); b8c55f166f #5257 Aug 2 (Clara typed her tool calls) | yes (named as left out by #22296) | logWarn [AvailabilityClaimGate] / [TourTimeClaimGate] / [TypedToolCall] ... retrying with forced tool_choice; step names availabilityclaim, tourtimeclaim |
| A2 | Intermediate-draft salvage | cm:14214 → decideIntermediateDraftSalvage agents/clara/lib/agent/intermediate-draft-drop-guard.ts:261 | When the final reply drops a URL or $ amount the pre-tool draft had, the old draft segments are merged back INTO the reply. Never on video. | F (dropped fact tokens) | append (merge) | trace (blocked:false, detail "merged N dropped fact(s)") | ce121fd357 #6278 Aug 25 (Camellia: prospect asked for the application, link sat in the undelivered draft) | yes (left out by #22296) | logWarn [IntermediateDraftDrop] ... merged the draft back in; step safety:intermediate-draft-drop |
| A3 | Empty-final fallback | cm:14256 → fallBackToEarlierTurnText agents/clara/lib/agent/empty-final-fallback.ts:62 | An empty final reply is replaced by text the model wrote earlier in the turn (before a tool call). Never on video. | F (empty final) | substitute | log only | a63c716246 #18626 Oct 10 (made it countable; behaviour older) | yes (left out by #22296) | logWarn event empty_final_fallback |
| A4 | Decision-why marker extraction | cm:14313 → agents/clara/lib/agent/safety/decision-why.ts:207 | Removes Clara's internal [[WHY: ...]] reason line from the reply. | F (marker present) | strip | reason stored on trace row; logCritical on malformed marker | b948f520a9 #6159 Aug 23 (Clara states her real reason) | yes | logCritical DecisionWhyLeak (only for malformed/mid-message) |
| A5 | Reasoning-preamble strip | cm:14338 → agents/clara/lib/agent/safety/reasoning-preamble.ts:102 | Drops leading paragraphs where the model talks about the turn in third person ("This is a walkaway signal..."). Customer channels only, not staff desk. Empty → nothing sent. | F | strip | trace (blocked:true) | 4320ccc0fb #21599 Oct 21 (WS SMS opened with the model's plan) | yes (internal-text strip, "PR D" not done) | logCritical ReasoningPreamble; step safety:reasoning-preamble-strip |
| A6 | History sent-time stamp strip | cm:14373 → src/lib/agent/turn-integrity/history-stamp-strip.ts:23 | Removes a copied "[Sent Thursday ...]" internal header line. | F | strip | trace | 51d0636693 #14874 Oct 1 (WS reply opened with the stamp) | yes (PR D) | logWarn Internal history stamp copied into a reply; step safety:history-stamp-strip |
| A7 | Typed-tool-call markup strip | cm:14407 → agents/clara/lib/agent/safety/tool-call-markup.ts:143 | Removes tool-call markup the model typed as text, keeps the sentence around it. Empty → nothing sent. | F | strip | trace | b8c55f166f #5257 Aug 2 | yes (PR D) | logCritical TypedToolCall; step safety:typed-tool-call-strip |
| A8 | Internal-prompt leak strip | cm:14459 → agents/clara/lib/agent/safety/internal-prompt-strip.ts (paragraph filter :70-75) | Drops any whole PARAGRAPH that quotes system context or names an internal tool. If every paragraph goes, SUBSTITUTES a canned line: "Hey there! I'm Clara. What can I help you with?" (SAFE_FALLBACK :37) or on video "Sorry, I lost my train of thought..." (:44). | F (denylist phrase) | strip; substitute when all dropped | trace; Sentry fatal | 0355aaf7cf #629 May 5 (internal prompt leak sentinel) | yes (PR D). Also runs on every writer retry (cm:14630) and redraft (cm:14593) | logCritical ContextLeak "Internal-prompt verbiage detected ... stripped before delivery"; step safety:internal-prompt-strip |
| A9 | Voice/video redraft (redraftReply) | cm:14568 | A text-only rewrite where the model IS SHOWN its own draft plus a correction. Since #22296 used only on voice and video (text/email go to A10). | Pol (each check) | redraft (draft shown) | trace "redrafted:" | ad65443706 #14353 Sep 30 (Clara confirmed an unbooked tour) | yes, voice/video only | trace detail prefix redrafted:; model trace name clara-<channel>-<label> |
| A10 | ONE writer retry per turn (sanctioned) | cm:14611 retryWriterThisTurn; note agents/clara/lib/agent/writer-call-capture.ts:97/108 | Not an edit: the writer's own call is re-sent with the finding appended; it does not see its draft. Every check below spends this one retry, then holds. | Pol | retry (allowed by Fede's Oct 23 decision 5) | trace "retried:" / held row | bc642b99fc #22296 Oct 25 | yes | model trace name clara-<channel>-<label>-check-retry; step details retried: |
| A11 | Weekday/date check | cm:14748 (enforceWeekdayDates) | Text/email: retry then hold. Voice/video: redraft via A9. | F | retry→hold (text/email); redraft (voice/video) | trace, held row weekday_date_guard | 9c5b35feee #12543 Sep 26 (wrong weekday reached a WS prospect) | yes; block-only on text/email since #22296 | step safety:weekday-date-guard; logCritical Weekday/date guard held a reply |
| A12 | Tour time not in this turn's read | cm:14835 (detectOffTurnTourTimes, gate agents/clara/lib/agent/tour-time-this-turn-gate.ts) | Text/email only; retry then hold. | F | retry→hold | trace; held row sub-reason tour_time:not_in_this_turn_read | ce62f94a74 #20338 Oct 16; widened #21497 Oct 20; pipeline slots #22036 (issue #22034) Oct 23 | yes, block-only | step safety:tour-time-this-turn; reportFallback surface clara-tour-time-this-turn |
| A13 | Unbooked tour confirmation | cm:14900 | Text/email/video (not voice): retry then hold. | F (booking tool failed) | retry→hold | trace; held row tour_booking:unbacked_confirmation | ad65443706 #14353 Sep 30 (Camellia "See you at 1" after both bookings failed) | yes, block-only | step safety:tour-booking-outcome |
| A14 | Work-order claim grounding (renewal threads) | cm:14966 → src/lib/agent/turn-integrity/work-order-claim.ts:145 | Text/email: retry then hold. VIDEO (channel != voice but not text/email): the claim sentences are still CUT and the rest ships (responseText = woClaim.text, cm:15010). | F | text/email: retry→hold; video: strip | trace blocked:false "stripped" on video | 2a9c46ed1b #14864 Oct 1 (renewal scoping eval: "A work order has been submitted" with no tool) | yes; strip branch survives for video | step safety:work-order-claim; logWarn Work-order claim with no filing this turn - stripped |
| A15 | Unit count / how-much-is-open | cm:15035 → src/lib/agent/turn-integrity/unit-count-stated.ts:218 | Text/email only (guard applies to those); retry then hold. | F | retry→hold | trace; held row availability:how_much_is_open | 4545dd190f #21145 Oct 19 (Fede "No counts at all"); extended #21806 Oct 22 | yes, block-only | step safety:unit-count-stated; log [guard:<UNIT_COUNT_GUARD_ID>] |
| A16 | Outbound safety policies (promise-backing, booking-promise, availability-disclosure; fair housing alert-only) | cm:15159; responseText = safety.replyText cm:15243; framework src/lib/domain/compliance/outbound-safety.ts (blockOnly :146, apply :461/:494) | Text/email: blockOnly: true → retry then hold. VOICE and VIDEO: blockOnly false, so a policy 'rewrite' verdict ships its replacement text (promise swapped for a deferral line, availability sentence removed, booking promise rewritten). | Pol | text/email: retry→hold; voice/video: rewrite/substitute | trace steps from the framework; MessageSafetyCheck[] stamped on the shipped row (outboundSafetyChecks) | ADR-0123 framework; promise-backing / booking-promise / availability-disclosure policies (rewrite verdicts at policies/promise-backing-policy.ts:312, booking-promise-policy.ts:98/113, availability-disclosure-policy.ts:63) | yes; rewrite mode survives on voice/video and on any caller that omits blockOnly | row field safetyChecks (verdict rewrite); logMessage Outbound-safety policy held a reply |
| A17 | Work-order confirmation sentence | cm:15260 → src/lib/agent/turn-integrity/work-order-confirmation.ts:127 | Text/email: retry then hold. VIDEO: a sentence "your repair request is in" is still APPENDED (cm:15318). | F (WO filed, reply silent on it) | text/email: retry→hold; video: append | trace added "<sentence>" | b6c304783c #16358 Oct 4 (real-path email E2E: repair filed, reply never said so) | yes; append survives on video | step safety:work-order-confirmation detail added "..." |
| A18 | PII redaction | cm:15336 (stripOutboundPII, defined cm:1289); voice/video branch cm:15385 → src/lib/domain/messaging/redacted-reply.ts:42 | Text/email: retry then hold (parked copy redacted). Voice/video: number replaced by [REDACTED], then a redraft, else withheld. | F | text/email: retry→hold; voice/video: redact + redraft | trace pii_strip | 6d3a69060c Mar 19 ("Audit AI Core" hardening); resolveRedactedReply 245dfabf15 #14348 Oct 1 | yes; voice/video path live (so issue #22319 "no caller left" is wrong: cm:15385 still calls it) | step checkName pii_strip detail PII found and stripped; noReply cause redacted_after_redraft |
| A19 | No dashes | cm:15410 (enforceNoDashes, src/lib/agent/turn-integrity/no-dashes.ts) | Text/email only: retry then hold. | F | retry→hold | trace; held row dash_in_reply | 995c9f204d #22236 Oct 24 (Fede: em dash is an AI tell) | yes, block-only | step safety:no-dashes |
| A20 | Hallucination guard canned line | cm:15584 safeEscalateMessage; GUARD_NO_HANDOFF_REPLY cm:6616 used at cm:15643/15656/15772/15879 | Text/email: retry, then hold (nothing sent, cm:15917-15920). VOICE/VIDEO: reply REPLACED by "Thanks, I'm looping in your property manager..." or "I'm not able to help with this one here, please contact your property management office...". | F (unbacked $/tour time/action) | text/email: retry→hold; voice/video: substitute | held row hallucination_guard (msg_blocked) | a786a23caf #485 Apr 27 (renewal legal-risk guard); GUARD_NO_HANDOFF_REPLY 604d7d973f #13649 Sep 29 | yes; substitution survives on voice/video | logCritical HALLUCINATION_GUARD_BLOCKED_MESSAGE; step safety:hallucination-guard |
| A21 | Send gate | cm:15935 sendGateFinding (cm:14682) | Block only: re-runs every check on the final text, holds on any finding. Not a rewrite. | F/Pol | block | trace | bc642b99fc #22296 | yes | step safety:send-gate, sub-reason send_gate:<finding> |
| A22 | Anomaly review gate holding line | cm:16021 → src/lib/agent/turn-integrity/anomaly-review-gate.ts:296 (holding line only when channel === 'voice', :308) | Text/email: true hold, nothing sent. Voice/video: reply REPLACED by a fact-free holding line ("let me check on that"). | F (fatal tool failure / timeout) | substitute (voice/video) / block (text/email) | trace guardAction: edited vs withheld | e1017d39d6 #4061 Jul 17 (ADR-0101 turn integrity) | yes; substitution on voice/video | logCritical ANOMALY_REVIEW_GATE_HELD_MESSAGE; step safety:anomaly-review-gate guardAction |
| A23 | Turnover fabrication guard (staff/PM) | cm:16219 (detectsTurnoverWorkClaim) | Replaces Clara's whole reply to a property manager with one of three fixed honest lines ("I wasn't able to confirm the work plan... Reply yes"). | F (claims work, tool did not run) | substitute | held row turnover_fabrication_guard; Sentry | d633e43e2e era; claim detector a4b2baa110 #4883 Jul 29 (2026-06-11: Clara told the PM "5 work orders created" that never existed) | yes (left out by #22296: "staff-facing turnover guard") | logCritical TURNOVER_FABRICATION_GUARD_BLOCKED_MESSAGE; step safety:turnover-fabrication-guard |
| A24 | Turnover async-WO honesty guard (staff/PM) | cm:16253 | Replaces the reply with "On it, lining up the work now. You'll see the work orders in AppFolio shortly." | F (completion framing) | substitute | held row turnover_async_honesty_guard | 263bb4e76d #4947 Jul 30 (Gate Run 1: "5 work orders created" while still minting) | yes | logCritical TURNOVER_ASYNC_HONESTY_GUARD_REWROTE_MESSAGE; step safety:turnover-async-honesty-guard |
| A25 | Turnover edit-honesty guard (staff/PM) | cm:16401 → agents/clara/lib/agent/turnover-edit-honesty-guard.ts:466 | Replaces a reply that claims a charge/plan edit with a fixed decline message. | F | substitute | held row turnover_edit_honesty_guard | d633e43e2e #2431 Jun 16 | yes | logCritical TURNOVER_EDIT_HONESTY_GUARD_BLOCKED_MESSAGE; step safety:turnover-edit-honesty-guard |
| A26 | Staff-desk ref echo strip + write-claim rewrite (staff) | cm:16412 stripDeskRefEcho (agents/clara/lib/agent/desk-history-refs.ts:52); cm:16425 evaluateDeskWriteClaims (desk-write-claims.ts:182) | Removes echoed internal record ids from a staff-desk text; rewrites a claim of a write that did not land. | F | strip; rewrite | trace; logWarn | 3320d4a655 #20602 Oct 17 | yes | logWarn DESK_WRITE_CLAIM_UNBACKED_LOG |
| A27 | Same-turn escalation: unreached-forward promise | cm:16661 → rewriteUnreachedForwardPromise cm:3064 | When a forward to the team reached nobody, each "the team will follow up" sentence is replaced by a fixed "unconfirmed" line; rest ships. | P (forward undelivered) + F | rewrite (sentence substitute) | log only | 163c60cc3f #21395 Oct 20 (issue #21386: escalating-turn protections stop depending on the template path) | yes (left out by #22296: "same-turn escalation strips") | logInfo Agent:escalated-gate reason=same_turn_escalation_undelivered_promise |
| A28 | Same-turn escalation: first-touch promise-clause strip | cm:16726 → stripAllHandoffPromiseClauses cm:2693 | On a guest-card first touch where a forward fired, every hand-off promise clause is cut and the rest ships. | P + F | strip | log only | b72611e4f6 #11980 Sep 24 (2026-09-24 leak fix, #11694 part 2) | yes | logInfo reason=same_turn_escalation_first_touch_strip |
| A29 | Same-turn escalation: template replacement | cm:16757 → buildBlockedDraftReplacement cm:2600 / buildEscalatedAckText cm:1453 | When a forward fired and the draft has no releasable substance, the whole reply is REPLACED by a templated acknowledgment (RULE 7.7 deferral or escalated_thread_ack). | P (forward fired) + Pol | substitute | outboundKind = ESCALATED_ACK_KIND on the row | ac928e64e4 #7119 Sep 5 (autonomous hand-off mode); verdict logic e0c4e6ff40 #5640 Aug 10 | yes | logInfo reason=same_turn_escalation; ... replaced by the templated acknowledgment; row kind escalated_thread_ack |
| A30 | Team-inbox silence | cm:16641 / 16829 (silenceTeamInboxDraft) | Withholds a bare hand-off ack on a mailbox the team reads (Decision 9). Block, not edit. | P + F | block | held row team_inbox_silence | daf0e67de5 #5985 Aug 20 | yes | step safety:team-inbox-silence, guardAction: withheld |
| A31 | Opt-out withhold | cm:17106 (withholdsReplyAfterOptOut, ./opted-out-reply) | Sends nothing to a number that opted out. Block. | Pol | block | log | a300105ff5 #21302 Oct 20 | yes | (block) |
| A32 | Video spoken releaser | cm:14193 (spokenReleaser.sayFinal, SpokenReleaser per-sentence judges) | On live video, the same strips (A4, A6, A7) then per-sentence judges decide what is said; what was spoken replaces the reply text. | P (video) + F | strip / drop sentences | spoken events log | f2f23574e9 #15679 Oct 2 | yes | logSpokenEvents() |
| # | Layer | file:line | Receives | What changes (plain words) | Trigger | Effect | Redraft/retry | Who can see | Added (commit, PR, why) | Touched by a no-rewrite PR? | Signal in production |
|---|---|---|---|---|---|---|---|---|---|---|---|
| B1 | Maintenance relay composeViaHaiku dash fix, then template | src/lib/temporal/activities/maintenance-comms.ts:440 (branch 462-467; fallbacks :600, :764, :945) | Resident (tech's question/status), tech (resident's answer) | A dash gets one redraft. Still a dash, empty or an error → a fixed template goes instead ("Quick question from the maintenance team about your work order: ...", "Resident says: ...") | Pol | redraft → substitute | redraft (draft shown) | nobody (bare catch, no log) | 2f417de900 #2106 Jun 10. Dash redraft added by 995c9f204d #22236 Oct 24 | #22236 ADDED it | none |
| B2 | Staff-answer relay rephrase, then verbatim relay | src/lib/domain/escalation/reply-release.ts:2563 (fallback :2581-2588; deterministic body :2524) | Resident / prospect | A model re-says a staff answer in Clara's voice. A failed fair-housing or number check, or a timeout → swapped for the scrubbed verbatim relay | Pol + P | substitute | fresh writer | logWarn escalation:reply-release "Own-words rephrase failed ... falling back" | fa02a692ba #6193 Aug 24; only path since #16349 | no | that log |
| B3 | Staff-answer scrub cleanStaffAnswer / stripStaffSignOff / stripMailClientTrailer | src/lib/domain/escalation/decision-request.ts:1541, :1382, :1554 (frames :1529, :1654) | Resident | Cuts the staff member's sign-off, mail trailer and "tell them..." lead-in, then wraps the rest in a fixed frame. Staff words, not Clara's | Pol | strip + frame | n/a | nobody | a6624d7d8f #5841 Aug 18 (staff sign-offs reached residents) | no | none |
| B4 | Booking-promise regenerate, then sentence rewrite | src/lib/domain/compliance/policies/booking-promise-policy.ts:85, :106; booking-promise-regenerate.ts:75 | Prospect, voice/video only | Rewrites a sentence claiming a tour is booked before it is | Pol | redraft → rewrite | redraft | logWarn BookingPromiseGuard; trace safety:booking-promise guardAction:edited | 7bf664079a #9635 Sep 18 | #22296 kept it for voice/video | log + trace |
| B5 | Promise-backing rewriteAutonomousPromise | promise-backing-policy.ts:316 (fn :480) | voice/video only | Swaps an unbacked "someone will follow up" for the approved deferral line | Pol | rewrite | none | trace safety:promise-backing edited | 7d46c03e1b #12370 Sep 25 | #22296 kept it for voice/video | trace |
| B6 | Availability-disclosure strip | availability-disclosure-policy.ts:51-66 | voice/video only | Removes counts and rosters of open homes | Pol | strip | none | logWarn outbound-safety [guard:availability-disclosure] ... removed | 4aacf771d1 #21806 Oct 22 | holds instead when blockOnly | that log |
| B7 | Policy runner blank-replacement fallback | src/lib/domain/compliance/outbound-safety.ts:523, constant :82 | voice/video only | A blank replacement becomes "Thanks for reaching out - let me check on that and get back to you shortly." (has an em dash and an unbacked promise) | Pol | substitute | n/a | logError OutboundSafety "returned a blank replacementText" | ab33619614 #5921 Aug 19 | blockOnly added by #22249 | that log |
| B8 | resolveRedactedReply (= A18 voice/video) | cm:15385; src/lib/domain/messaging/redacted-reply.ts:42 | voice/video | PII → [REDACTED] → one redraft, else withheld | F | strip + redraft | redraft | noReplyAlert('redacted_after_redraft') only when withheld | 245dfabf15 #14348 Oct 1 (prospect got zoom.us/j/[REDACTED]) | #22321 wrongly called it dead; issue #22319 open | withheld only |
| B9 | Crash fallback buildFailOpenFallbackReply | agents/clara/lib/messaging/inbound-router.ts:1967; src/lib/domain/messaging/fail-open-fallback-reply.ts:134 (line :39) | Tenant/prospect after a mid-reply crash | Fixed lines per completed action ("Got it, your repair request is in. Someone will follow up on timing.") | F (crash) | substitute (no draft) | n/a | logError inbound-router + alertNoReplySent cause crash_mid_reply | 9edc671900 #10419 Sep 24 | no | that alert |
| B10 | Cadence minimalFallback | src/lib/domain/leasing/generate-followup-message.ts:411 (used :747, :779) | Prospect (follow-ups, re-engagement) | A writer exception or empty first write → a fixed template | F (failure) | substitute | no retry on these paths | logWarn GenerateFollowUpMessage "... using template" (exception only; empty write is silent) | 2ccd10d347 #2509 Jun 19 | #22306 kept it | that log |
| B11 | Cadence email format cuts: Subject: line, stripEmailSignoff | generate-followup-message.ts:741, :432/:742 | Prospect (email) | Cuts a leading "Subject:" line and a trailing Clara sign-off and closer | F | strip | n/a | nobody | #2509; 78f2ea7a7f #2515 | #22306 kept them on purpose ("structure, not words; PR D decides") | none |
| B12 | Dispatch summary mint | src/lib/domain/maintenance/dispatch-summary.ts:268; hard cut clean() :135 | Vendor/handyman page, PMS-created WO tenant notice | A model rewrites the work-order description into dispatch prose, hard-cut at 240 chars | Pol | rewrite + truncate | fresh writer | fields dispatchSummary, dispatchSummarizedAt, dispatchTitle; logInfo DispatchSummary | 462ea30d13 #6792 (CAM-2592: a tenant's apology paragraph paged to a tech) | no | fields + log |
| B13 | Work-order judge ("Agent Smith") applyAdjustmentsToWO | src/lib/maintenance/work-orders/judge.ts:452 (write :471) | AppFolio work order → vendors, staff | A model polishes the title, description, priority and category Clara committed | Pol | rewrite | reviewer model | WorkOrderJudgment record adjustments (judgment-record.ts:124) | 30fa124f01 #1230 May 21 | no | adjustments |
| B14 | Post-call note narration swap stripNarration / NARRATION_REWRITES | src/lib/integrations/voice/postcall-call-note.ts:95, :162, applied :183 | AppFolio guest-card note | "The agent, Clara, informed the user" → "Clara informed the caller", etc. Fires on 301 of 310 summaries (#22321) | Pol | substitute words | none | nobody | e3e921453f #10892 Sep 22 | #22321 kept it; "Decision for Fede" | none |
| B15 | enforceGuestCardNoteShape | src/lib/domain/pms/guest-card-note-shape.ts:156 (stamp cut :188; fallback :182/:193/:214) | AppFolio note | An empty or out-of-shape note → "Clara touched this lead; no outcome recorded for this contact."; legacy "M/D - " stamps cut | Pol | substitute + strip | n/a | fallback: logWarn GuestCardNoteShape; stamp cut: nobody | 3586d8c6d1 #10668 | no | that log |
| B16 | Card-note writer rejected → record sentence | src/lib/domain/leasing/card-notes/compose-card-note-live.ts:90/:99 | AppFolio note | A model note that fails the check becomes the row's fixed sentence | P | substitute | none | logWarn CardNoteWriter "posting the record sentence instead of a model note" | 9422f0f080 #21985 Oct 23 | post-rule | that log |
| B17 | cleanCapturedReason (stripSummaryFiller + stripRestatedCallback) | src/lib/tools/handlers/handle-capture-unknown-caller-note.ts:253 (callers 369, 905, 1135, 1364); autonomous-backstop-staff-sms.ts:77 | Staff email/SMS "Message taken from..." | Cuts filler and "call me back" sentences from Clara's tool argument, and the repeated phone clause | Pol | strip | none | nobody | 68de93c276 #10752 Sep 22 | #22321 named it out of scope | none |
| B18 | Missed-call note stripRestatedCallback + tidyIssue/trimToClause | src/lib/domain/promises/missed-call-note.ts:820, :654, :626 | Staff email | The number clause is cut; the subject reason is cut to 8 words with an ellipsis | Pol | strip + truncate | none | nobody | 6542417b38 #6813 | no | none |
| B19 | truncateToTwoSentences | src/lib/domain/prospects/guest-card-core-facts.ts:122 (via calendar/sync-tour.ts:197) | Staff tour calendar invite | Clara's aiNotes are cut to 2 sentences | F (length) | truncate | none | nobody | 08802d5bd2 #8120 | no | none |
| B20 | Renewal held-text re-render | src/lib/domain/leasing/renewal-batch/release-render.ts:155 (:178); held-text.ts:965 | Resident (held renewal text/email) | A template re-render with the reviewer's numbers is saved over the held words (template words, not model words) | F | re-render | n/a | logInfo RenewalBatch "re-rendered from the decided offer"; Slack thread "It now reads"; hold.sms.body overwritten | c433c061dd #18423; d6baa3d77b #22064 Oct 23 | n/a | log + thread |
| B21 | Knowledge writer scrubDraftForKnowledge | src/lib/domain/escalation/teaching-capture.ts:927 | Knowledge base → later replies | Drops sentences with private contacts from a distilled fact | Pol | strip | none | leftOutPrivate: true | d5cc0b263a #17645 | no | leftOutPrivate |
| B22 | Neighborhood notes fenceNeighborhoodLines/tidy | src/lib/domain/leasing/neighborhood-notes.ts:223, :208 | Listing card, spoken on calls | Strips citations and bullets, drops long or steering lines, caps at 5 | Pol | strip + cap | one thin-note retry | FenceResult.dropped | deebad1ec9 #8091 | no | dropped[] |
| B23 | Reviewer Slack feedback redraft | src/lib/domain/escalation/review-redraft.ts:98 → held-draft-redraft.ts:59 | Prospect (held email) | A person's Slack feedback → a true redraft ("CURRENT DRAFT" shown). Overwrites the hold in place, so the prior version is not kept | P (human-asked) | redraft | redraft | logs escalation:review-redraft; Slack | 330cc0d61c #9948 | #22321 kept it ("a person asking, not a check") | log |
| B24 | Team-notice subject fallback | src/lib/team-notify/summarize.ts:442 | Customer staff | After 3 failed tries on the subject phrase, the engine's fixed subject is used | F | substitute (subject) | 3 fresh retries | summaryCheck:'retried' | d2c8d2f627 #16229 | no | that field |
| B25 | Tour-arrival translation | src/lib/i18n/translate.ts:92 (tour.ts:1277, :1327; weekend-tour-checkin.ts:306) | Prospect | Translates staff-written arrival text; on failure sends the English | P | substitute | n/a | #alerts surface translate | dc6c65d642 #4273 | no | alert |
| B26 | stripTtsDirectives | agents/clara/lib/messaging/adapters/elevenlabs.ts:249/:498 | Stored call transcript (record only) | Cuts [laughs]-style tags | F | strip | n/a | nobody | (record only, not delivery) | no | none |
| # | Layer | file:line | What changes | Trigger | Effect | Cosmetic or content | Who can see / stored row | Added (PR, why) | Still on main? | Signal |
|---|---|---|---|---|---|---|---|---|---|---|
| C1 | Template greeting prepend (plain text) - issue #22299 item 9 | agents/clara/lib/email/clara-reply-template.ts:223-238 (prepend :238) | Adds "Hi {first}," / "Hola {first}," above a body with no greeting | F + Pol | append | CONTENT | nothing logged | d7a899f66b Mar 20 (threaded HTML email); name guard #12196 | yes (#22299 OPEN) | none (renderedTemplate='leasing' only) |
| C2 | renderGreetingParagraphHtml (HTML twin) | clara-reply-template.ts:326-336, called :346 | The same greeting as its own <p> | F + Pol | append | CONTENT | nothing | original; made conditional #393 Apr 24 ("Hi Sydneylavigne727,") | yes | none |
| C3 | buildClaraInternalReplyText | clara-reply-template.ts:414-421 | Staff emails: "Hi {first}," + "Property: {name}" + "Clara" sign-off | P (staff recipient) | append | CONTENT | nothing | #6003 Aug 20; property line #10677 | yes | renderedTemplate='internal_plain' |
| C4 | normalizeGreetingPlacement | clara-reply-template.ts:142-157 (limit :91) | Moves a greeting found in paragraph 2 or 3 to the top | F | reorder | CONTENT (order) | nothing | #11371 Sep 23 (WS "Hi Alexis... Hi Alexis") | yes | none |
| C5 | Shell language pick resolveShellLanguage/SHELL_COPY | clara-reply-template.ts:56, :72-80 | Picks EN/ES for the template's greeting and footer from the body's first word | F | substitute (shell) | CONTENT (shell only) | nothing | #17500 Oct 7 ("Hi" on top of "Hola") | yes | none |
| C6 | Footer, signature block, preheader | buildClaraReplyHtml footer :372, claraSignatureFooter :298-323, preheader :352 | "Have more questions? Just reply to this email...", a Clara/company/address/phone block, a hidden "Reply from {property}" line. HTML leg only | Pol | append | CONTENT (fixed words) | nothing | d7a899f66b Mar 20; 0a19aef94c Apr 14; branding #10480 | yes | renderedTemplate='leasing' |
| C7 | stripMarkdown | agents/clara/lib/email/strip-markdown.ts:28 (called template :230, :415) | Removes **, #, code fences, backticks, ---; squeezes blank lines | F | strip | content-lite (marks only) | nothing | #5057 Jul 30 (closes #4976: markdown reached prospects) | yes | none |
| C8 | renderBodyHtml | clara-reply-template.ts:270-295 | HTML-escape, \n→<br>, paragraphs, bold → <strong> | F | encode | COSMETIC | nothing | #5057 | yes | none |
| C9 | splitSmsBody | src/lib/integrations/twilio/sms-segments.ts:71-87 (sendSms client.ts:1356) | Splits a long text into several texts (5 segments each, 1600 cap); cuts at sentence, then word, then mid-word; trims spaces at cuts | F | split | COSMETIC (words kept) | row = full text, providerId = sids[0] | #19424 Oct 14 (a 701-char Spanish reply refused, Twilio 30019) | yes | status callback ?op=&part=N (client.ts:122) |
| C10 | Email-bounce text fallback flattenForSms | src/lib/.../ndr-bounce.ts:337-347, body :533-537 | Clara's bounced email: HTML removed, cut to about 500 chars at a sentence, wrapped "Hi {first}, it's Clara with {property}. I replied by email but it couldn't be delivered (...), so I'm texting instead." + " Reply STOP to opt out." on the first text | P + F + Pol | strip + truncate + append | CONTENT | row is post-transform, kind bounce_fallback_sms | #6010 Aug 20 (a Zillow lead's full Gmail inbox) | yes | Message.kind='bounce_fallback_sms', log email-ndr-bounce |
| C11 | stripInternalPrompt at first-email prep and send | agents/clara/lib/agent/outbound-safety-pipeline.ts:227-230; taken at clara-guest-card-engagement-prep.ts:681 and clara-guest-card-first-email-send.ts:693 | Removes a leaked prompt or tool paragraph from the first email. At prep a difference first triggers a writer retry (prep.ts:622); at send it is a silent strip | F | strip | CONTENT | nothing logged at this site (matches discarded); row draftBody is post-strip | #9721 Sep 19 | yes | none |
| C12 | emailChannelWording + reminder email fallback | email-channel-wording.ts:27; tour-reminder.ts:446-448; tour.ts:504; staff-booking-confirm-prospect.ts:229, :389 | "text me here" → "reply to this email" on template text reused in email; adds a "best phone number" line | P | substitute + append | CONTENT (template text, not the writer's) | row post-substitution | #21591 Oct 21 | yes | none |
| C13 | buildReplySubject | clara-reply-template.ts:39-42 | Subject → "Re: {original}" | Pol | rewrite (subject) | COSMETIC | on row | d7a899f66b | yes | none |
| C14 | Staff escalation HTML linkifyPhones | decision-request.ts:1297-1319 | Escaping, <br>, phones become tel: links | F | encode | COSMETIC | nothing | #10933 Sep 22 | yes | none |
| C15 | Graph/SendGrid body choice | inbox-client.ts:1795, :2068, :2194; sendgrid client.ts:349-352 | Graph sends HTML if present, else text; SendGrid sends both | n/a | encode | COSMETIC | n/a | n/a | yes | n/a |
Two calls are open (A2, A3); A1 was decided out on Oct 25. Proposed, pending review
Fede, Oct 25 night (Clara Refactor session): no readiness floors yet. Each customer stays exactly where it is today; a module moves at a customer only on Fede's explicit per-customer word; Situs stays supervised; no automatic promotion logic. Floors may be proposed again once the bench has enough runs to know the right percent, and Fede sets the number. His words: "this is dumb, i dont want this yet until we significantly fine tune it", "they live wherever they are today and what i decide manually", "floors can be determined later we are nowhere close enough to even know what the right percent is".
Cost per inquiry, estimate (Oct 27 night; measured numbers come from the baseline runner): official prices, Opus 5.5 = $4 in / $20 out / $0.20 cached read per million tokens; Sonnet 5.5 = exactly half on every line. Today's prompt is about 105k tokens per turn; the decomposed one about 7 to 19k; an inquiry is about 5 turns. Per inquiry: today on Sonnet about 30 cents; decomposed on Sonnet 5.5 about 8 cents; decomposed on Opus 5.5 about 16 cents. The decomposition saves more than the model choice costs.
The measurements behind the sections above, in one place.
human-notification-lane.ts still carriesRead from the code on main at commit 47c1188a19: almost every piece exists; one decision (should Clara speak here?) is made in many places.
| Camellia (JP&Co) | Western Slope | Situs | |
|---|---|---|---|
| Conversations graded | 448 | 218 | 2 |
| Needed a person | 326 | 172 | — |
| Failed: never asked for help (incl. failed to transfer or answer) + silent drop | 121 of 326, 37% (36 + 85) | 51 (14 + 37) | — |
| Asked for help when she should not have | 4 | 2 | — |
| Held drafts expired unsent | — | 60 | 35 of 35 |
| Transfers with no human leg | 70 | 61 | — |
| Main-line loop | 21 legs in 12 calls, Oct 13–19 | guarded: Clara never transfers to the forwarded line; since Oct 22 a bounce alerts and counts as no answer | — |
| Measure (first reply) | Today | New prompt |
|---|---|---|
| Prompt tokens | 109,658 (Oct 22 trace) | ~7,440 median |
| Topics with contradicting rules | 8 (length alone has 6 rules) | build fails on any second rule |
| Held, 67 recent cards | 6 | 0 (Opus 5.5) |
| Emails rewritten after writing | 51 of 78 | 0 |
| Time per turn | 75 s | 7.4 s (Opus 5.5) |
| Cost per card, list price | $0.29 | $0.016 Opus · $0.008 Sonnet 5.5 |
| Writer bill, 30 days | ~$298 (Sonnet 5) | ~$65–125 Opus 5.5 (estimate) |
| Measure | Value | Source |
|---|---|---|
| Turns graded after the fact | 3,509 | Sep 8 to Oct 25, 702 conversations, 87% voice |
| Flagged red | 362 | 174 from the vendored engine, 188 from two app checks; precision unmeasured |
| Human labels on any flag | 0 | none of 85 thumbs since Aug 21 froze a per-turn row |
| CI catches in 60 days | 0 | 0 failures in 400 runs of the required gate |
| Western Slope and Situs Group | Count |
|---|---|
| Tenant texts and emails | 475 |
| Answered by nobody | 274 |
| Answered by staff | 189 |
| Answered by Clara | 1 |
| Bench | Result |
|---|---|
| D3: office-conversation rule, reply or silent | 22 of 23 of Fede's graded examples (in-sample); 0 replies on staff-run threads, 0 invented facts; routine replies 12 of 50 |
| G2: 261 repair episodes, two outcomes | Invented a fact in 1 of 261 (was 13); filed 8 of 42 where the office filed, all right |
| Triage: 1,994 real texts | 45% need no reply; of the rest 23% Clara today, 44% need a person, 33% need more tools |
Opus 5.5 beat today's Sonnet 5 on the first prospect email, on a replay of every real guest card we could rebuild. On Fede's go (Oct 31, 00:25 MDT: first email on Opus, autonomous, all customers) the swap PR #24036 merged and went live for every customer at 05:11 UTC. Texts and every later turn stay on Sonnet.
| Before | After | |
|---|---|---|
| Model on the first prospect email | Sonnet 5 (trace step "claude-email-initial": claude-sonnet-5) | Opus 5.5 (trace_aa2fc390-19f5-4621-b31b-c80622334432, step "claude-email-initial": model claude-opus-5-5, model path firstEmail) |
| Time from inquiry to reply in the renter's inbox | 75 seconds (inquiry 05:19:25, reply delivered 05:20:40) | |
| First-email bar | No em dashes, tour times offered from the calendar, listing facts match unit 201, special quoted, rent question answered, no "check with the team" |
A Zillow-style inquiry for a made-up renter (test mailbox, no real person) went through the real intake at The Willows: guest card 365, conversation 35a617cb-d10a-42d4-8669-a8d1b777ae45, reply msg_c98cc919-1122-409e-8449-f66b2187ca97. Small things, not blocking: the opening sentence joins two thoughts with a comma; the delivered subject carries "Re:" while the record stores the plain subject.

Follow-up turns stay on Sonnet. 53 real follow-up turns since the recorder started: Opus better 26, tie 8, worse 19 after the shared tour-reply rule (PR #24064, merged: who meets the renter only if asked, tour times never from office hours). Both of Fede's "Wrong" turns are clean on both models. A narrow win on a thin set; the swap waits for Fede's go.
| Sonnet 5 (today) | Opus 5.5 | |
|---|---|---|
| Head to head, same card | Opus better 161 (45%), tie 139 (39%), worse 56 (16%) | |
| Cards with any never-do | 40 | 32 |
| Cards tripping one of Fede's checks | 46 | 31 |
| Em dashes | 9 | 0 |
| Home named without its special | 17 | 13 |
| Formal date instead of today/tomorrow | 18 | 10 |
| Unprompted vacancy count or list of homes | 5 | 1 |
| "Check with the team" | 3 | 1 |
| Tour time not on the calendar | 8 | 6 |
| Customer | Opus better | Tie | Opus worse | Cards |
|---|---|---|---|---|
| Western Slope | 58 | 24 | 20 | 102 |
| Situs | 88 | 109 | 36 | 233 |
| Camellia | 15 | 6 | 0 | 21 |
Where the renter wrote something themselves (182 cards): Opus better 105, tie 45, worse 32.
Where Opus is worse. Two real misses, both at Situs. On a home that is gone, Opus says it will get it confirmed the home is still open. On a renter who offered a double deposit, Opus carried it to the tour though the records say one deposit per home. Two more hits were false alarms: a correctly worked out effective rent, and passing on what a staffer had already told an applicant.
What the judges are worth. The comparison judge matches Fede on 5 of his 7 rated cards. The code checks reproduce his 6 of 6 "Wrong" calls and caught 8 of 8 planted defects with 0 false hits. The corpus judge's "do not send" rate (about two thirds on both models) is not used, because it never sees the writer's input.
How many cards made it in. 856 real lead records; 430 rebuildable; 72 dropped with no email on file, 1 with no time anchor; 1 to the bad-data bucket; 356 compared. Not possible: 426 records with no time anchor, and about 2,640 historical report rows with no PropFlow record (Situs 1,572, Western Slope about 1,068). Tour slots come from today's calendar for both models. Lampliter input still names the unposted Unit A for both.
Samantha C. (Western Slope, Oct 30 evening). Rebuilt after the population was pulled; Opus better. The pet fees ($300 deposit, $35 a month per pet) are in Western Slope's knowledge base, not invented. The sentence "the property team will follow up with you about the eviction" is ordered word for word by the current prompt for eviction asks, and both models write it; keeping it is a prompt decision. Neither model mentions her $2,000 budget or 2-bed interest.
Review list of 104 cards for Fede, most important first (the first 25 matter), held in the run folder ~/.claude/guest-card-corpus/runs/2026-10-30-opus-swap-qa/.
The 40-reply runaway limit muted a thread for good with no card. Now only a fast unbroken run counts, a 30-minute pause ends it, a new text 30 minutes after her last reply restarts her, held drafts never count, and the first stop posts a card through the shared #alerts helper as its own alert type (test properties get it too).
Before. On Oct 28 and Oct 30 the Willows tester thread hit the 40-reply limit and Clara went silent for good with no card. Replaying that real thread through the old rule trips both times; the new rule counts 1 and does not trip.
Willows proof, Oct 30 night (tester number to the Willows line only, our own numbers):
| Step | What happened | Proof |
|---|---|---|
| 1 | A real fast run: 40 texts about one a minute, 40 replies delivered (02:07 to 02:41 UTC) | Twilio ids in the PR comment on #23927 |
| 2 | The 41st text got no reply, so real loops still trip | text SM44bfb24e |
| 3 | One internal card posted to #alerts for the Willows | Slack card |
| 4 | A new text 32 minutes later was answered, with no reset | Twilio SMaa31364179b2ec3b8392c027a349287d, row msg_a6fa4a3d-2b68-4fd7-8375-dc51c8b26793 |

Follow-up suggestions: issue #23940.
A correct tour reply ("tours run 10 AM to 4 PM") was held by the tour-time check because the pipeline path did not pass the tour window, with no way out.
Part 1. The check gets the window the reply used. Willows proof: the tester texted "Could I come tomorrow at 10 PM?" and Clara's reply arrived (Twilio SMc9150f7f96d6b5374ee267b9004ab8a8). Caveat: that reply came through Clara's own calendar lookup, not the changed pipeline path.
Part 2. Every held reply draft gets one internal review row with Approve and Reject, admin only; client staff never see it. Production proof at Willows: the row exists, one draft makes one row, and the detail page shows the draft with both buttons. Approve on a stale draft was refused with a 409 because a later reply had already reached the tester (nothing sent). Reject worked.

Follow-ups: #24031 (test context), #24032 (holds list shows only newest 200, search within them), #23992, #23933, #23942.
| Who | What staff were told | What really happened |
|---|---|---|
| Deyci S. | "Not sent" | Sent 14 s after approval (inbox busy, queued, then sent; lane already fixed by #23554) |
| Yaramin R. | "Not sent" | Sent 13 min later (AppFolio login was cold, treated as final, a second try sent it anyway; issue #24022) |
| "Michael" (staff test) | "Not sent" | Correctly stopped, a teammate had texted the lead 4 minutes earlier |
| Estrella P. | "send failed, retrying, nothing has gone out yet" twice, then "Sent" at 10:45 | Sent 22 s after approval, one copy; only the status was 39 minutes late |
No renter was left without a text. The bug is a false "Not sent" to staff when the AppFolio inbox queued the text. The fix counts a queued reply as on its way and posts "Sent" when it goes. Issue #24033 for the one suggestion.
Willows proof in production (Oct 31). A held Clara reply to our tester was approved at 9:03 PM the night before, parked by quiet hours, and sent by the 8:00 AM sweep with no false failure line.
| Step | What happened | Proof |
|---|---|---|
| 1 | Held reply approved at 9:03 PM MT, correctly parked for quiet hours | hold eschold_adeefa26-4bac-4c22-9102-a6425d0f95a8, approved, awaiting the send window |
| 2 | Sent at 8:00:50 AM MT, one text, delivered | Twilio SMf9aee1b81dffe5801f04e7f4e14085b0 |
| 3 | One matching row in the conversation, hold marked approved with no failure or retry fields | row msg_0eed8dcd-68a3-4101-a391-3f0d83a4df03, conversation 81e09472-535a-4a10-9531-521b926cf949 |

The busy-inbox case itself (the AppFolio door queuing a Situs text) is proven the next time it happens at Situs; the Situs Leasing session's watch reports it.
Too small to delete anything. Since the swap, Opus wrote 5 first emails, all at Willows (our test property). Western Slope, Situs and Camellia got no new guest cards in the window: the last customer card synced at 01:08 UTC, and an overnight gap like this is normal (about 6 customer first emails a day on Oct 30). On the 5 Opus emails no check fired, nothing was held or retried, and the words Opus wrote are the words that were sent. Zero out of 5 is still consistent with a true rate as high as about 60 per 100, so it proves nothing yet.
| Door | Oct 26 ledger: Western Slope first emails, 30 days (fires: right / false / unresolved) | Same, per 100 sent | Opus, 5 Willows emails | Opus, customer emails |
|---|---|---|---|---|
| Door 1, should Clara speak (the turn chose no reply) | 0 | 0 | 0 | none sent yet |
| Door 4, claims match the record (first-email checks, fact trace, writer retry) | 47: 1 / 6 / 40 | 63: 1 / 8 / 53 | 0 | none sent yet |
| Door 5, voice | 0 | 0 | not on this path | not on this path |
| Door 6, send door and Supervised holds | 0 judged | 0 | 0 | none sent yet |
| Fair-housing judge | 0 (the one false fire was at Camellia) | 0 | 0 (5 of 5 "Compliant") | none sent yet |
| All doors | 47: 1 / 6 / 40 | 63 | 0 | 0 emails |
How the per-100 is built: the 47 Western Slope first-email rows of the Oct 26 ledger (the fact trace, draft checks, no home named, sentence strip, tour-time retries, retry passed), over the 75 Western Slope first emails sent in the same 30 days (one "lead replied" event each). An estimate: held emails are not in the 75, so the rate per email attempted is a little lower. Camellia (2 first-email fires) and Situs (1) are left out because their sent count for that window was not recorded.
| Kind | Count | Which |
|---|---|---|
| Checks in the reply loop (trace steps) | 8 | draft-drop, internal-prompt strip, fair-housing, promise backing, booking promise (recorded on 4 of 5 turns), how-much-is-open, PII, send gate |
| First-email checks (block, retry once, then hold) | 7 | bare hand-off, draft checks (tour times, rent per size, dashes, hedged money, unbacked promise), no home named, fact trace, how much is open, PII, send-door check |
| Send door and holds | 2 | first-email send door, Supervised hold |
| Rewrite layers on this path that changed no word | 10 | 5 internal-text strips (reasoning preamble, sent-time stamp, tool-call markup, prompt leak, first-email leak at send), draft salvage, empty-reply fallback, 3 hand-off rewrites |
Rewrite layers not settled by this run. The why-line strip changed 5 of 5 emails, by design: the writer is asked for a one-line reason and it is cut before sending. The email template (greeting, shell, footer, signature, markdown strip; 8 layers) wraps every email at send and leaves no record, so the saved draft cannot show it. The other families (voice and video, staff replies, bounce texts, side writers, transport) never touch a first email, so this ledger says nothing about them. That is 19 of the about 60 layers on this path, 10 at zero.
False fires on Opus: none. No fire, so no Fable judge was run.
Also seen. One Willows email from a sender with no guest card ran on Sonnet at 14:00 UTC and was held by the Supervised setting at Willows: that is the general inbound email lane, which the first-email swap does not cover.
Next run: after the first 50 customer first emails on Opus (about a week at today's pace). A check that fires rarely, like the fair-housing judge (once in 30 days), needs about a month of mail before "never fires" means anything.
Sources, read only: agent traces since 05:11 (45, of which 5 on claude-opus-5-5: trace_aa2fc390, trace_6a429fbc, trace_aaa674d1, trace_9b5c2c9a, trace_fcff1596); the draft stop record since 05:11 (45 rows, 0 first-email rows); first-email intent rows by key (5 of 5 sent, no held checks); conversation rows (sent words equal the saved draft, 5 of 5); new-lead events and the prospect list per company; CloudWatch (5 first-email turns logged, all at Willows; 0 customer guest cards created after 01:08). Raw rows: gate-ledger-opus-2026-10-31.json (scratchpad of session 001).
The narrated explainer, the whole-vision podcast in two cuts, and the five chapter episodes.
Listen: the whole vision in 22 minutes. The medium cut Fede asked for; two hosts, generated from this page on Oct 24 night with the facts pinned and claim-checked line by line. Episode page with read-along transcript, brief and outline.
The 63-minute deep dive (the Oct 24 night redo, 148 of 157 claims confirmed on the page):
Listen: chapter 1, the new hire and the spine, 5 minutes.
Listen: chapter 2, week one, 4 minutes. Two hosts, generated from this section with the facts pinned and claim-checked (nothing invented).
Listen: chapter 3, learn, get feedback, then supervised, 5 minutes. Two hosts, generated from this section with the facts pinned and claim-checked (nothing invented).
Listen: chapter 4, live as a coworker, the five habits, 7 minutes. Two hosts, generated from this section with the facts pinned and claim-checked (nothing invented).
Listen: chapter 5, the two dials, scope and autonomy, 4 minutes. Two hosts, generated from this section with the facts pinned. Claim-checked: 24 of 26 confirmed; two slips to ignore: the senders are live today, not merged-but-off, and "ships dark" means default off until Fede turns it on, not running silently.
Read-only audit, Oct 31, against main 2014531244. Fede: "seems like we left it half baked." Mostly true: the plumbing landed; the three goal items did not.
| Promise (Fede's words) | What shipped | What is missing | State |
|---|---|---|---|
| "specialized smaller prompts" (Oct 24) | A split option in the bench only (bench #31) | Nothing split live; leasing prompt grew 2,298 to 2,616 lines. Row 143 parked | Not started in production |
| "a higher frontier model" | First email on Opus (#24036, live Oct 31, Willows proven) | Every other job still Sonnet | Partial: 1 job of about 6 |
| "a repeatable way to upgrade it safely" | Bench upgrade command (bench #40 to #62) | Bench: 0 Situs, 0 Spanish, 0 replies; his Wrong cards not frozen | Half-baked |
| A. Frozen baseline | Bench #21; Day 0 #22445, #22457 | Hold decisions only, not words | Done for holds only |
| B. Hold: one door | #22334, #22450, #22473 (0 of 5,515 differ) | Nothing; row 4's text is #22473 | Merged with Day 0 proof |
| C. Ask for help | Seam #22455; folds #22592, #22595, #22617, #22646 | Staff senders still in 38 files | Seam only, callers not collapsed |
| D. One classifier | #22519, #22797 | Live run | Merged with Day 0 proof |
| E. 47 copies to 4 | 10 row folds (#22506 to #22877), 5 door folds | Every detector kept | Moved, not reduced |
| F. Model plumbing | #22446, used by #24036 | Nothing | Done (tracker says canceled) |
| G. Situs onboarding | #22373, #22411 at Willows | The Situs package itself | Willows only |
| H. Full context bench | Bench #27, #28; tenant shadow week | Repair prompt on Opus stuck locally (row 16) | Half-baked |
| I. Hand-off fixes | About 40 single-case fixes | The framework (row 13); "did a person already reply" (row 14) | Patches only, framework not started |
| J. Tenant replies, dark | None | All three rows | Not started |
| K. PMS inbox adapter | Read interface only (#19263, Oct 13) | The pass-through | Not started (in motion today) |
No go from Fede was ever recorded; the page still says "awaits his go". His Oct 26 yes covered folding only.
| Step or verdict | What shipped | State |
|---|---|---|
| 1. Hold lifecycle | #22538, #22559, #22542, #23954. Re-alert covers leasing only; 106 of 110 unseen holds were maintenance texts | Partial |
| 2. Facts in, with ids | Facts block (#22932, #24015). No ids; vacant units, not posted homes | Partial |
| 3. Declaration schema | None | Not started |
| 4. Door 4 as one join, observe week, retire lines | #22649, #22691 moved checks, unchanged. No join; fact trace grew (#23347) | Fold only |
| 5. Voice to retry-only | #22643 moved them; the dash check still holds | Not started |
| 6. Check registry plus build check | None | Not started |
| Delete: farewell loop | Still on main | Not done |
| Delete: shape rules Opus never trips | Still in the first-email draft checks | Not done |
| Delete: availability forced retry | Still on main | Not done |
| Delete: typed tool-call strip | #23013: retry gone, cut became a hold | Changed, not deleted |
| Delete: work-order claim | Kept (delete judged unsafe) | Correctly kept |
| Fold: 5 rows (facts, date, tour time, promises, follow-up facts) | Behind door 4, detectors intact | Moved, not merged |
| Target | Before (30-day ledger) | Today |
|---|---|---|
| Leasing emails held for no reason | Door 4: 14 false, 4 right, 60 unreadable; 110 of 124 holds expired unseen | No live number. Bench only: held first emails 24 to 12-16 of 51 |
| Wrong data sent | Never counted: the ledger logs holds, not what got out | No live number. 356-card replay: Fede's checks tripped 46 Sonnet, 31 Opus |
| Accidental escalations | One in three hand-offs never reached a person; 3 hand-off-promise catches | No live number. Replay: "check with the team" 3 to 1. Row 147 open |
Fix-shaped, already decided, no switch, nothing turned on at a customer. Size: S under 200 lines, M under 600.
| Item | Size | Proof | Before-number it moves |
|---|---|---|---|
| Delete the farewell-loop silencer (Oct 27 rule: never fires on Opus, delete) | S | Day 0 | 0 fires in 30 days; check count about 255 |
| Delete the availability forced retry (gate report verdict) | S | Day 0 + replay | Forced retries on first emails |
| Delete the first-email shape rules Opus never trips, now that the first email is on Opus | S | 356-card replay | 0 of 72 Opus trips; held first emails |
| Row 101: delete the first email's rewrite and retry layers Opus never trips | M | 356-card replay + Willows | About 60 rewrite layers |
| Row 147: no "could not answer" team email when Clara answered fully | S | Willows | Accidental escalations |
| Row 107: tool results stop prescribing "I've alerted the property manager" | S | Replay + Willows | Unbacked hand-off promises |
| Row 142: reword the other rule headings Clara can echo (same fix as #23822) | S | Willows | False holds |
| Row 140: availability and reminder texts say today or tomorrow (extends #23792) | S | Willows | Formal date on 10 of 356 Opus cards |
| Row 88: follow-up writer stops echoing a bedroom count from card notes | S | Replay + Willows | Wrong data (Jessalyn) |
| Row 65: an Outlook 550 bounce marks the address dead | S | Replay | Repeat sends to dead addresses (David Jones, 2) |
| Row 112: Day 0 compare pairs by question, not position | S | Planted grid change | Proof rail reliability |
| Tracker: fix the 10 rows whose status disagrees with their PRs | S | bin/refresh-tracker | 18 "said, not acted on" |
Needs Fede
Fede, Oct 31: "review what was done this week, what we did well, where we missed, and the roadmap for what's next", "grounded in reality of the operations and what we learned in Western and Situs from real production interactions", "check my transcripts with Gera too", "plan of attack: autonomous weekend prep and hands on Monday". Two reviewers (Fable 5.1 and Opus 5.5, max effort) worked the same brief independently on this week's production records; a third agent read the 14 raw Fede and Gera transcripts. Full texts are at the bottom.
| Question | Fable | Opus |
|---|---|---|
| Smaller prompts | Not started. Leasing prompt grew 2,298 to 2,616 lines | Same. One real email ran on a 47,000-word system prompt |
| Opus 5.5 | First email only; no real renter had one yet (no new lead at any customer since 05:00Z) | Same, re-checked 16:06Z |
| Fewer checks | Not started: 255 sites, 60 rewrite layers unchanged | Re-housed, not reduced; 1 measured deletion; 3 of 10 judged fires false |
| One engine | Partial, pass-through only | Same |
| "Finish, no WIP" | Missed: 21 tracker rows disagreed with PRs | Missed: cap 3 merged-unproven rows, actual 42 |
| What next | Gates and hold lifecycle before any further swap | Same; honest that it slows the Oct 30 funnel order |
| Western Slope (autonomous) | Situs (supervised) | |
|---|---|---|
| New leads answered | 23 of 26 | 9 of 38 |
| Median time to first reply | 2.0 min | 11.5 min |
| Leads with no first-email attempt at all | 0 | 30 of 38 (skip reason logged, never stored) |
| Renters with no personal reply from anyone | 0 | 14 |
| Held messages decided by | our team 24, client staff 0, timed out 52 (of 110 at both customers) | |
Other misses with ids in the full texts: one renter got two first emails 63 seconds apart naming different homes; an approved email opened with the wrong first name; two callers never got their follow-up text (waited 74 times for an AppFolio record, then expired); one booked tour never reached AppFolio after 50 tries; "lock in the tour" held 5 replies as a false booking promise; hand tests from our side on Situs's live line paged real staff twice.
34 commitments: 7 done, 15 partial, 10 not started, 1 contradicted. No trace of work: track every inbound as an open item; Spanish bench slice; plan to exit supervised tenant replies; lead-source report to Situs; "get renewals ready"; the green check on unsent drafts; remove old-model rules once on Opus; Western Slope staff guidance. Contradicted: "no new features" (Oct 28) then two tour features merged Oct 30 on Fede's own go. Gera owns maintenance, the classifier and its gates, tenant AppFolio messaging.
Method: one ledger of every check and rewrite layer; each row lands in one of four buckets by rule (keep: one-right-answer send rules and fair housing; context: hand the fact in with ids, then delete; fold: into one of four doors; delete: zero or only false fires, or a Sonnet-era habit); every rewrite layer becomes retry-once-then-send or is deleted; one PR per family proven on the Day 0 replay plus that family's historic fires; the count only goes down (registry with a build check); one weekly scoreboard: checks, rewrite layers, false-fire rate, holds expired unseen.
| When | Work | Who decides |
|---|---|---|
| Running now (Oct 31) | Triage ledger of all 315 rows; step 1 hold lifecycle (leasing); step 2 facts with ids, "open now" from what AppFolio advertises; funnel inspection by both reviewers (job map, turn 1 vs turn N inputs, one-writer verdict). Merged today: hold records name the check (#24119), PMS inbox behind one interface (#24118); ask-for-help collapse (#24120) in the queue. | Grant |
| Weekend, autonomous | Delete and fold waves per family from the ledger; the merged weekend queue below; bench gaps (Fede's 8 Wrong cards frozen, Situs and Spanish slices); steps 3 to 6 in order (declaration schema, one claims check in watch mode, voice retry-only, registry). | Grant; each PR dark, proven, armed |
| Monday, hands on with Fede | Read the Opus ledger after the Monday lead wave (first real Opus mail); read the one-writer verdict and decide; walk the Situs autonomy settings with Gera (his Oct 30 ask); the two remaining asks below; the funnel order for the next swaps. | Fede |
Monday's lead wave is the first real Opus traffic, through checks and rewrite layers tuned for Sonnet, with no production number yet for how often they fire on Opus. The two failure shapes are both on record: a check fires and the retry sends a trimmed email (Yaramin lost her terms), or nothing goes out and nothing alarms (Jeanine, four days). The Opus ledger re-runs after about 50 customer emails.
Fable 5.1 review Full text
# Clara refactor lane, week review, Oct 24 to Oct 31, 2026
Independent, read-only review (Fable 5.1). Sources: Western Slope and Situs production records pulled Oct 31 about 15:05Z by key and query (conversations, holds, the check-fire ledger, ratings, Slack cards), the docs and tracker, the bench, PR bodies. Maintenance holds are Gera's lane and stay out of this lane's misses.
## A. Scoreboard
| Fede's goal, his words (date) | State | Proof |
|---|---|---|
| "decomposed job prompts" (Oct 24) | Not started | Leasing prompt grew 2,298 to 2,616 lines and 163 to 179 RULE lines in 39 commits since Oct 23; first-email rules file 741 to 850 lines. The split (row 143) is parked, no PR. |
| "Opus 5.5" (Oct 24); "entire leasing funnel email+text on Opus" (Oct 30) | Partial | First email on Opus for every customer since Oct 31 05:11Z (#24036), proven on Willows (trace aa2fc390). Email and text replies still on Sonnet. By 15:05Z no Western Slope or Situs turn had run on Opus: no new guest card has arrived at either customer since the switch (last first email: Western Slope bb81adac, 01:09Z, Sonnet by the time, the model is not stored per email). Willows since the swap: 5 Opus first emails, 0 checks fired, 4 sent within a minute, 1 with no send recorded (unexplained). |
| "a repeatable safe upgrade path" (Oct 24) | Partial | Model named per job in code, swap = one PR, rollback = revert; 356-card replay, bench, Willows before the swap. Gaps: bench has 0 Situs and 0 Spanish items; Day 0 compares hold decisions, not words. |
| "fewer, reliable gates" (Oct 24); "fix all the gates and rewrites" (Oct 31) | Not started | About 255 check sites and 60 rewrite layers (Oct 26 inventory), unchanged; 38 retry and rewrite call sites on main. The six-door order has read "awaiting Fede's go" since Oct 26 and was never put to him as one yes/hold line. |
| "one core engine every module uses" (Oct 24) | Partial | Hold rule behind one seam (#22334, #22450, #22473), 0 differing cells on 5,907; askTheTeam callers collapsing; PmsInbox door open (#24118). Pass-through only. |
| "count fires on the Opus arm, never fires means delete" (Oct 27) | In motion, 0 deletions | Fire ledger exists since Oct 27 03:06Z, Sonnet only; 10 checks fired zero times in 5 days. Opus-arm ledger starts today. |
| "finish decided work, verified end to end; nothing new without a decision" (Oct 30); "no new features" (Oct 28) | Missed on "finish"; "nothing new" held | The decomposed prompt, decided Oct 24, never started; rows 4 and 6 "said, not acted" 6 days; 21 tracker rows disagree with their PRs; 337 PRs merged in 8 days. The Oct 30 tour features (#23858, #23938, #23869) carry Fede's own go in their PR bodies ("yes build it"), so they are a course change he did not pass to Gera, not sessions inventing scope. |
| Situs leasing "all by itself" by end of week (to Gera, Oct 27) | Partial | First email autonomous at Situs since 05:11Z; replies and texts stay Supervised. The week's measured gap between the two stages: Western Slope, autonomous, Clara answered 23 of 26 new leads, median 2.0 minutes; Situs, Supervised, 9 of 38, median 11.5 minutes, 30 of 38 with no first-email attempt at all (reason only logged, never stored), 14 renters with no personally written reply from anyone, AppFolio's auto-reply reaching 25 of them first. 4 renter-facing Situs holds expired unseen. |
### Docs say vs production shows
| Docs or tracker say | Production shows |
|---|---|
| Hold lifecycle: 4 PRs merged, "every hold ends in a send, a person or an alarm" | Held emails re-alert; held renter texts expire with no alarm (3 Situs, 3 Western Slope this week) |
| Specials "RESOLVED Oct 29, Situs 20/20, WS 4/4" | A send check stripped "4 week free" from Yaramin's Lampliter email Oct 30 (it kept "after a rent concession"), so the renter got vaguer terms than the listing |
| "Retry never ran on held first emails" (Oct 26) | Fixed by #22500: 13 drafts went out as sent_after_retry at the two customers this week |
| Bench "PASS, never-do 0" (Oct 30) | Fede's own ticks, all properties: 27 Wrong of 64, 53 unread when PASS was declared. Of 59 customer ratings by anyone, 57 judge Opus shadow drafts never sent (his two specials Wrongs judged drafts whose facts block had no special) and 2 judge a reply a renter received (Pomona Park, Oct 26, both Wrong) |
| Tracker: hold lifecycle, ask-the-team, rewrite audit all "open" | Their PRs are merged (4, 1 and 8) |
| Opus "live, all customers, 05:11Z" | True in code; zero customer turns on it by 15:05Z, because no new lead has arrived since |
| Situs sources on the approved list get a first email | 8 TurboTenant cards from 7 renters (Oct 26 on) had an approved source and an email and got no first-email attempt, no draft, no card; 3 of the 7 never got a staff message either |
## B. What went well
1. **The plumbing refactor touched no renter.** Four seam PRs (#22334, #22450, #22473, #22592) merged with 0 differing cells on the 5,907-cell Day 0 replay; renters got exactly the messages they would have gotten before.
2. **Situs Supervised first emails got out fast.** 13 of 24 prospect-facing Situs holds were approved within 0 to 41 minutes: 12 by Fede (11 in the app, where the approver id resolves by key to his account: eschold_4e1984ca, first email, 31 seconds; eschold_539542e7, reply text, 27 seconds; and Deyci's held text eschold_5fb65729 from the Slack card in 13 minutes, the first approve-from-Slack, #24079) and 1 by another PropFlow admin; none was edited before sending; no customer staff member closed a hold at either customer. Those renters heard back inside the hour.
3. **The checks that fire catch real mistakes, and the retry now runs.** Amy, Western Slope (3edfcfc7): the draft said "Tuesday November 5" (a Thursday) and that "a few 3-bedroom homes" were empty; two redrafts fixed both and she got a correct reply. Anthony (3aad8363): "lock in" before any booking, redrafted, sent. Before #22500 the retry never ran on a held first email; this week 9 Western Slope and 4 Situs drafts went out as sent_after_retry (the same path is the suspect in Micheal's double first email, C row 3).
4. **A false "Not sent" was chased to the record.** Three Situs approved texts showed "Not sent" Oct 30; the conversation rows showed the two real ones went (quiet hours delayed them) and the third belonged to a test lead at Zephyr. Fix #24021 live, proven on Willows Oct 31 08:00 MT.
5. **Two rulings became code the same week.** Specials read by a model with exact quotes, no word list (#23252, #23253; Situs 20 of 20, Western Slope 4 of 4 matched the listing). The Anthony hole, our own check emailing Western Slope staff to reply by hand (Oct 27), closed by #22417 the same day.
## C. Where we missed
| Miss | Dropped rule (date) | What production shows | Cost |
|---|---|---|---|
| 1. Opus shipped on top of unconsolidated gates | Gate report order "hold lifecycle first, then fold and delete" awaiting go since Oct 26; Fede Oct 31: "how did we roll out opus without consolidating and fixing all the gates and rewrites?" | Three Situs renters were dropped silently between Oct 27 20:16Z and Oct 28 00:00Z. Jeanine (card c08dd8c5, thread 2fb269b4): three checks fired in a row (booking promise, no tool call, tour-time redraft), nothing sent, no hold row, no card; four days later nobody has replied to her. Tyler (cards 2c3aa774 and 1f058cd7, thread 71fa5b88): his first draft was stopped by a unit-count check with no card; four later drafts were held Oct 28 to 29, one rejected by Fede (eschold_c008df66); his first reply was an email a PropFlow person sent by hand 44.9 hours later; his follow-up text card is still pending after 63 hours. Chataca (card d181a905, thread fb2adfc2): two replies stopped with no check named, nothing sent; 4 minutes later AppFolio sent her a showing notice under a staff member's name. | Opus now runs behind the same checks with zero production measurement. #23954 (Oct 31) gives Jeanine's shape a hold row from now on; Jeanine is still unanswered. |
| 2. Renter-facing holds and queued texts still die unseen | "Every hold ends in a send, a person or an alarm" (#22486, Oct 26) | Emelyn (Situs, card 4b10dedd): first email eschold_83a5fa65 expired after 24 hours; the first staff-written message reached her 25.5 hours after she inquired. A Situs renter called Oct 28; the tour confirmation text (eschold_lr_3c63930d) and the cancellation text (eschold_lr_2b421ee2) both expired unseen, so her receipt for the call never arrived. Follow-up texts: eschold_lt_ecccba5a expired, Tyler's eschold_lt_ab532d59 still pending after 63 hours. Western Slope reply texts: 3 expired, 2 pending; 9 Clara texts sit stuck in the AppFolio text queue. Two Western Slope phone callers (Dora dc885796, and 97db06a3) got nothing after their calls: each follow-up text waited 74 times for an AppFolio record, then expired as stale. Lori's tour (7d9fe683, tour 9dc6f8eb) was booked but never written to AppFolio after 50 tries, and her 1-hour reminder was held as the wrong thread. | #22538 re-alerts emails only (its body: "Leasing only for the re-alert (clara_prospect_email)"); held texts have no alarm. And when a person did act on a Slack card, the card bot sometimes did not: Fede's two "Approve" replies on Deyci card 1793339299 got "Draft unchanged"; his "approve" on card 1793371126 got "Revised per your feedback" quoting words he never wrote, and a session sent the text by hand 11 minutes later; his "Rejected. Do not send" on Lampliter card 1793387032 got "Draft unchanged"; Gera's two questions and Fede's "im on this already" on the Nicole card each produced a rewritten draft. |
| 3. Fact bugs reported resolved reached real renters | "Specials always mentioned"; "no partial sends" (Oct 26); row 127 "Situs 20/20, WS 4/4" (Oct 29) | Yaramin (Situs, Lampliter, 7b77f97b, Oct 30): a send check stripped "4 week free" from a true sentence (ledger: "fact_trace: stripped"); her email kept "after a rent concession" but lost the terms. Her Opus shadow card was handed a different home with no special (Fede: "no mention of concession"); Kerry's (Western Slope, b9274556) was handed the $2,300 home with no special (Fede: "Missed specials") while production named it. Micheal (Western Slope, 8ee909cf) got two first emails 63 seconds apart from one card, naming different Ember homes and prices: 1,228 sq ft "from $2,300/mo" at 14:09:53Z, then 1,289 sq ft at "$2,550 or $2,795 a month depending on lease length" at 14:10:56Z. The ledger ties only the second to the first-touch path (check fired 14:10:49Z, sent after retry), which leaves the first unexplained; inferred: the same inquiry answered through a second door. David got two 48 seconds apart from duplicate cards. Deyci's approved Situs first email (card c9a96584, msg_7ceae9e6) opens "Hi Pedro"; the hand approval did not catch it. | One renter got vaguer terms than the listing; both specials Wrongs trace to the facts handed to the shadow writer, not the model; the fact-trace fix (Patrick, Oct 26: "I've sent the application" held on Clara's own turn) sat under "fix now" since Oct 26. |
| 4. Fede's feedback loop closed late | "Fede ratings are the bar" (Oct 30); "Spanish first-class" (Oct 23); 8 commitments made to Gera | 64 ratings from Fede (27 Wrong, 37 Right); 53 sat unread while the bench said PASS. His Oct 28 to 30 Wrong notes ("No need to say a leasing agent will meet you", Micheal 8ee909cf; "Confusing office hours with availability", Deyci 9278d6a6) got their fix Oct 31 (#24064). No delivered trace for 8 commitments he made to Gera (listed in D row 8); and the Oct 30 tour features (#23858, #23938, #23869) carry his own go in their PR bodies two days after he told Gera "no new features", a course change Gera was not told about. An agency writing for a client at Lampliter (Michael N., b09b8690, eschold_abe02cc4) got a full priced draft from both arms, rejected by hand on Fede's "stay quiet" ruling, which is in neither prompt. | Fede re-asked decisions he had made and called the work "half baked". |
| 5. Statuses lag the work; hygiene debt | "Keep the roadmap current" (Oct 26); "No WIP tomorrow" (Oct 28); "No Slack bursts"; "Tests never write to production" (Oct 26) | 21 tracker rows disagree with their PRs (hold lifecycle "open" with #22538, #22500, #22542, #22559 merged; 6 "wip" rows with no PR). 11 branches of closed PRs on origin, 14 worktrees. Issues #24114 ("Approved, will send" on texts that went) and #24022 (false "Not sent", duplicate text) open. 96 "calendar unreachable" cards from the Oct 25 bench door still in #western-slope. Fake rows written into Patrick's real thread Oct 26. Day 0 (hold decisions) cited as proof for changes to Clara's words. Gera's lane, but a production lesson from the Situs channel: Fede on Oct 28, "Stop the work order created texts. Appfolio already does it", yet such texts kept being drafted and held at Western Slope (eschold_mr_0b1aa8c6, Oct 28 21:09Z, expired), and on Oct 29 Hugo asked that Clara "not do anything with maintenance" through the weekend. | The page Fede reads to know what is untested was wrong on 21 rows; staff saw a wrong status twice. |
| 6. Most Situs leads got no first-email attempt | "track every inbound as an open item" (Oct 26, no trace); the lead-source report promised to Situs (Oct 27, no trace); "if we can do it, we do it" (Sept 30) | 30 of 38 new Situs leads this week had no first-email attempt (measured); the skip reason is logged but never stored, so the cause is inferred from source labels. 8 TurboTenant cards from 7 renters (a9bcb78a, 02ac1d6a, 5c6ed19b, cc393d6c, b16b132b, e6ef77b9, 6e334420, 88f056e0) had an approved source and an email, were each seen within a minute of their AppFolio inquiry time (spread Oct 26 to 30, so real-time leads, not a backfill), and got no draft, no card, no Clara activity; 3 of the 7 never got a staff message either. AppFolio's auto-reply reached 25 of 38 renters first; 14 never got a personally written reply from anyone; Clara answered 9 of 38, median 11.5 minutes. Western Slope, autonomous, same week: 23 of 26, median 2.0 minutes. | The Situs week measured approval speed on 8 leads while 30 went unmeasured and mostly unanswered; whether this is a bug or a gate working as designed cannot be told from the data, which is the miss. |
## D. Roadmap (dependency order)
| # | Piece | Why (Fede's words) | Status |
|---|---|---|---|
| 1 | Gate fire ledger on the Opus arm: every check and rewrite counts fires per model for one week | "count fires on the Opus arm, never fires means delete" (Oct 27) | In motion today |
| 2 | Hold lifecycle for renter texts: alarm and re-alert for held reply and outreach texts; confirm #23954 covers the Jeanine shape on Willows | "every hold ends in a send, a person or an alarm" (Oct 26) | Not started |
| 3 | Seams: row 4 one shouldSpeak log line, no duplicates; row 5 askTheTeam caller collapse; row 6 PmsInbox pass-through (#24118) | "one core engine every module uses" (Oct 24) | In motion today |
| 4 | Fact-trace fixes from the real cases: this turn's tool results count (Patrick); the listing special is a traced source (Yaramin) | "no partial sends"; "specials always mentioned" (Oct 26) | Not started |
| 5 | Fold and delete from the ledger: zero-fire checks on the Opus arm deleted, doors folded per the gate report | "fewer, reliable gates" (Oct 24); "fix all the gates and rewrites" (Oct 31) | Needs the go below, after 1 |
| 6 | Decomposed first-email prompt (row 143) on Opus: bench and shadow, Fede reviews, swap by hand | "decompose, never rewrite" (Oct 24) | Parked |
| 7 | Next swaps: email reply and text reply on Opus, bench and shadow first, Situs and Spanish bench items added | "entire leasing funnel email+text on Opus 5.5" (Oct 30); "Spanish first-class" (Oct 23) | After 5 |
| 8 | Gap audit: the 8 commitments with no trace (Spanish bench slice, 0 items; "get renewals ready", no PR since; supervised-tenant exit plan; lead-source report to Situs; green check on an unsent draft; Western Slope hand-off guidance; "track every inbound", owned by this lane's reply framework and still open; remove old-model rules once on Opus), tracker reconciled to PRs, Fede's ratings read every morning | "never trust a recap" (Oct 27); "Fede ratings are the bar" (Oct 30) | Not started |
**The one decision (yes/hold):** run the gate report's order, hold lifecycle first, then fold and delete from the Opus ledger, before the email-reply and text-reply swaps. Recommend **yes**: each swap is one PR and loses nothing by waiting a week; the gates decide whether a renter hears back at all.
## E. Biggest risk next week
Monday's lead wave is the first real Opus traffic, through checks and rewrite layers tuned for Sonnet, with no production number for how often they fire on Opus. The Sonnet ledger shows both failure shapes: a check fires and the retry sends a trimmed email (Yaramin lost her terms), or nothing goes out and nothing alarms (Jeanine, four days). The bench saw Opus write longer and correct itself in line, so it may trip the checks more often; the visible symptom is more held cards on a weekend when only Fede approves, the invisible one is a renter who hears nothing, which at Situs was already most new leads this week (C row 6). Day one may already have one: of 5 Willows first emails on Opus since the swap, 1 (snapshot c5d5d07c, 14:17Z) has no send recorded, no check fired and no hold; Willows suppresses outbound by design, so it may be nothing, and F item 1 asks. The Opus-arm ledger and the text alarm turn that guess into a number by Tuesday.
## F. Weekend queue (no decision needed), by value per hour
Each item is fix-shaped, no switch, turns nothing on at a customer, sends nothing outbound. S under 200 lines, M under 600.
1. **Find out why the fifth Willows Opus first email has no send recorded** (snapshot c5d5d07c, Oct 31 14:17Z; 0 checks fired, no hold). Read-only, S. Before: 1 unexplained silent first email on the live Opus path, day one.
2. **Situs intake: find why 8 TurboTenant cards with an approved source and an email got no first-email attempt, then store the skip reason on the intent row** (read-only first; a bug is fixed under the grant, a gate working as designed goes to the needs-Fede list). S. Before: 30 of 38 Situs leads with no attempt and no stored reason.
3. **Slack card replies "approve", "approved" and "reject" are commands, never feedback for a rewrite, and a question from a person never produces a new draft.** S to M. Before: 2 approvals and 1 rejection by Fede answered "Draft unchanged" or "Revised per your feedback", 3 questions turned into rewrites. Proof: replay of the four cards, one Willows card.
4. **Alarm and re-alert for held renter texts**, the shape #22538 gave emails. S. Before: 3 Situs and 3 Western Slope renter texts expired unseen this week. Proof: Day 0 byte-identical, Willows held text left to expire.
5. **Fact trace counts this turn's tool results**, so "I've sent the application" is not held when the send ran this turn (Patrick, Oct 26). S. Before: 1 true sentence held. Proof: replay of the card, Willows.
6. **Fact trace treats the listing special as a traced source** (Yaramin: "stripped: 4 week free"). S. Before: 1 email lost its concession, rated Wrong by Fede. Proof: replay of the Lampliter cards, byte-identical elsewhere.
7. **Hold page shows "Sent" after the quiet-hours sweep** (#24114). S. Before: staff see "Approved, will send" on texts that went. Proof: Willows, production screenshot.
8. **Duplicate sends: the session-cold hold marked final yet the text re-sent (#24022), and one inquiry answered twice through two doors (Micheal, C row 3).** S to M. Before: 1 duplicate text with a false "Not sent", 1 renter with two first emails naming different homes. Proof: reproduced-case replay of both, Willows.
9. **Outlook 550 bounce marks the address dead and stops the cadence** (David Jones, Oct 26). S to M. Before: 2 dead addresses treated as live. Proof: replay of the bounce record.
10. **Tracker rows reconciled to their PRs** (status derived, never typed). S. Before: 21 wrong rows. Proof: the refresh output.
11. **Delete the 96 "calendar unreachable" cards** the Oct 25 bench door posted in #western-slope (Fede under the burst: "wtf"; our own bot, internal channel, print the count first). S. Before: 96 junk cards above real alerts.
12. **Branch and worktree cleanup**: print the list first; anything with unpushed work stays Fede's. S. Before: 11 closed-PR branches, 14 worktrees.
13. **Voice and video blank-reply fallback copy**: one constant with an em dash and an unbacked promise. S. Before: 1 string. Proof: unit-covered, one Willows call.
14. **Dark setting proof before it ages out**: the Oct 27 staff welcome text setting (#22648, #22669) crosses 7 days Monday; run its Willows proof and link it on the register. S. Before: 1 setting about to render "prove it or delete it".
15. **AppFolio literals out of core**: 41 in the leasing domain and agent files, one small byte-identical PR per file (Fede, Oct 15). M total. Before: 41.
Not this weekend: deleting the 10 zero-fire checks. Fede's rule is "never fires on the Opus arm"; the ledger holds 4 days of Sonnet. Prepare locally if idle; merge only on the Opus ledger.
**Needs Fede (3 lines):**
- Six-door order go (hold lifecycle first, then fold and delete from the Opus ledger) before the reply-model swaps. Recommend yes.
- Situs under Supervised answered far fewer leads than Western Slope autonomous (A row 8), Fede approved by hand 12 times and customer staff never, and tour confirmation, cancellation and follow-up texts expired unseen: flip those three text kinds to autonomous now, or accept the gap. Recommend flipping confirmation and cancellation; they are the renter's receipt for a call she already made.
- Fixed-template maintenance notices skip review (Western Slope: 46 supervised maintenance holds this week, most expiring). Gera's lane, Fede's call.
Opus 5.5 review (the test-call wording in miss 6 was corrected by its author after ids were pulled) Full text
# Clara refactor lane: week review, Sat Oct 24 to Sat Oct 31 (Opus 5.5 reviewer)
**In short:** the plumbing is built and proven (the doors, the replay harness, a record of every stopped draft), but Fede's Oct 24 goal is about a third done in production. Opus writes the first email inside the old prompt and checks, and no real renter has had one yet. What hurt renters this week was wrong listing facts (5 of 21 first emails) and checks or holds that silenced them (Jeanine, Tyler). A model swap fixes neither.
## A. Scoreboard
| Goal, Fede's words | What the docs say | What production shows | Verdict |
|---|---|---|---|
| "specialized smaller prompts" (Oct 24) | Split on the bench only; the real split parked Oct 30. | The leasing prompt grew 14% (2,298 to 2,616 lines); Yaramin's email ran on a 47,000-word system prompt. | Not started in production |
| "a higher frontier model" (Oct 24) | Shipped. | First email only, all customers since 05:11Z Oct 31 (#24036), proven at Willows after it went live. No real renter has had one by 16:06Z. Everything else: Sonnet. | Partial, 1 of about 7 prospect jobs |
| "a repeatable way to upgrade it safely" (Oct 24) | Model registry (#22446); the bench upgrade command is in pieces. | Cleared by a hand-run 356-card replay, not the bench, which has no Situs, no Spanish, none of his 8 Wrong cards. | Partial |
| "we need to really question each one" (Oct 26, on checks) | Four of six checkpoints folded, 0 of 5,515 replay cells differing; report: keep 29, delete 7, fold 18. | Re-housed, not reduced: one measured deletion (#23013). Of 10 real fires judged, 3 were false: Jeanine lost her answer, Yaramin her special. | Partial |
| "let me get everything to one central place" (Oct 26) | One door each for speaking, asking the team (23 callers), person lookup, review, tools. | Callers not collapsed. Jeanine's and Patrick's held drafts never got a card. | Partial |
| "a more autonomous opus 5.5 with less checks but only if safe" (Oct 27) | Count Opus fires, delete never-fires: 0 PRs. | First-email stop records carry no model (0 of 43), and no real Opus email has gone out: nothing to count. | Not started |
| "we have too much on our plates", "done done" (Oct 30) | His Oct 27 cap: 3 merged-but-unproven rows. | 42 today. | Missed |
## B. What went well
1. **Fold first, then change.** 31 pass-through PRs put every check behind named doors with no behavior change on real traffic (promise checks: 0 of 1,816,185 differ on 24,543 real texts, #22691); a replay of every real record now guards core PRs (#22872). Western Slope never felt it: all 17 renters got a first reply, 16 within 15 minutes.
2. **Nothing edits Clara's words after she writes them** (#22249, #22296): checks block, one retry, then hold, so the Oct 22 Ember Estates rewrite that put invented unit counts in front of a renter cannot recur.
3. **Every stopped draft leaves a record** (#22659), so checks can now be judged: 7 of 10 fires were right, catching a wrong weekday ("Tuesday November 5", Amy) and an unbacked "I've sent the application".
4. **False "Not sent" labels fixed (#24021).** All 3 approved Situs texts marked unsent had gone out; a Willows text approved at 03:03Z went out at the 8 AM sweep (Twilio SMf9aee1b8).
5. **Specials fixed at the source.** Jose C. (Lampliter) got no special. The cause, a shared sync budget, was proven from 30 days of logs and fixed (#23253, #23252): stored specials Situs 20 of 20, Western Slope 4 of 4.
## C. Where we missed
1. **Opus went in before the cleanup it was meant to follow.** His Oct 26 order was split, fold, Opus in shadow, then his flip; on Oct 27, count Opus fires and delete what never fires. He pulled Opus forward on Oct 30 to 31 ("ok lets pencil in start with first email", then "Well not just situs all customers") on a bar that measured the writer, never the checks. A check re-drafts about a third of first emails in both the replay (114 of 356) and production (5 of 17 since Oct 27), and nobody knows how many were right; one cut Yaramin's special.
2. **False checks and invisible holds silenced renters.** Jeanine (Situs, conversation 2fb269b4, Oct 27) asked for a Friday tour. Checks read "get your full name to lock in the tour" as a booking promise and "5:20 won't quite work" as offering 5:20; the reply was held with no card, and she has no answer on record. Tyler (Situs, 71fa5b88) asked for an application; a check stopped the reply with no card, and Fede answered by hand 45 hours later. Austin (Western Slope) sent references Oct 26; a check held the reply, and staff answered four days later. Yaramin (Lampliter, 7b77f97b) lost her "4 week free" because the fact check could not match it to the listing's "4-week-free"; Fede rated her Opus card Wrong: "no mention of concession".
3. **Wrong facts reached renters; fixes came card by card.** 5 of 21 real first emails named a wrong home or price. Micheal, Bernadette and Kerry (Western Slope) were quoted "374 Ember Lane, 1,228 sq ft, $2,300": the property's own address with a delisted home's size and price (fixed Oct 29). Estrella (Situs) got a delisted home's rent and another home's special in an email Fede approved, as was Deyci's "Hi Pedro". After Kat's "no clear read" tour notes, Micheal and Trang got applications for a home neither had picked. Tour-time handling was patched 8 times in 7 days (#22178 through #24064).
4. **His ratings were not the bar until he caught it.** 53 ratings sat unread Oct 26 to 30 while the bench said PASS on 3 of 4 runs (Fede: "its frustrating its been a whole weeek"). All 8 of his Wrong cards were Opus versions; none is a frozen bench check or re-proven fixed, and Yaramin's still reproduces.
5. **Merged is not done; said is not built.** His cap is 3 merged-but-unproven rows; today 42. Said, not built: every inbound tracked as an open item (Oct 26), Outlook's "550" bounce read as a dead address, no alerts tagging Fede and Gera, the Spanish slice (Oct 23), a supervised tenant-reply exit ("not a month", Oct 21), the green check on unsent supervised drafts (Oct 29). A relay hole known since Oct 16 stayed open until it texted Nicole (Situs, Oct 27, under Supervised) "I've already reviewed the RFTA packet", which nobody had said (fixed that day, #22865). Promises to Gera show no work: renewals, the Situs lead-source report, Western Slope staff guidance. "No new features" (Oct 28, to Gera) changed on his own go two days later (#23858, #23869), without a word to Gera.
6. **Supervision and tests landed on real people.** Supervised mode is a queue only we see: of 110 held messages at the two customers, our team decided 24, client staff none, and 52 timed out. Emelyn's held first email (Situs) expired unseen; a person reached her 25 hours later. Fede's "Approve" on Deyci's reply was misread twice, and a version "revised per your feedback" with words he never wrote went out 9 hours after the card. Test calls from phones registered to our own test residents set off two evening emergency alerts to Situs staff (a third was Situs's own test), and "Michael" left 9 notes on real guest card 9010; today a Situs staffer answered his "$500 rent, 20 dogs" request (conversation f2978476). All after the Oct 26 rule that tests never touch production (#22504).
## D. Roadmap, dependency order
| # | Piece | Why (Fede) | State |
|---|---|---|---|
| 1 | Check ledger: each fire labeled right or false; first-email stop records name the model | "how many times do we rely on re writes?" (Oct 27) | In motion today |
| 2 | Fix false fires at the mechanism, one PR per check, Yaramin's and Jeanine's first | "the core turn needs to add the times not rely on another re write" (Oct 27) | Not started |
| 3 | Row 4: one hold path: every held draft gets a row, a working timer, one log line (#23954 started it) | "let me get everything to one central place" (Oct 26) | In motion today |
| 4 | Row 5: the 23 ask-the-team callers on one interface; none leans on client staff or relays their words unreviewed | "this should have never happened" (Oct 27) | In motion today |
| 5 | Row 6: property-software inbox behind the adapter, pass-through | "this is a big miss" (Oct 15) | In motion today |
| 6 | Delete never-fires, one PR per check, with ledger numbers | "start fresh not just copy paste the issues" (Oct 26) | Waits on 1 |
| 7 | His Wrong cards, Situs and Spanish in the bench | "did u check my ratings" (Oct 29) | Not started |
| 8 | Follow-ups to Opus after the gap audit; proposal: repair on Opus to Gera's lane (his Oct 30 order parks it) | "ok lets pencil in start with first email" (Oct 30) | Gap audit in motion today |
**Decision for Fede (slows his own Oct 30 order):** follow-ups move to Opus only after items 1, 2, 3 and 6, not job by job now? Recommend yes.
## E. Biggest risk next week
The first real Opus emails meet the checks and holds that hurt renters this week, and nobody can tell an Opus fire from a Sonnet fire. Opus cannot fix facts either: the 5 wrong homes or prices came from listing data. If follow-ups move on the narrow bench pass (better on 26 of 53 turns, worse on 19) before the ledger exists, every Wrong card becomes another patch on a 47,000-word prompt. The guard is the order in D.
## F. Weekend queue (no decision needed)
Fixes or proofs under the standing grant (no switch, no turn-on, nothing to real people), by value per hour.
| # | Item | Size | Proof | Before-number |
|---|---|---|---|---|
| 1 | Move our test calls and the "Michael" test lead off Situs's real line and AppFolio to Willows | S | Next test run lands only at Willows | 2 evening alerts to Situs staff; 9 notes on card 9010; staff answered one today |
| 2 | The 24-hour timer fires on later-turn holds | S to M | Tyler's hold replayed at Willows expires | Tyler's hold open 63 hours |
| 3 | Three false fires: "4-week-free" matches "4 week free"; "lock in the tour" and "won't quite work" stop reading as promises | S each | Each real turn replayed keeps its words | 3 of 10 judged fires false |
| 4 | First-email stop records name the model and keep the text finally sent | S | A Willows first email that trips a check | 0 of 43 rows name the model; 2 "held" rows sent anyway |
| 5 | Proof sweep: each unproven row gets a production record or is reopened, #23954 first; misstated rows corrected | S per row | Read-only records, Willows | 42 unproven rows (cap 3); 5 misstated |
| 6 | Delete the availability forced retry (report's delete list) | S | Replay compare | 10 fires in 30 days, 0 real catches |
| 7 | Outlook "550 5.5.0" counts as a dead address | S | Replay of David Jones's bounces | Follow-ups to 2 dead addresses |
| 8 | Hold status tells the truth (#24114, #24033, #24022) | S each | Willows approve; replay of Oct 30 | Wrong status on Willows and Situs holds |
| 9 | Alerts stop tagging Fede and Gera ("it should not tag me and gera") | S | One Willows alert | 13 of 198 alerts this week |
| 10 | His 8 Wrong cards as frozen bench checks | S to M | Each fails on its old input | 0 of 8 |
| 11 | Green check on unsent supervised drafts: tell Smith (Gera's screen) | Message | n/a | "Clara engaged" shown when nothing went out |
**Needs Fede:**
- Spanish bench slice, judged in Spanish with his spot check on 10? Recommend yes.
- Supervised tenant replies ("not a month", Oct 21): exit bar and first customer? Recommend Western Slope after his shadow-card verdicts.
- Situs replies after the first email go live (its open decision 4)? Supervision kept Deyci waiting 9 hours and left Fede answering Tyler by hand, while approved emails still carried a wrong price and name. Recommend yes once weekend items 2 and 3 are proven.
Fede and Gera commitments Full text
# What Fede told Gera we would do, Oct 20 to 31, and what happened
Every internal Fede and Gera meeting had a raw per-speaker transcript in #transcripts. All quotes come from those transcripts, so no item is recap-only. Not covered: the Oct 20 client leasing meeting. Fede missed the Oct 30 sync, so it is used only for what Gera owns.
Link pattern: `https://propflowai-workspace.slack.com/archives/C0BE1NFTA0K/p<ts without the dot>`. Extractions: `scratchpad/fg/out/*.md`.
## Oct 20, 12:32 and 15:18 (ts 1792517625.060189, 1792527765.013419; raw)
| Fede said | Topic | Promised / by when | What happened | Status |
|---|---|---|---|---|
| "I want to flip maintenance to be through a folio... Like, this week, that should happen." | Maintenance over AppFolio | This week | Handed to Gera on Oct 21. Gera's resident maintenance text now wakes Clara on the AppFolio thread (#21946, #21942). | Done (by Gera) |
| "Hopefully by today or tomorrow. I'll turn it on. We need it for Cytos Group." | AppFolio messaging, Situs first | Oct 20-21 | Western Slope leasing went on Oct 20, before Situs. Situs leasing is in supervised mode. | Partial |
## Oct 20, 17:20 (ts 1792534991.156149; raw)
| Fede said | Topic | Promised / by when | What happened | Status |
|---|---|---|---|---|
| "there's no source recorded, so I'll fix that. Fixing that right now" | Situs lead source | Now | #21536 merged Oct 20 | Done |
| "I'll supervise it for a week" | Situs leasing | A week | Still supervised on Oct 29. Opus first email autonomous Oct 31. | Partial |
| "start with very simple one doc... save the draft... have them do the final review" | WS renewals | Next meeting | Renewal packet built (#21876, #21956). WS deep-dive still pending. | Partial |
## Oct 21, 13:31 and 18:25 debrief (ts 1792607607.941179, 1792625319.521779; raw)
| Fede said | Topic | Promised / by when | What happened | Status |
|---|---|---|---|---|
| "turn it on supervised so that at least we know what would have happened" | Tenant inbox | Not stated | Shadow cards on every WS and Situs tenant text (opus-shadow-week) | Partial (shadow only) |
| "I don't think we should leave supervised mode for, like, a month" | Exit plan | Under a month | Tenant replies are still off everywhere, with no exit date | Not started |
## Oct 22, 18:29 (ts 1792712041.671219; raw)
| Fede said | Topic | Promised / by when | What happened | Status |
|---|---|---|---|---|
| "I'm gonna basically copy this for leasing for the next meeting" | Leasing who-owns-what for WS | Next meeting | Map drafted on the master page, not delivered (row situs-intro open) | Partial |
| "Put on some clear guidance for them... put the notes on the guest card" | Staff hand-off | Next meeting | No guidance document found | Not started |
## Oct 23, 13:02 (ts 1792778693.072789; raw)
| Fede said | Topic | Promised / by when | What happened | Status |
|---|---|---|---|---|
| "I want to clean up the prompt... a benchmark tool... upgrade models easily" | Baseline, Fede owns | Weeks to months | Gates folded into six checks. shouldSpeak PR2 in progress. | Partial |
| "my strategy of, like, haiku classifiers is kind of stupid" | Gates | Direction | Checks merged into fewer checkpoints, but the rewrite and retry layers still exist (tracker row open) | Partial |
| "upgrade to Opus 5.5. As a main model everywhere" | Model | Direction | Opus live only on the first email (#24036, Oct 31). Tenant texts, repair and voice wait. | Partial |
## Oct 23, 17:11 (ts 1792793634.155599; raw)
| Fede said | Topic | Promised / by when | What happened | Status |
|---|---|---|---|---|
| "running all the tenant communications through Opus 5.5... I'm gonna work on it all weekend" | Tenant comms on Opus | Weekend | Shadow ran. Repair on Opus in progress. Tenant replies off. | Partial |
| "once we have high confidence, we'll flip it. So it might take, like, a week" | Flip | About Oct 30 | Not flipped. On Oct 27 Fede made leasing the priority. | Not started |
| "I'm gonna be working on our benchmark in Spanish" | Spanish slice | Not stated | The Spanish benchmark slice has 0 items. Bench item #52 waits on Fede. | Not started |
## Oct 26, 15:05 and 18:18 (ts 1793045326.934169, 1793056944.308289; raw)
| Fede said | Topic | Promised / by when | What happened | Status |
|---|---|---|---|---|
| "let me get everything to one central place... just refactor" | Turn-decision framework | In progress | #22450, #22473, #22522, and about 20 merge PRs that combine duplicated code | Done (pass-through refactor) |
| "Cutting it down into pieces" (core prompt) | Prompt split | Since Friday | The leasing pure split merged. The real split by deletion is parked. The core prompt is not split. | Partial |
| "I think that should be our focus right now" (tenant inbox replies) | Priority | A couple of weeks | Fede moved the focus to Situs leasing in front of Gera on Oct 27 | Not started (superseded Oct 27) |
| "every single thing that comes in. We're tracking it somewhere" | Open items | Not stated | No open-item tracker. The rows "quiet-signal" and "hold-lifecycle" are open. | Not started |
| "the readiness gate is... replay the last 30 days... graduate" | Readiness | Not stated | Floors dropped Oct 25. The Opus swap went live after a bench PASS, his ratings and a Willows end-to-end run, with no 30-day replay score. | Partial |
## Oct 27, 11:37 (ts 1793119142.587719; raw)
| Fede said | Topic | Promised / by when | What happened | Status |
|---|---|---|---|---|
| "Opus 5 doesn't need that" (remove old rules) | Prompt rules | Not stated | Rewrite and retry audit open. "Delete layers after swap" open. | Not started |
## Oct 27, 17:58 (ts 1793142132.987359; raw)
| Fede said | Topic | Promised / by when | What happened | Status |
|---|---|---|---|---|
| "Our goal should be to be live... By end of week. With leasing" | Situs leasing | Oct 31 | Phones live Oct 28. First email autonomous. Other leasing paths supervised. | Partial |
| "Claire can read their email and then create the guest card" | Situs lead intake | Not stated | #23047, #24013, #24038 (TurboTenant, AffordableHousing) | Done (merged) |
| "I'm going to send [contact] a little report" | Situs lead sources | Not stated | Not found | Not started |
| "get renewals ready" | Renewals | This weekend | No renewals PRs merged Oct 28 to 31 | Not started |
## Oct 28, 19:04 Situs go-live debrief (ts 1793238193.424549; raw)
| Fede said | Topic | Promised / by when | What happened | Status |
|---|---|---|---|---|
| "by the end of this week, I wanna... Just let it... All by itself" | Situs leasing autonomous | Oct 31 | Only the first email is autonomous. Gera says the Situs leasing paths are still supervised. | Partial |
| "It's a bug. I'll own that." (backup calendar) | Tours | Not stated | #23587 (the offered slot carries its host) | Done (likely) |
| "address matching, alias... tomorrow morning" | Property match | Oct 29 | #23382 merged. Ember Estates design handed to the Situs Leasing session. | Partial |
| "No new features. Let's just fix this." | Priority | Now | Zoom tour recaps (#23858, #23938) and trip-aware tours (#23869), on Fede's later go | Contradicted |
## Oct 29, 18:49 (ts 1793317995.367689; raw)
| Fede said | Topic | Promised / by when | What happened | Status |
|---|---|---|---|---|
| "I want to keep it a few more days... Supervised, and then... Turn it on" | Situs leasing | A few days | First email went autonomous Oct 31 05:11Z | Partial |
| "if it's supervised, do not do the green checkmark" | Supervised UI bug | Not stated | No PR found (#21371 on Oct 20 is a different screen) | Not started |
| "I might just do it on the weekend... ready by Monday" (Orca) | Orca integration | Nov 2 | #23702, #23667, #23740 merged Oct 30 | Done |
## Commitments with no trace of work
1. Track every inbound message as an open item (Oct 26).
2. A Spanish slice in the benchmark (Oct 23). It has 0 items.
3. An exit plan for tenant replies in supervised mode, "not a month" (Oct 21).
4. The lead-source report to Situs (Oct 27).
5. "Get renewals ready" (Oct 27). No renewals PRs have merged since.
6. The fix for the green checkmark on unsent supervised drafts (Oct 29).
7. Remove the rules older models needed once Clara is on Opus (Oct 27).
8. Staff hand-off guidance for Western Slope (Oct 22). The who-owns-what map is drafted but was never delivered.
## Where what we built contradicts what he told Gera
- **No new features.** He said this on Oct 28. On Oct 30 Zoom tour transcripts and recaps and trip-aware tour offers merged. Both were built on Fede's own go in other sessions ("yes build it"), so this is Fede changing his mind without telling Gera, not a session acting alone. The Orca work doesn't count, because he told Gera about it on Oct 29.
- **Maintenance is Gera's lane.** On Oct 26 he said "I'm not gonna fix anything maintenance-related." Our lane still runs the repair path on Opus (tracker row repair-path, in progress). Check this with Gera.
- **Not contradictions, but changed in front of Gera.** "Tenant inbox is our focus" (Oct 26) became "I'll focus on getting leasing on" (Oct 27). "Opus everywhere, fewer gates" is unfinished, not reversed.
## What Gera owns (not our misses)
Maintenance end to end. Also: the topic classifier and gatekeeper (Paths tab, path modes), tenant-side PMS messaging (from Oct 21), Situs phones (Crescendo, MCC routing, on-call sync, the Monday maintenance turn-on), Yardi and ConAm, CI/CD speed and the break-glass lane, alert noise, ingest and the questionnaires, the meeting note-taker, Bitwarden, the admin shadow view, and the resident-questions digest (#24103). On Oct 30 Gera said the Situs leasing path modes are Fede's to set, and he wants to review every path with Fede.
Fede: "theres too many gates and too many layers where we re write the ai output sometimes with not enough context or false fires. that was designed for an older sonnet model. now that we are on more intelligent models, we want to rely on the model reasoning giving it all the right context, so 100 gates need to be inspected and refactored or removed." He said yes to the six-step order. Below, every check and rewrite layer gets one fate. Read only, main 9c971af59b.
| Keep | Context | Fold | Delete | Retry only | Total | |
|---|---|---|---|---|---|---|
| Checks that hold or block | 187 | 10 | 16 | 33 | 3 | 249 |
| Layers that rewrite the words | 11 | 0 | 0 | 22 | 39 | 72 |
So 123 of 321 distinct rows change or go. Most keeps are send rules and intake skips that never judge Clara's words.
| Family | Rows | Keep | Ctx | Fold | Del | Retry | Fires right / false (30d) | Proof for its PR | Size | Wave |
|---|---|---|---|---|---|---|---|---|---|---|
| H1 Supervised reply holds | 19 | 18 | 0 | 0 | 1 | 0 | 0 / 0 | Day 0 only | S | 1 |
| H2 Maintenance and renewal template holds | 8 | 8 | 0 | 0 | 0 | 0 | 0 / 0 | no PR, all keep | ||
| H3 Hold lifecycle | 12 | 11 | 0 | 0 | 1 | 0 | 0 / 0 | Step 1 Willows expiry re-alert | S | 1 |
| H4 First-email draft checks | 30 | 4 | 2 | 4 | 18 | 2 | 7 / 14 | Day 0; 356-card replay; 6 judged drafts | M, 2 PRs | 1 |
| H5 Tour time from this turn | 1 | 0 | 1 | 0 | 0 | 0 | 0 / 1 | Day 0; 3 false holds replayed with slot ids | S | 2 |
| H6 Main-loop draft checks | 18 | 2 | 1 | 8 | 6 | 1 | 20 / 14 | Day 0; 22 judged drafts replayed | M, 3 PRs | 1 |
| H7 Forced lookup retries | 8 | 3 | 3 | 0 | 2 | 0 | 3 / 7 | Day 0; 10 judged retries with slots and units in | S | 2 |
| H8 Turn silencers | 13 | 11 | 1 | 0 | 1 | 0 | 12 / 0 | Day 0; 12 right closers still silent | S | 1 |
| H9 Send-door refusals | 59 | 57 | 1 | 0 | 1 | 0 | 0 / 0 | Day 0 only | S | 3 |
| H10 Lead intake skips | 29 | 29 | 0 | 0 | 0 | 0 | 0 / 0 | no PR, all keep | ||
| H11 Cadence and reminder gates | 25 | 24 | 1 | 0 | 0 | 0 | 3 / 0 | Day 0; 2 cadence drafts with unit facts in | S | 2 |
| H12 Fair housing | 2 | 2 | 0 | 0 | 0 | 0 | 0 / 1 | no PR, all keep | ||
| H13 Hold could not be written | 3 | 3 | 0 | 0 | 0 | 0 | 0 / 0 | no PR, all keep | ||
| H14 Quiet hours | 7 | 6 | 0 | 0 | 1 | 0 | 0 / 0 | ledger only (already gone) | S | 1 |
| H15 Voice call gates | 7 | 7 | 0 | 0 | 0 | 0 | 0 / 0 | no PR, all keep | ||
| W1 Internal-text strips | 7 | 1 | 0 | 1 | 3 | 2 | 12 / 0 | Day 0; 6 stamp catches retried, never cut | S | 1 |
| W2 Draft salvage | 3 | 0 | 0 | 0 | 1 | 2 | 0 / 0 | Day 0; drafts that lost a link or price | S | 4 |
| W3 Hand-off rewrites | 7 | 3 | 0 | 1 | 2 | 1 | 5 / 3 | Day 0; 3 hand-off fires vs the open-ask record | S | 1 |
| W4 Voice and video rewrites | 21 | 2 | 0 | 0 | 3 | 16 | 41 / 38 (shared counts) | Day 0; recorder first, then call replays; 33 text drafts | L, per check | 4 |
| W5 Staff-facing rewrites | 7 | 0 | 0 | 2 | 1 | 4 | 0 / 0 | Day 0; add the stop record first | S | 4 |
| W6 Email template words | 7 | 1 | 0 | 0 | 6 | 0 | 0 / 0 | Day 0; 356-card greeting check; Fede rules on footer | S | 4 |
| W7 Bounce fallback | 1 | 0 | 0 | 0 | 1 | 0 | 0 / 0 | bounce fixtures | S | 4 |
| W8 Side writers to renters | 10 | 0 | 0 | 0 | 3 | 7 | 0 / 0 | Willows run per writer; add the stop record first | M | 4 |
| W9 Side writers to PMS and staff | 12 | 1 | 0 | 0 | 4 | 7 | 0 / 0 | Willows run per writer; add the stop record first | M | 4 |
| W10 Transport cosmetics | 5 | 5 | 0 | 0 | 0 | 0 | 0 / 0 | no PR, all keep |
"Day 0" means the replay grid stays byte-identical outside the cells the PR names. Fires before Oct 27 left no stored draft, so each PR says how many of its fires it could replay. S is under 200 lines, M under 600.
Correction, Nov 1: wave 2 deferred. Seven of the ten context rows need facts with ids in the main loop (later turns), which steps 2 and 3 built only for the first email; that input is being built first. C30 (no tour times offered) stays: on 357 first emails replayed on Opus it fires 34 times, about 6 of them real misses (all Situs), so it is not foldable until a prompt fix plus a bench run. C40 (fact trace) waits for door 4 to leave observe mode (wave 3).
Every row, with file, origin, fires and reason, is in gate-triage-ledger-2026-10-31.json (001 session).
Fede, Oct 31: "did we inspect what needs to happen to also bring the model to multi turn and cover all leasing funnel", "why we even have a separate multi turn prompt is an open item", "i want both agents to inspect that". Fable 5.1 and Opus 5.5 inspected independently (code on main, production turn snapshots, the draft-stop log, 15 real Western Slope and Situs threads with 47 later turns, read-only). They reached the same verdict.
Option A, one writer per channel, the turn as context. Keep the one prompt. The first-touch block renders whenever Clara has not written yet, cold email included. Every turn gets facts with ids, a calendar read, declared asks and one claims check. Then one model per channel, one PR per job. The Oct 30 first-email split is NOT merged as a second prompt (on today's inputs every arm, split or not, avoided all 6 of Fede's misses: the inputs were the cause); its other-job and duplicate cuts (about 15K characters) become the first trim of the one prompt, proven on the replay after the inputs land. Rejected: B, two prompts (23% fewer first-email tokens, two of everything to keep from drifting); C, swap later turns to Opus as they are (marginal bench 26/8/19, no Spanish, no booking turns, Opus without the facts it used well on turn 1).
| Job | Model today | What turn N lacks vs turn 1 | Bench | Before its Opus swap |
|---|---|---|---|---|
| First email to a guest card | Opus 5.5 (all customers since Oct 31 05:11Z) | n/a | 73 items + 356-card replay | Done |
| Cold prospect email, no card | Door decides: listing-site email from a new sender gets the Opus first-email path; anything else the Sonnet loop with no facts | facts, ids, calendar, declaration | none | First-touch block on every first touch |
| Later email replies | Sonnet 5, path shared with tenant emails | facts block (0 of 44 had it), code calendar read (9 of 44), declaration, ids, open asks | 53 real turns (WS 42, Situs 11, Spanish 0) | Prospect-only model path; facts on turn N; bench with Spanish and Situs slices |
| Later text replies | Sonnet 5, path shared with tenant texts | same | 0 graded | as email replies, plus the tenant/prospect split |
| First reply text (60 s after the email) | Haiku 4.5 by direct import, no model-path row | condenses the email | 44-item prototype | Model-path row; lowest risk |
| Tour times, booking, reschedule, cancel | the turn's model; Haiku reads first | code calendar read | 35 of 53 bench follow-ups could not book | Code calendar read on turn N; a bench that books |
| Confirmations, reminders, nudge to confirm | templates | n/a | none | nothing to swap |
| Quiet-lead texts and emails | Haiku 4.5, own prompt, no thread, no ids, bans specials | everything | timing only, no delivered text | Record its input (0 recorded today); fold into the channel writer |
| Application questions and link | the turn's model | as replies | 18 cases | as replies; Fede's Oct 31 Situs ruling (applicants get no reply) not yet on main |
| Reviewer-asked redraft of a held message | Sonnet 5, even for a held first email | outside the loop, unrecorded | none | same writer as the channel |
| Piece | Today | Missing | Step |
|---|---|---|---|
| One prompt builder | yes, every turn | nothing | done |
| One model path per prospect job | replies share a setting with tenant replies; two side writers read a tier alias, invisible to the swap mechanism | prospect-only reply paths; rows for the first-reply text and the quiet-lead writer | now (hygiene, no behavior change) |
| Facts with ids, every turn | first email only (#24125 fixed "open now") | every turn, cold email and quiet-lead too; ids on each fact line | 2 |
| This turn's calendar read | first email only | any turn that talks times; count a dispatcher read as this turn's read | 2 |
| Actions taken, as records with ids | a completed-actions block, no ids; automated texts reach the writer as Clara's own replies | ids; label automated texts; never the card-time instruction | 2 |
| Declaration | declared-asks tool, first email only | every prospect turn, email and text, with the ids used | 3 |
| One claims check | registry exists; fact trace on the first email only; three checks run twice on turn 1 | fact trace on every turn; special-to-home match (Situs fe4f838f: the offer landed on the wrong home through two human approvals) | 4 |
| Lookups by code, not forcing | claim-gate retries force a tool call; rejected on Opus | code-run reads | 4 |
| One model per channel | Opus first email; Sonnet replies and redraft; Haiku shortener and nudge | one setting per channel writer; swaps one PR per job, Fede's ratings as the bar, then the Willows end-to-end path | after 4 |
Fable 5.1 inspection Full text
# Leasing funnel on Opus: every writer, why two prompts, what moves as one
Fable reviewer, Oct 31 2026. Read-only: code on main (9c971af59b), production turn records by key (propflow-prod, Oct 24 to 31), prompts rendered offline.
## 0. Answer first
Every reply turn comes from one function (`getUnifiedSystemPrompt`); the FIRST email is the special case, the same prompt plus a first-turn pack (facts block, about 20 rules, code calendar read, declaration tool, second check stack, Opus 5.5). The one separate multi-turn writer is the quiet-lead nudge (row 7, Haiku, own 2,907-char prompt). Verdict: one writer per channel with the turn as context; close turn N's gap and fold the nudge in (2e, 3).
## 1. Map: every prospect-facing writer job
CM = `agents/clara/lib/agent/conversation-manager.ts`; CU = `clara-unified.ts`; MP = `platform/ai/model-paths.ts`; GFM = `leasing/generate-followup-message.ts`. Bench = items / Situs / Spanish. Loop checks = post-draft checks at CM:15243-16671, one retry then a hold.
| # | Job and where | Model | Writer gets | Checks and rewrites | Bench | Willows proof | Before Opus swap |
|---|---|---|---|---|---|---|---|
| 1 | First email to a guest card: `guest-card-engine-turn.ts:668` → CM loop, `inboundSource.kind = pms_guest_card` | Opus 5.5 (MP:44, live since Oct 31 05:11Z) | Card text; facts block (homes, verdict, alternatives, rent, special, slots), no record ids; slots read by code (CM:10103); ~20 overriding rules (CU:1181-1411); `declare_deferred_asks` | Loop checks, then fact trace plus 14 named checks; up to two retries | 73 / not stated / 0 | `guest-card-harness/real-send.ts` | Done |
| 2 | Direct prospect email, no card: `inbound-processor/handler.ts:1441`, `email-lead-engine-reply.ts:202` | All three customers carry `leadSource = listing_site_email` (production ORG rows): a new sender's listing-site email takes row 1 on Opus; a known person or plain email takes the Sonnet 5 loop, no pack | Row 1, or row 3 on an empty thread | Row 1, or row 3 | 0 | email the Willows mailbox as a sandbox persona | Moves with row 3 |
| 3 | Email follow-up reply: CM loop, CU:1449 plus the email overlay | Sonnet 5 (`emailReply`, shared with tenant emails, CM:3637) | Full thread; blocks for actions this turn, available units, open matter, cadence events, conversation and tour state. Missing vs turn 1: facts, ids, code calendar read, declaration tool, first-email rules, open asks | Claim gates with a forced-tool retry (CM:13063; `auto` on Opus 5.5); loop checks; escalation may swap in a template | 53 real turns (WS 42, Situs 11) / 11 / 0; no frozen set | `email-personas.ts --live` | Prospect-only path; forced retries replaced; bench pass (Oct 30-31: 26 better / 8 tie / 19 worse); facts on turn N |
| 4 | Text follow-up reply: same loop | Sonnet 5 (`textReply`, shared with tenant texts) | As row 3, no overlay; the router's tour pipeline (Haiku) can book before the model | As row 3 | 0 / 0 / 0 | text the Willows line from a tester number | As row 3 plus the audience split (tenant texts wait) |
| 5 | First reply text, 60 s after the first email (`activities/first-reply-text.ts:163`) | Haiku 4.5 by direct import, no MP row | The lead's message and the delivered email | Every number and time must appear in the email; one retry then a template | 0 | a Willows card with a registered phone and consent | MP row; lowest risk |
| 6 | Tour times, booking, reschedule, cancel: tools inside rows 1-4; pre-turn pipeline (`process-tour.ts:218`) | the turn's model | Turn 1: slots in the facts. Turn N: the model must call `get_tour_slots` | tour-time and booking gates (turn 2+) | inside rows 1 and 3 | `email-personas.ts p1_tour_saturday_confirm`, `p2_tour_reschedule_sunday` | Code calendar read on turn N; booking unproven on Opus (35 of 53 bench follow-ups could not book) |
| 7 | Quiet-lead nudge (+24 h, +48 h, +7 d, +14 d) and re-engagement (`prospect-outreach.ts:476` → GFM:711) | Haiku 4.5 by direct import (GFM:36), no MP row | Own prompt (GFM:554-665): no thread, "NO tour calendar", no ids, no rent except a verified drop | `followUpFinding` (dash, pricing, policy, slots, availability); one retry then held | 0; no recorded input | `email-personas.ts p8_goes_silent`, real schedule | MP row; input recorder; fold into the loop |
| 8 | Application questions: the loop, `send_application`, status and denied blocks | the turn's model | rows 3-4 plus the leasing block | loop checks | 0 | `email-personas.ts p5_application_link` | As rows 3-4; no code for Fede's Oct 31 Situs ruling (applicants get no reply) on main 1bf5423cb8 |
Not in the table: the escalated tour-booking-only turn (CM:18781) writes nothing a person reads; confirmations, reminders and application links are templates; voice is an ElevenLabs-hosted agent with its own prompt.
## 2. Why a separate multi-turn prompt?
### 2a. Rendered prompts
| Writer | Model | System prompt | Thread seen |
|---|---|---|---|
| First email | Opus 5.5 | 268,419 chars (~67k tokens), 23 tools | the card only |
| Email follow-up | Sonnet 5 | 249,589 chars (~62k), 22 tools | full |
| Text follow-up | Sonnet 5 | 223,055 chars (~56k) | full |
| Nudge | Haiku 4.5 | 2,907 chars, no tools | none |
99.5% of the follow-up prompt's words are in the first-email prompt (43,326 of 43,540); the first email adds 3,639 words, the follow-up 214. A production pair (8857096c, 1c981bfa) agrees: 27,385 chars only on the first email, 6,018 only on the follow-up.
### 2b. What is only on one side
| Only on the first email (28 lines) | Count | Belongs |
|---|---|---|
| Rules that read the facts block (opening names the home, gone home, unknown availability, short open list) | 10 | with the facts block, on any turn |
| General writing rules (price per size, money as fact, special as written, never volunteer a gap, book a requested open time) | 11 | shared prompt, every turn |
| First-touch lines (real greeting, one sentence on a PMS-made card, no-promises wording, declare the asks, tour times close the email) | 5 | keep, switched on "nobody has written back yet" |
| Booked-tour close and confirmed-tour exception | 2 | every turn |
Only on follow-ups: the specials hedge "on select apartments, restrictions may apply" (CU:815-822), retired from first emails by the code's own note (CU:1330-1337) for inventing a condition, against Fede's Oct 26 ruling; and the returning-person summary (CU:1992-1995), context. In production it rendered in 4 of 44 follow-up prompts and reached 0 bodies (2d): the one follow-up-only rule did nothing.
### 2c. Where the sides disagree
1. Same special, three rules: email 1 quotes it as written, email 2 may hedge it, the nudge may not mention it (GFM:569).
2. Tour times: the first email must offer real times; the nudge must never propose one (GFM:589-596).
3. A known person's new card gets both "pick up where you left off" and "never reference an earlier message".
4. Forcing: `modelAcceptsForcedToolChoice` (`model-tiers.ts:448`) rejects `claude-opus-5-5`, so every forced retry becomes `auto` plus a reminder (CM:3689); turn 1 reads the calendar in code, turn N depends on forcing.
### 2d. Production threads this week (ids only)
84 prospect writer turns on 34 Western Slope and Situs threads, all on Sonnet 5; 15 threads read turn by turn (42 of the 44 follow-ups).
| Input | Guest-card turn 1 (25) | Follow-ups (44) |
|---|---|---|
| Facts block with tour slots | 25 | 0 |
| Calendar read this turn | 4 by tool, 25 via the facts | 9 |
| Dispatcher "actions taken" note plus company home list | 7 | 29 |
| Specials hedge rule in the prompt / in the body | 0 / 0 (ban in 11 of 14 read) | 4 / 0 |
| Follow-up-only inputs: helped / hurt / nothing | | 8 / 8 / 26 |
Helped (8): 4 tours the dispatcher booked before the writer ran (4ffbe01d T3, 8ee909cf T3, b26a5299 T4, bdaed90a T2), 2 dispatcher calendar answers, 2 home-list answers. All records of something done.
Hurt (8): a named time misread as "more than one option" cost a round trip (3edfcfc7 T3, 1010d08d T3); the card-time "ask them for a date" note rode along for days (17 of 29, 3 while a tour was booked); a false "prospect created" note (fb2adfc2 T2); the home list sat behind all 3 unit-count rewrites, 0 of 39 turns without one; an application never sent: `send_application` got a typed address, not an id (a9a3b173 T4-T5); re-inquiries got no tour times (3aad8363 T3, 3edfcfc7 T6). The quiet-lead texts, which never see the facts, named a different home than the first email on 2 of 3 threads (b9274556, 303e8770). Every hurt is an instruction, a list, or a missing id.
Outside the prompt question: 5 Situs drafts were held with no review card ever created, so never sent (71fa5b88 T1, 2fb269b4 T1-T2, fb2adfc2 T1-T2); approved replies left 1 to 42 min after the turn, one after 9 h.
### 2e. Verdict
**One writer per channel, with the turn as context; the nudge prompt is the one to delete.** The follow-up is already the same writer (2a), its extra inputs helped only as records of done actions (2d), the week's worst misses were missing ids and facts on later turns (2d), and Opus makes those worse because forcing is ignored (2c).
| Option | Risk | Containment |
|---|---|---|
| One writer per channel | Moving the first email's rules onto replies without the facts they read is the regression path; proof is bench plus shadow, not a byte-identical replay | Context first (step B), rules second, one group at a time; today's first email is the bar |
| Keep two | Drift: the specials fix reached one side; the nudge bans what the first email requires; every fix costs two reviews and benches | Discipline only |
| Either | Three models muddy any "follow-ups are worse" reading | Compare both turns on one model first |
## 3. Shared pieces so the funnel moves as one
| Piece | Exists | Missing | Order |
|---|---|---|---|
| A. One model path per prospect job | MP rows `firstEmail`, `emailReply`, `textReply`; reply path by channel; rows 5 and 7 import `FAST_MODEL` | Prospect-only reply paths from the turn's identity; MP rows for rows 5 and 7, else a reply swap moves tenant replies | 1 |
| B. One context builder | Turn 1: facts and code calendar read. Turn N: eight blocks, each its own reader | Facts every turn; ids on each fact line (none today, `guest-card-home-facts.ts:699`); code calendar read on turn N; an open-asks block; "actions taken" as records with ids and results, never the card-time instruction | 2 |
| E. Lookups without forcing | `forcedToolChoiceFor` turns forcing into `auto` on Opus 5.5 | Replace claim-gate retries with code-run reads from B; count a dispatcher read as this turn's read | 3 |
| C. One declaration schema | `declare_deferred_asks` (max 8) on the guest-card email turn only | Same tool on every prospect turn | 4 |
| D. One claims check | Turn 1: fact trace plus 14 checks. Turn N: claim gates and loop checks; three run twice on turn 1 | Fact trace on turn N (ids from B); one list for both stacks; one retry budget | 5 |
| Swaps | first email done | one PR per job: email follow-up, text follow-up (after the split), first reply text, nudge (after a recorder); Fede's ratings as the bar, Situs and Spanish slices in every set, then the Willows path | 6 |
After steps 2 to 5 nothing in the code needs a second prompt; the nudge folds into the loop once B exists, so it gets the calendar and the special it is banned from today.
Opus 5.5 inspection (its "helped 5 of 5" count was corrected by its author to 2 helped, 3 nothing) Full text
# Leasing funnel inspection (Opus 5.5 reviewer), Oct 31
Read-only. Code: main ff9fe58645. Production: 7 turn snapshots diffed, the draft-stop log (Oct 27 to 31) and 15 multi-turn threads at Western Slope (WS) and Situs. Ids only.
**In short**
- **There is no separate multi-turn prompt in production; what is separate is the first email's inputs, checks and model.** On WS card 3060 the first email and the next email share 1,046 identical prompt lines; 48 are first-email only, 6 reply only.
- Only the first email gets the home facts, tour times read ahead, the declared-asks tool, its own word checks and Opus. Of 47 real later turns, 12 went wrong or missed the ask; none traces to a rule difference, since every later-turn rule also renders on turn 1 (2d, 2e).
- **Verdict: one writer per channel, with the turn as context.** The prompt already is one; "delete, not swap" means giving every turn the first email's inputs and checks, then one model per channel. Do not merge the first-email split as a second prompt.
## 1. Every prospect-facing writer job
**1a. The jobs**
| # | Job | Code (entry → writer) | Model today | Turn 1 vs later turns |
|---|---|---|---|---|
| J1 | First email, guest card | guest-card-engine-turn.ts → agent loop | Opus 5.5 (since 05:11Z) | Only turn with home facts (rent, special per listing, open now), tour times read ahead, stated budget, declared-asks tool, 18 first-email rules |
| J2 | Text right after the first email | first-reply-text-condense.ts | Haiku 4.5 | The sent email only |
| J3 | Cold email, no guest card | inbound-router.ts → agent loop | Sonnet 5 | A first touch run as a reply: no facts, no tour times, no declared asks |
| J4 | Later email replies | agent loop | Sonnet 5 | Thread (80K tokens), unit list without specials or ids, completed actions, tour state; calendar and specials by tool call only |
| J5 | Later text replies | agent loop, no email clauses | Sonnet 5 | Same as J4 |
| J6 | Tour times and booking | J4/J5 tools; Haiku reads run first | Sonnet 5 writes; Haiku 4.5 reads | Turn 1: times read in code. Later: the model must call the tool |
| J7 | Confirmations, reminders, unconfirmed nudge | tour.ts, send-tour-outcome-sms.ts | none: templates | n/a |
| J8 | Reschedule, cancel, decline | template, then the J4/J5 reply | Sonnet 5 | as J4/J5 |
| J9 | Quiet-lead texts and emails | generate-followup-message.ts (two scheduled callers) | Haiku 4.5 | Fields only: no thread (went-quiet gets 4 messages), no rent, no tour times; its prompt bans specials |
| J10 | Application questions and link | J4/J5 reply; link sends are templates | Sonnet 5 | as J4/J5 |
| J11 | Reviewer-asked redraft of a held message | review-redraft.ts → read-only-leasing-draft.ts:46 | Sonnet 5, even for a held first email | Outside the loop, unrecorded |
Out of scope: voice, video, staff desk.
**1b. What proves each job**
| # | Checks fired, WS + Situs, Oct 27 to 31 | Input recorded | Bench | Willows run; before Opus |
|---|---|---|---|---|
| J1 | first-email checks 5 (all sent after a retry); dashes 1, no-tool-call 1 held; Situs review holds 7 | yes | v1 set 73 (Spanish 4); 356-card replay 161/139/56 better/tie/worse | card 365 on Opus today; no customer email read yet |
| J2 | none logged | no | 44-item prototype | none; record input first |
| J3 | 7 stops, all held (Situs) | yes | none; the door cannot rebuild it | email-stress; needs first-touch input |
| J4 | tour time without a calendar read 3, unit count 2, booking promise 1, promise backing 1; Situs review holds 6 (1 approved) | yes, since Oct 24 | 53 real later turns (WS 42, Situs 11, Spanish 0): Opus 26/8/19; frozen reply set empty | email-stress; no Opus run from times to confirmation |
| J5 | silence 3, dashes 1, weekday 1; Situs hold 1 | yes | inside the 53; 26 shadow pairs | sms-stress; as J4 |
| J6 | inside J4 | Haiku reads no | bookings blocked: 35 of 53 lack the confirmation reply | sms-stress tours; needs a bench that books |
| J7 | n/a | n/a | none | tour-confirm harness; nothing to swap |
| J8 | inside J4/J5 | yes | none separate | reschedule gauntlet (16); rides J4/J5 |
| J9 | copy check 2; 15 stops; Situs holds 9; the went-quiet path logs nothing | no | 39 real touches; 155-item writer set, 80 synthetic | timing only, no delivered text; record input, settle the specials ban |
| J10 | inside J4/J5 | yes | 18-case dataset | persona p5; rides J4/J5 |
| J11 | not logged | no | none | none; same writer as J1/J4 |
J4, J5, J6, J8 and J10 share two model settings, so they move together. Several Willows harnesses still email as Fede (Oct 27 rule).
## 2. Why a separate multi-turn prompt? There is none
**2a. What differs between turns** (WS card 3060, conversation 1010d08d, Oct 30, prompt version 26b72399b7)
| Block, characters | First email | Later email | Later text |
|---|---|---|---|
| Leasing rules (every later-turn rule lives here) | 187,288 | identical | identical, less 910 phone lines |
| Home facts: special per listing, open now, tour times | 1,053 | none; unit numbers only (specials only via the unit lookup) | none |
| 18 first-email rules, which "override anything below that disagrees" | 14,902 | none | none |
| Email clauses | 29,658 | 24,076 | none (text rules instead) |
| Whole system prompt | 271,561 (about 95K tokens) | 250,194 | 225,720 |
| Declared-asks tool | yes | no | no |
| Checks on the words | loop checks plus fact trace, draft checks, body guard, retry then hold | loop checks | loop checks |
| Model | Opus 5.5 | Sonnet 5 | Sonnet 5 |
Snapshots tsnap_29c42a08, tsnap_703caa57, tsnap_878096f4. Situs pair tsnap_9dae7e08, tsnap_3c72eb70: same shape.
**2b. How it got here.** Two merges made it one prompt: one email core for every prospect email (#9966, Sep 19: "so a reply in an ongoing conversation reads consistently with the first email"), then the old first-email composer deleted (#21112, Oct 19). The Oct 30 split cut the first email by 23.6%, yet on today's inputs every arm, split or not, avoided all 6 of Fede's misses: the inputs had been the cause.
**2c. What later turns miss, with records**
1. Situs fe4f838f, Oct 30. The facts tied "6 weeks free, about $1,349" to the open 2-bedroom at $1,525; the first email put the offer on the $1,393 home. No check fired: the fact trace asks whether a fact has a source, not which home it belongs to. It passed two human approvals, the email and the next text (eschold_539542e7, in 27 s); that text copied the offer from the thread and AppFolio delivered it (message 1172147801).
2. WS 1010d08d turn 2: the prompt hands later turns a list of open units while the rules say name one home, never how many are open; the draft named two (dstop_de38778d).
3. Cold emails are first touches run as replies: 3 of 6 new Situs email threads this week, 7 stops, all held, none answered.
**2d. Later-turn rules.** 16 rules (23,177 characters) apply only once a thread exists (tour booked, reschedule, staff replied, goodbye); all 16 already render on the first email too.
**2e. Real later turns** (15 threads, 47 later turns: WS 39, Situs 8, all Sonnet; Situs ran supervised, so only 2 Clara-written later replies reached a renter there.)
| Later-turn rule or check | Helped | Hurt | Nothing or missed | Strongest ids |
|---|---|---|---|---|
| Goodbye: close without a reply | 5 | 0 | 0 | a74cc053 |
| Staff run the thread: stay quiet | 0 | 0 | 2 missed | 9cc7dc8f, bbc825d1 drafted anyway; a check held them |
| Forced calendar read, weekday guard, booking promise | 2 | 0 | 3 | ded19e70 caught a wrong weekday; bfaae842 blocked a true "lock in" |
| Unit count, dash retry, tour time this turn | 1 | 3 | 2 | d5953add: the dash retry sent "go ahead and head in" |
| Tour handler line "named more than one option" | 0 | 2 | 1 | f888234d: a bare "10am" falls to the unclear-ask branch (process-tour.ts:2091); the model called it odd, then re-asked |
| Promise backing | 2 | 0 | 3 (2 missed) | ef38ed96 sent "keeping an eye out" |
32 were right; none of the 12 bad ones traces to a rule difference between turns. Causes: checks and code-written lines 4, holds with no record 3 (one after a false check fire), a failed application lookup 1, the first email's wrong rent carried forward 1, the model 3. The quiet-lead writer (J9), the one truly separate follow-up prompt, got 4 of 4 wrong: three sent emails (3aad8363) claimed interest in a home Clara had added, two introducing Clara as "with" that home; the fourth (fb2adfc2) re-asked what the renter had answered. No check caught the problem.
**2f. Verdict**
| Option | What it means | For | Risk |
|---|---|---|---|
| **A. One writer per channel, turn as context (recommended)** | Keep the one prompt. The first-touch block renders whenever Clara has not written yet, cold email included. Every turn gets facts with ids, a calendar read, declared asks and one claims check. Then one model per channel | Already one prompt; no bad later turn traces to a rule difference (2e) | First email keeps about 23% more tokens. Later turns wait for inputs before Opus |
| B. Keep two: merge the split as the first email's own prompt | Two prompts, benched and swapped separately | 23% fewer first-email tokens | No gain on Fede's cards; two prompts to edit, bench and keep from drifting |
| C. Swap later turns to Opus as they are | One model line | Fastest | Marginal bench (26/8/19), no Spanish, no booking turns; later turns lack the facts Opus used well on turn 1 |
Recommend A. Keep the split's other-job and duplicate cuts (about 15K characters) as the first trim of the one prompt, proven on the replay after the inputs land. J2, J9 and J11 are the funnel's real separate prompts; under A each folds into its channel's writer.
## 3. Shared pieces, in Fede's six-step order
| Piece | Today | Missing | Step |
|---|---|---|---|
| One prompt builder | yes, every turn | nothing | done |
| Facts with ids (rent, special per home, open now) | first email only (guest-card-home-facts.ts); later turns get a unit list | every turn, cold email and quiet-lead texts too; ids in the text | 2 (running) |
| This turn's calendar read | first email only | any turn that talks times | 2 |
| Actions taken, with ids | completed-actions block every turn, no ids; nudges and the first-reply text reach the writer as Clara's own replies | ids; label automated texts | 2 |
| Declaration | declared-asks tool, first email only | every turn, plus which ids were used | 3 |
| One claims check | registry exists (claims/facts.ts, promises.ts); fact trace on the first email only | fact trace on every turn; special-to-home match; later-turn gates fold in | 4 |
| One model per channel | Opus first email; Sonnet replies and redraft; Haiku shortener and quiet-lead writer | one setting per channel writer | after 4, one PR per job |