How Clara asks the team for help, hands a customer to a person, and understands what staff write back. Honest state first, then a phased plan where each phase is gated by the previous phase's proof.
Proposed — pending Fede's decisions (2026-09-28) · Evidence: staff loop review (Steven / Kat, Western Slope, 9/27–9/28) · QA script: Fede-as-Kat at Willows
Bottom line: the staff side of Clara is buggy today. It was built for one kind of escalation ("ask the team, save the answer") and never exercised, because Camellia staff never replied to Clara. Western Slope is its first real use, and the first real reply (Kat: "I called them") broke it. The fix is four phases, each gated by the previous phase's proof (harness green, QA pass); Fede can turn it on at Western Slope once Phase 2 passes, then it bakes on real traffic. What limits speed is judgment and testing, not typing.
| Phase | What | Gate |
|---|---|---|
| 0 | Stop the bleeding (bug fixes, live) | none |
| 1 | The two paths (dark) | Phase 0 green |
| 2 | Prove it (harness, Fede-as-Kat QA) | Phases 0–1, corpus mining |
| 3 | Staff memory (dark) | Phase 2 baked |
Seven defects, all seen on one real thread. Four are code bugs. Three are wrong assumptions in the design, which is why patching keyword lists will not hold.
| # | Defect | What Kat (or Fede) saw | Root cause |
|---|---|---|---|
| 1 | A keyword list decides what a staff reply means; only two outcomes, "answer" or "question" | "I called them… will find out when someone will be down there" was read as a question | wrong assumption every reply is an answer or a question |
| 2 | Clara answers staff by running the customer's own conversation | Got a reply written to Steven, not to her; her words landed in Steven's thread | wrong assumption customer thread doubles as staff thread |
| 3 | Fixed re-ask wording ("So — what should I tell them about…?") | Asked again after she said she had it | bug |
| 4 | Expiry guessed from dates in the question text, and only runs at two test properties | An ask about a finished 10:00 tour stayed open | bug + wrong design (not linked to the tour) |
| 5 | Nags never stop because someone took it; the "human owns it" state exists but nothing enters it | Kept getting reminders | wrong assumption "takeover is rare" built as "never" |
| 6 | Clara escalated something she could answer (company video-tour link) | Pulled into a question Clara had the answer to | bug no "can I answer this myself?" check |
| 7 | Conversation page labels Kat's reply "Staff asked Clara"; nothing shows that staff replied | Fede only noticed Kat replied by accident | bug label ignores direction; matter owner not shown |
Most important rule: escalation is about a matter, never the person. Clara keeps helping with everything else; she never brings up the pending matter unless the customer does.
"The team is still looking into it" is said only when the customer re-asks about that same matter and it's truly still open. Once staff answered, the tour moved, or the topic is over, Clara never mentions it again — no per-turn reminders. Real past transcripts where Clara got stuck or kept repeating that line become harness fixtures.
14 real cases in 9 conversations (12 Camellia, 2 Western Slope) were pulled from production to build this model.
| Case / Property | Date | What Clara did | What a human would do |
|---|---|---|---|
| Voucher answer walked back, Camellia | 2026-08-28 | Two minutes after correctly telling a prospect vouchers are accepted, Clara took it back and said she had no answer on file. | "That's great — go for it. And yes, we accept housing vouchers, so you're all set to apply." |
| Repeating the same line, Camellia | 2026-08-28 | Clara said "I've checked with the team... I'll follow up tomorrow either way" three times in six minutes, even after the prospect said "no need to repeat." | "You're welcome!" |
| Silence at the door, Camellia | 2026-08-29 | A prospect standing outside asking for the entry code got no reply, because an unrelated open promise had the whole thread on hold. He had to call in. | "It's [code]. Enter it at the main entrance, and the office is on the right." |
| West-facing unit question, Camellia | 2026-09-03 | A prospect who had moved on to touring a specific unit still got "still no word on the west-facing / floor 3+ question" tacked onto four unrelated replies in a row. | Just book the tour and let him in: "See you at 4:45. The buzz code is [code] and the office is on the right." |
Three causes, in plain words:
These fixes are shipping as bug fixes, live, no gate; the 14 cases above are now permanent test fixtures so they never come back.
The unit is the matter: one thing Clara is stuck on for one customer. When stuck, Clara judges which path applies using reasonable defaults, never a hard-coded list. Hand-off: a person must act (show up, get on a call, sign, negotiate, calm someone down). The guest card is assigned to that person, Clara steps back, no nags. The customer hears only Clara's own follow-up line. Knowledge question: a policy, process or price Clara lacks. She asks, keeps serving the customer, and saves the answer to the knowledge base with who said it.
Clara handles the customer
│ stuck
▼
┌─ judge (defaults, not a list) ──────────────────────────────┐
│ knowledge q. → ask team ──► WAITING_ON_STAFF (one reminder) │
│ hand-off → assign card to person ──► HUMAN_OWNED │
└─────────────────────────────────────────────────────────────┘
staff reply on WAITING_ON_STAFF, judged by meaning:
answered → relay to customer + save to KB → RESOLVED
has it → thank once, stop nags → HUMAN_OWNED
question back → answer in the STAFF thread → WAITING_ON_STAFF
declined → tell customer Clara's own line → RESOLVED
out of office → ask the next person in chain → WAITING_ON_STAFF
unrelated → no transition, no re-ask
HUMAN_OWNED → back to Clara only when the customer writes again
any state → EXPIRED when the linked tour / appointment has passed
No staff typing as Clara, no new staff screen.
One reminder per matter, ever. If the prospect asks again, Clara tells them the team is still looking into it — she does not re-ask staff.
If the answer was already in the knowledge base or settings, Clara was never stuck; a case like that is a red harness case against her answering, fixed there, never routed.
Rules kept from earlier rulings: staff talk to Clara like a person, over email first, with no new staff screen. Clara keeps working while she waits. She never promises on a named person's behalf. Staff answers become property policy directly, with provenance. No review queues, no engineering flags.
Temporal is the engine we already use to keep long-running processes, such as a booked tour, alive for days and wake them when something happens. The workflow stays alive after a hand-off, so while a person has the matter we still know who owns it and keep every event. This extends today's escalation-matter workflow rather than starting a new one, the same pattern the renewals process already uses.
Where the code lives: one directory holds the workflow, its events (signals), its timer and its steps (activities). The matter row in the database is written by the workflow, as a copy for the screens to read. Every entry point feeds the one workflow; nothing keeps its own copy of a decision (Fede's rule, 2026-09-28). Once the timer owns the reminder, the 9 AM reminder job goes away.
The two judgments (stuck, and what a staff reply means) are LLM calls to Claude. They run only when Clara is stuck or when staff reply, a few calls a day, so quality matters more than cost.
| Model | Reproduced incidents (25) | Other real cases (17) | Decisions filed as questions | Clears the bar |
|---|---|---|---|---|
| Sonnet 5.5 | 25/25 | 17/17 | 0 | yes, picked |
| Sonnet 5 | 25/25 | 16/17 | 0 | no |
| Opus 5.5 | 25/25 | 17/17 | 0 | yes, costs more |
| Haiku 4.5 | 25/25 | 16/17 | 0 | no |
The bar: 100% on the reproduced incidents, at least 95% on the rest, and zero decisions swallowed as questions.
Sonnet 5's one miss sent an application-process question to a person instead of treating it as a fact question, the safe direction.
The judge is not called from the live path until the hand-off step routes on its verdict; a compute-and-log-only mode was rejected as shadow mode.
Today's default for replies: Clara's email and text replies to prospects use Sonnet 5 (claude-sonnet-5, the drafting tier in model-tiers.ts, high effort); other turns use the general tier, Sonnet 4.6.
scripts/coworker-harness. It runs on every PR that touches a judgment.52 real cases where Clara asked for help: 26 written questions and 26 missed-call callbacks. Most are from Camellia in August; 3 are from Western Slope. Test runs are counted separately.
| Reason | Count | Share |
|---|---|---|
| A fact she should have had (knowledge, listing, standard process) | 14 | 54% |
| A person's judgment, an exception, or a wrong document | 8 | 31% |
| Customer asked for a human | 2 | 8% |
| Clara promised something she couldn't do | 1 | 4% |
| Vendor message | 1 | 4% |
| Staff response | Count | How Clara handled it |
|---|---|---|
| Client staff answered through Clara's reply loop | 0 | Never happened at a real customer |
| PropFlow operator answered, typing as Clara | 9 | Fine for the customer; answer never saved as knowledge |
| Client staff answered the customer directly | 3 | Clara never saw it; kept forwarding |
| Staff said "I've got it" | 1 | Read as a question; Clara replied to the manager as if they were the prospect |
| Live transfer connected | 1 | Fine |
| No visible reply ever | 12 | one reminder, then silence or a quiet close |
| Measure | Value |
|---|---|
| Avoidable (answerable from knowledge, listing or process) | 14 of 26 (54%) |
| Never answered by anyone, visibly | 12 of 26 (46%) |
| Customer left without an answer | 10 of 26 (38%) |
| Missed-call pages with a recorded callback | 0 of 26 |
| Median time to first human reply, when one came | about 6 h (11 cases; 9 by PropFlow operators) |
| Real staff replies that followed a reminder | 0 |
| Reason | Staff response | What Clara did | What a coworker should do | Verdict |
|---|---|---|---|---|
| Fact she should have had | any | Forwards, says "the team will follow up" | Answers from knowledge; if truly missing, asks once and saves it | WRONG |
| Fact | Answered | Relays once | Relays and saves as a property fact | MISSING |
| Judgment or document | Never replied | one fixed reminder, then silent close | Reminds against the customer's deadline, tries a second person, tells the customer honestly | WRONG |
| Judgment or document | Replied from own mailbox | Doesn't see it, keeps forwarding | Treats the mailbox reply as the answer and closes | MISSING |
| any | Customer writes again while waiting | Forwards every message, tells the customer nothing | Gives a status line, answers what she knows, recognises "done" | WRONG |
| any | "I've got it" | Treats it as a question or answer | Marks it human-owned, stops reminders | WRONG |
| any | Out of office / "let me check" | Would relay it as the answer (offline test) | Keeps waiting, routes to backup | WRONG (test only) |
| Exception | Declined with an alternative | Relays both | Same | OK |
| any | Staff asks back | Answers from records, stays open | Same, with a cap on rounds | PARTLY |
| Complaint or policy | Answer relayed, customer pushes | Took back the approved answer | Holds to the approved answer | WRONG |
| Asked for a human (phone) | Transfer unanswered | One email per call, no callback tracking | One page per caller, callback window texted, tracked to done | MISSING |
| Vendor or billing voicemail | – | Pages the leasing inbox | Routes billing to billing, property calls to the owner | PARTLY |
| Staff's own question | – | Escalates it back to staff | Answers from records or says she can't | WRONG (test) |
| Plain tour request | – | Escalates it | Books it | WRONG (test) |
The 10 most instructive real threads are saved, scrubbed, as harness fixtures.
The case. Kat texted Clara from her personal cell and Clara did not recognise her. On 9/20 someone entered Kat's work number (the office landline) in the team page's single phone field. The code treated that field as "replace": saving the work phone replaced her mobile and retired it. The landline receives texts but cannot reply.
| Slot | What it is | Kat |
|---|---|---|
| Work phone | The number on the team page; callers and the office line. May be a landline. | …8394 (landline, cannot reply) |
| Mobile / personal | A phone that can text back. | …8120 (Clara texts here) |
In storage terms: two live number records on the person, each labeled work or mobile; the existing "primary" flag means "text me here".
| Piece | Today |
|---|---|
| Storage | Each number is a record on the person with a primary flag (the number Clara texts) and a retired flag. Several live numbers are allowed; there is no work / mobile label. |
| Recognition | Reads all live numbers, so a second live number is already recognised as the same person. |
| Team page / self-service save | root cause one phone field; saving the work phone replaced her mobile and retired it. |
| Tour check-in ask | Texts the primary; its thread is keyed by that phone, so a reply from her other phone lands elsewhere. |
| Staff tour-alert texts | Go only to the mobile typed on the opt-in row. |
| PR | Changes | Ships |
|---|---|---|
| 1 | Saving the team-page phone never retires the number already on file: the saved number becomes the one Clara texts, and the old one stays live as a backup | live (bug fix) |
| 2 | Team page gets two fields, Work phone and Mobile, each saved on its own; Clara texts the mobile when there is one | dark |
| 3 | Optional: the check-in ask texts the number she booked from, and joins replies from either of her numbers | dark |
No landline detection. Nothing beyond the two slots.
No conflict with Gera's portfolio model: both numbers live on the one person record in the company. Caveat: recognising her by an old, already-retired number company-wide needs a company-wide staff role.
Every row is one PR, one concern, about 300 lines or fewer, with a harness fixture and a mutation control (plant the bug back, the check goes red). Each phase starts when its dependencies are merged and proven. Bug fixes ship live everywhere; behavior changes ship dark behind a property setting, default off, and Fede turns them on.
For Kat: "I called them" ends the reminders. Her questions get answers addressed to her. Asks about a past tour close on their own. The conversation page shows she replied and who owns the matter now.
| PR | Changes | Depends on | Proof (+ mutation control) | Ships |
|---|---|---|---|---|
| 0.1 | Wire the "human took over" transition; 9 AM reminder job skips human-owned matters | — | Kat replay: zero reminders after takeover (control: remove the skip → red) | live |
| 0.2 | Staff reply read by a model step into the six reply types; replaces the keyword list | 0.1 | Kat's reply → has it (control: put "find out" back as a rule → red) | live |
| 0.3 | Answer staff questions in a staff context, never through the customer's conversation | — | Zero writes to Steven's thread (control: route via customer thread → red) | live |
| 0.4 | Re-ask only after a real question back | 0.2 | No re-ask on "has it" (control: always re-ask → red) | live |
| 0.5 | Matter expiry on for all properties (drop the test-only list) and linked to the tour record | — | Matter on a finished tour → expired (control: restore date parsing → red) | live |
| 0.6 | Clara sends the company video link herself, including around a tour already in progress | — | 10:39 question on a 10:00 tour gets the link, no escalation (control: drop the rule → red) | live |
| 0.7 | Conversation page labels by direction and intent ("Kat replied to Clara", "Kat asked Clara", "Clara answered Kat", "Clara asked the team"), drawn from the matter's own transitions; shows who owns the matter now | 0.2 (0.3 makes it correct at the source) | Rendered label on the Kat thread (control: label by sender only → red) | live |
| Gate: none; bug fixes, each ships live when its harness fixture and control pass. | ||||
For Kat: when a customer needs a person, the guest card lands on her name in AppFolio with one email, and Clara stops talking about it. She is never asked the same kind of question twice once she has answered it.
| PR | Changes | Depends on | Proof (+ mutation control) | Ships |
|---|---|---|---|---|
| 1.1 | Escalation-time judgment: knowledge question / hand-off (an answer already in knowledge or settings is a bug in her answering, not a third outcome), from defaults (presence, signing, negotiation, upset → hand-off; policy, process, price → question) | 0.2 | Steven question → hand-off, never a knowledge question (control: force "question" → red) | dark |
| 1.2 | Staff person → AppFolio user map, pulled from AppFolio (none exists today) | — | Kat maps to her AppFolio user at Willows (control: blank map → red) | dark |
| 1.3 | Default hand-off person per property, a property setting on the property page | 1.2 | Setting read back on the page | dark [superseded 2026-09-29, see decisions] |
| 1.4 | On hand-off, assign the guest card to that person (the assign write already exists); re-assign to whoever says "I've got it" | 1.2, 1.3 | Card owner changes at Willows (control: skip assign → red) | dark |
| 1.5 | Hand-off opens human-owned directly: one email, no nags, one customer line from Clara, no name | 0.1, 1.1 | One staff email, one customer line, zero nags (control: open as waiting → red) | dark |
| 1.6 | Centralize: one generic matter engine for all modules (module + subject record; leasing first, renewals/maintenance/collections add routing rows) — the refactor task | 1.1–1.5 | Leasing runs on the generic engine, no leasing-only code path left (control: hard-code leasing → red) | dark |
| Gate: Phase 0 fixes merged and green on the harness; 1.2 and 1.3 before 1.4. | ||||
The harness first pass (a results table, each case red/green on main) gates Fede's own QA as Kat; Fede does not test until that table is green.
For Kat: nothing changes until the replays and Fede's own run as Kat pass. Then she gets the Phase 1 behavior at Western Slope, with staff-driven relays reviewed until they are proven.
| PR | Changes | Depends on | Proof | Ships |
|---|---|---|---|---|
| 2.1 | Ten corpus threads as fixtures on the coworker harness (extension already in progress) | corpus mining, 0.x, 1.x | All ten green; each with a planted-bug control | test only |
| 2.2 | Fede-as-Kat QA at Willows (real email, real guest card) | 2.1 | QA checklist on the go-live tracker, with permalinks | test only |
| 2.3 | Review gate on relays triggered by staff replies at Western Slope, until baked | — | Held relay appears in the review loop | setting |
| Gate: Phase 0 and 1 merged, corpus mining done; then bake on real Western Slope traffic. | ||||
For Kat: she can go back and forth with Clara over several messages, like a coworker who remembers yesterday's conversation.
| PR | Changes | Depends on | Proof | Ships |
|---|---|---|---|---|
| 3.1 | A Clara ↔ staff conversation per staff person, with its own memory; the matter is the shared object | 0.3 | Staff transcript never touches the customer's (control: shared store → red) | dark |
| 3.2 | Staff replies routed into that conversation; Clara uses its history | 3.1 | Multi-turn replay recalls an earlier answer | dark |
| 3.3 | Staff ask-back rounds: several questions both ways on one matter | 3.2 | Three-round fixture resolves correctly | dark |
| 3.4 | Multi-turn harness fixtures | 3.3 | All green with controls | test only |
| Gate: Phase 2 proven and baked, and real multi-turn staff traffic to test against. | ||||
Phases 0 and 1 overlap; Phase 2 is gated by the corpus pass and Fede's QA run; Phase 3 waits for real multi-turn staff traffic.
A hand-off goes to a role, picked by the type of task, never to a hard-coded person. The company's Team page and its role dropdown are the only source of people and roles. There is no separate list of roles or people; Clara looks at the team and its roles and delegates from there. We ship opinionated defaults; a client can tweak them later.
We use only the roles the Team page has today: Property Manager, Leasing Agent, Company Admin. No new roles now. At Western Slope: Kat and Beverly are leasing agents (Kat does the showings, Beverly does computer work), Cheryl is the property manager, and Kat is the default person.
| Task type | Default role now | Later | Example |
|---|---|---|---|
| Tour logistics | Leasing agent (Kat, the default person) | same | Prospect running late, wants to reschedule, no-show, lockbox trouble, something happened on site |
| Paperwork / application admin | Leasing agent | could point at Beverly as the default for this task; still no new role | Application stuck, missing document, wrong form sent |
| Knowledge or policy question Clara can't answer (answer saved to the knowledge base) | Leasing agent | same as paperwork | "Do you take cosigners?", "Is the yard fenced?" |
| Exception or approval | Property manager | same | Waive a fee, hold a unit, special lease terms |
| Renewals | not in chunk 1 | Property manager (negotiation, exceptions) | Tenant pushes back on the renewal rent |
| Maintenance | not in chunk 1 | Emergency: on-call line or call center. Non-emergency: maintenance coordinator, else property manager | Burst pipe at night; a slow drain |
| Collections (not offered yet) | not in chunk 1 | Property manager, or accounting | Late rent conversation |
| Anything else | The property's default person | same | At Western Slope: Kat |
The long-term role list the design scales to. Rows marked "later" are future only; nothing is added to the dropdown now.
| Role | On the Team page today? |
|---|---|
| Owner / principal | Later (closest today: Company Admin) |
| Property manager | Yes |
| Assistant property manager | Later |
| Leasing agent | Yes |
| Leasing assistant | Later |
| Maintenance coordinator / technician | Later, with the maintenance chunk |
| Accounting / bookkeeping | Later, with the collections chunk |
| Virtual assistant / admin | Later |
Chunks: the design is the whole matrix; we build it one module at a time. Chunk 1 is leasing only, which is what we are tuning now: with today's roles only: tour logistics, paperwork and knowledge → leasing agent; exceptions → property manager; anything else → the default person (Kat at Western Slope). Later chunks are renewals, maintenance and collections; each adds its rows to the same matrix and, if needed, its roles to the same dropdown.
How a client tweaks it: later, a section on the company settings page, working title "Human in the loop", with one small table: task type → role. People still come only from the Team page. Left alone, it uses our defaults. Defaults ship first; this section is not built now. It follows the building-then-company settings order in Gera's portfolio design: the company sets the matrix, and a building's own default person wins for that building. [superseded 2026-09-29, see decisions]
One naming note: these are job roles, separate from the access roles in Gera's design (org admin, property owner, PMS credential holder). The Team page dropdown mixes the two today (Company Admin sits next to Leasing Agent); they must stay distinct in the model.
Short answer: there is a simple, shared model, and ours already matches most of it. Two things differ: how often we remind, and how module-specific the matter is. (Research 2026-09-28.)
The matter is the same record in every module: module · subject record · task type · owner (role → person) · what the human gets · what the customer hears · when it returns to Clara. Only the subject record and the AppFolio surface change.
| Module | Subject record | Typical hand-off → role | Human acts in AppFolio | Back to Clara when |
|---|---|---|---|---|
| Leasing | Guest card | Tour problem, exception → leasing agent | Guest card assigned to them (Phase 1.4) | Customer writes again |
| Renewals | Lease renewal offer | Rent pushback, special terms → property manager | The renewal offer; AppFolio itself keeps staff on renewal offers (Realm-X) | Offer sent or customer writes again |
| Maintenance | Work order | Emergency, access, vendor dispute → on-call line or coordinator | Work order assigned to a tech or vendor (AppFolio) | Work order status changes |
| Collections | Tenant ledger balance | Payment plan, dispute → property manager | Tenant ledger; Colleen escalates to the manager after its own follow-ups (Colleen) | Payment posts or customer writes again |
Repeating the same reminder backfires: acceptance "dropped by 30% for each additional reminder" (Ancker 2017). Incident tools notify once, then move to the next person after a timeout (PagerDuty); help desks remind before the deadline and escalate after it (Freshdesk). So: not three reminders. One ask, one reminder timed to the customer's deadline, then the next person, then an honest line to the customer.
We built pieces of this loop several times: turnover texting for property managers, the promise ledger, the Camellia property-manager experiment, a reminder system for property-manager tasks, and a coworker test run. None became the product, but three of them solved the hard part, knowing which matter a staff message is about.
Today Clara knows a staff member's name and role, nothing else. Their questions and answers are stored inside the customer's conversation (the cause of Kat's reply landing in Steven's thread). No setting says "text me, don't email", and nothing lists the matters open with one person. The engine gets a thread per staff person, a list of their open matters kept current by the matter workflow, and preferences that only the person or the Team page can change.
Today staff messages show as collapsed rows on the customer's conversation, labelled by who wrote them only. There is no matter page, and the workflow keeps counts, not events. Needed: an ordered event list in the workflow, one matter view with owner and timeline, and a "with Clara" list on each person's Team page entry.
| Piece | Today | Verdict | Why |
|---|---|---|---|
| Open-asks-per-person list (built for turnovers) | live, turnovers and renewal letters only | move in | Becomes each staff person's open-matters list |
| "Which matter is this?" ladder (turnover texting) | live, texts only | move in | Last message Clara sent → only open matter → one question |
| Email reply matching by headers | live | keep | Exact; the email rung of the same ladder |
| Turnover "Conversation with Clara" panel | live, never used by a client | move in | The idea becomes the matter view |
| Staff answer saved as knowledge | live | move in | The knowledge step |
| Property-manager task reminders, "forwarded question" kind | live | move, then delete | A second reminder system chasing the same forward |
| Daily 9 AM reminder job | live | delete | Replaced by the matter's own timer |
| Keyword list for staff replies | live | delete | Replaced by judgment |
| Four separate on/off switches for this loop | live, off | fold, then delete | One company switch: Human in the loop [superseded 2026-09-29, see decisions] |
| Coworker test run (unmerged) | dead | delete | Proved the text loops; timeline idea moves to the matter view |
| Duplicate test harnesses (escalation, human-in-the-loop) | tests only | fold, then delete | One harness for one engine |
| Staff texting tools (book, move, tour notes) | live | leave | Staff tools; they read the engine's matter list |
| Promise ledger | live, test property only | leave | Customer promises; already opens matters through the engine |
| Other property-manager reminders (applications, countersigns) | live | leave | Not the staff loop |
Fede, 2026-09-29: "I think we should remove all the switches and just have it on" and "there shouldn't be lines where it's not on." Every on/off switch on the staff loop has been deleted (all merged 2026-09-29) so the behaviour is simply on everywhere; only the coworker-mode switch remains, pending Fede's yes/hold.
| Switch (plain words) | What it gated | Before | After | PR | State |
|---|---|---|---|---|---|
| Staff ask-by-text (company) | Whether Clara texts staff a question at all | Off at every company | Always on | #13563 | Merged |
| Staff text leasing (company) | Staff booking, moving or cancelling tours by text | Off | Always on | #13577 | Merged |
| Human in the loop (company) | The whole staff loop | Set at 0 of 10 companies | Removed; loop always on | #13613 | Merged |
| Escalated tour booking | Clara books the tour herself after a hand-off | Off | Always on | #13565 | Merged |
| Team tour email after a call (property) | Email to the team after a tour is booked on a call | On at Western Slope front door and the test property | Always on | #13583 | Merged |
| Missed-transfer follow-up (property) + the old tour-day fallback sender | The follow-up when a transfer to staff fails | On at Western Slope front door, Camellia, test property | Always on; fallback sender deleted | #13603 | Merged |
| Temporal enrollment env flag | Whether escalation matters run on Temporal | Env flag | Deleted; always Temporal | #13559 | Merged |
| Ask-lane allowlist | Which companies could receive staff asks | Allowlist | Retired; guard-refused asks go to #alerts | #13548 | Merged |
| Topic-scoped hold unmute | — | — | Merged | #13578 | Merged |
| Memory-ack gate | Dead confirmation prompt after a staff answer | Dead code | Deleted; the in-thread remember path is the one lane | #13611 | Merged |
| Coworker mode on tour confirmation | Coworker wording in the tour confirmation | Off | Always on | #13622 | Merged |
| Coworker mode (the rest) | A group of staff-facing behaviours | Off everywhere | Kept for now; yes/hold ask posted to Fede 2026-09-29 | — | Pending Fede |
Also: the default hand-off person is now one person per company (an organization setting), merged in #13547.
Proof: after deploy, the Willows dry run on Fede's cell (staff text loop end to end), a Temporal count showing escalation matters running, and one real-property memory-ack message id with a clean Sentry window.
| Claim | Evidence | Result |
|---|---|---|
| Tenant text sent | Twilio SMefa55f26e072c4752b607bfe6a68f542, 3:20 PM | PASS |
| Received | msg_3a57ee9c-b20d-4009-acd6-fbf923d4b779, 3:21 PM | PASS |
| Staff question opened, waiting on staff, timer owned by Temporal, 0 reminders | matter_5de82075-fdd6-49dc-b84e-0213dca16e23 | PASS |
| Reminder workflow running | escalation-matter_5de82075-fdd6-49dc-b84e-0213dca16e23 | PASS |
| Staff question email delivered to our own Gmail test mailbox (morpheus@propflowai.co) | Message-ID d7aa7140-4f57-4f46-8cf0-c71b0b602966@propflowai.co, 02:12:50Z, SPF/DKIM/DMARC pass (landed in Gmail spam) | PASS |
| Staff reply by email from that mailbox | Message-ID 179073476631.3036.2874291882834973000@propflowai.co, 02:19:26Z | PASS |
| Matter resolved | matter_5de82075-fdd6-49dc-b84e-0213dca16e23, state resolved, resolvedAt and memoryAckSentAt 02:19:37Z, staffAnsweredBy morpheus@propflowai.co | PASS |
| Knowledge saved at Willows | know_c0cdee3b-d258-4c2a-9f55-fa2b93d37f8e, source staff_reply, title "Pet Deposit Refund Terms" | PASS |
| Thank-you email to staff | msg_1114bd7a-abab-4c57-a7f7-e8b08382b518, writer escalation.memory-ack, delivered as 90436a26-c9d1-4dcc-9aef-f9d51e1609a4@propflowai.co (spam) | PASS |
| Tenant follow-up text | Twilio SMb5aefefe8bf001bb09a4e5d22f6d2c79, delivered 02:19:40Z to our own test number | PASS |
| Reminder workflow completed | escalation-matter_5de82075-fdd6-49dc-b84e-0213dca16e23, run 01a0ef0b-023f-74d2-928d-cea7d44bce95, completed 02:19:40Z | PASS |
The loop is proven end to end at Willows as of 2026-09-30. The staff mailbox used is our own Gmail test inbox, admitted by #13936.
Found by the dry run:
| PR | Changes | State |
|---|---|---|
| #13829 | Test properties may page our own sandbox mailboxes | Merged |
| #13839 | Tour cancel on a held thread is confirmed to the prospect | Open |
| #13844 | Delete the last coworker-mode switch | Open |
| #13854 | Bench stops writing the deleted switch | Open |
| #13893 | flagged with the property team | Open |
| #13936 | Willows dry run proven end to end via Gmail test mailbox and worker build cc20dd843 | Merged (2026-09-30) |
Fede (2026-09-28): only turning on at a customer needs his call; everything else is decided on the recommendation.
Why: Situs 2026-10-27: a staff note about an applicant was reworded and texted to the applicant; RCA: leak audit §Situs. Fede: 'we need to rewrite this'; checked with the Clara Refactor session (one shared reader, no rewrite layer, unsure never goes outward).
The Clara Refactor session and the Clara Coworker Vision session both reviewed this. Verdict: right shape, no conflict with the master page (/a/clara-coworker-loop-v3-2026-10: #turn-rebuilt, #gates, #inv-interfaces, #idea). Five changes, now part of the design:
BEFORE staff writes ──► phrase list ── no match + 15 chars = "the answer" ──►
rewrite step ("as if you looked it up yourself") ──► person (on failure: staff words sent verbatim)
AFTER staff writes ──► ONE READER (model judge) ──► what is this?
answer for the person ──► Clara's normal reply writer, staff fact labeled ──► Supervised hold ──► person
staff handled it ──► close as staff-owned, thank staff ──────────► person gets nothing
question for Clara ──► Clara answers staff from the records ───────► person gets nothing
note / policy ──► record it (policy is remembered) ───────────► person gets nothing
unclear, or any error ──► one short line to staff, team-ask hold ─────► person gets nothing
On Sep 29 we built a model judge (Sonnet 5.5, thinking on). It scored 64 of 64 on its own test set: 18 real incidents, the rest synthetic. The phrase list scored 53. It never reached main. We finish it rather than write a second one.
| Sep 29 judge | What the refactor needs | |
|---|---|---|
| Outcomes | 3: decision, takeover, working | 5: answer, handled, question, note/policy, unclear |
| When unsure | "Never drop an answer": relay | Unsure never goes to the person |
| On error | Falls back to the phrase list | Unclear: ask staff |
| Reads | Clara's question + the reply | Adds the person, recent thread, who wrote it, where |
| Gives back | A bucket + a reason | Adds the facts for the person + "part still open" |
Reused: the model call, the model choice, the 64-case test set and runner. Missing: five outcomes, one shared door, the one-line staff ask, and the hand-off to Clara's reply writer.
| Outcome | To the person | Matter | Remembered as policy |
|---|---|---|---|
| Answer | Clara's normal reply, held under Supervised | Closed (open if part is unanswered) | If property-wide, from staff's own words |
| Handled | Nothing | Closed, staff-owned; Clara's pending held draft withdrawn | No |
| Question | Nothing | Open | No |
| Note / policy ("I'll find out" = note + follow-up) | Nothing | Unchanged | Policy only |
| Unclear | Nothing | Open, one ask to staff via the team-ask path | No |
Answers take the path team notes already use, minus its "news you looked into yourself" line. Clara credits "our team" for staff actions, never names them. Writer fails: nothing sent, engineering alerted. Model error: three retries, then unclear. A typed out-of-office reads as a note. Staff get at most one unclear ask per matter.
Deleted: the phrase list, the rewrite step, the verbatim backup, the numbers-only check, once the facts check exists (not in PR 2).
Slack held-card replies keep today's grammar (Fede: no). The Clara Refactor session points in-thread teammate messages at the same door.
feat/staff-loop-v2 (not main): src/lib/domain/escalation/staff-loop/reply-meaning.ts (judgeStaffReply, JUDGE_STAFF_REPLY_MODEL = claude-sonnet-5-5), judgment.ts (callJudge), types.ts. Eval: scripts/coworker-harness/judge-staff-reply-benchmark.ts, fixtures/judge-staff-reply/{cases,labels}.json, results judge-staff-reply-benchmark-2026-09-28.md. Lift only these files, not the rest of the branch (routing, settings, team).bucket becomes answer | handled | question | note | unclear, plus facts[], stillOpen and followingUp. Prompt rule 5 flips to "unsure → unclear". The classifyStaffReply fallback becomes unclear; answer with no facts reads as unclear. Input adds person name/role, last ~6 turns, author, channel. The door is readStaffInput(), shaped like should-speak.ts, on the master page shared-door table. Folds in #13289 (held-out set), #13495 (zero-tolerance false-relay counter), #13496 (production timeout) and #13499 (unparseable vs unavailable). #13494 (Temporal activity wiring) is superseded by a direct call; close it with a pointer.releaseMatterOnStaffReply (replaces classifyStaffReply, the text OOO check and isUnrelatedStaffReply; header auto-reply, identity, retraction and dedupe stay) and ingestTeamReply ask (no check at all today). Answer path: relayTeamNoteAnswer → runTeamAnswerTurnUnderSupervisedHold(runTeamAnswerTurn). Rewrite buildTeamAnswerSystemContext and drop answeredByFirstName from it. Delete relayAnswerToPerson, buildStaffAnswerRelay/buildStaffUpdateRelay, relay-rephrase.ts, staff-reply-intent.ts and its tests, and the relay-site passesFabricationGuard. The staff_reply label leaves the relay. Repoint unmute-on-resolve.ts and gate-registry.ts. rememberStaffAnswerInThread runs only on answer/note; distillStaffAnswer's guard stays. Unclear asks go through #22866's team-ask hold.origin/harness/staff-reply-relay-leak. Scrub: scripts/promise-harness/scrub.ts. Willows recipe: scratchpad willows-proof-recipe.md (never a fede+ address). Related: #22863 stopgap, #22865 relay hold, #17641 (courtesy reply relayed) closes with PR 2.