Approved by Fede 2026-09-18
Brief standalone version of the design in section 5 of the Western Slope go-live page.
Fede's ruling, 2026-09-18 evening: Clara replies only to guest cards whose oldest activity-log entry is "Guest Card Received" by System. No setting, no value, no transition path. Staff-typed cards are parked. Once the client stops typing cards, every card passes on its own.
Contents
Clara replies only to guest cards a listing site (Zillow, Apartments.com) created, never a staff-typed card. AppFolio's own "new lead" email is not the trigger; Western Slope gets none of those. The card's "Source" label doesn't work either: 772 staff-typed cards and 381 listing-site cards carried identical labels.
Every guest card's activity log starts with one line naming who made it: "Received" for a listing-site card, or "Created" by a staff name otherwise. That's the only reliable tell, so a reply goes out only once that line reads "Received."
flowchart LR
A[Feed checks AppFolio\nonce a minute] --> B{New card?}
B -- no --> A
B -- yes --> C[Feed calls the robot service's\nread endpoint, 1-2 sec]
C -- "staff name" --> D[No reply]
C -- "Received by System" --> E{Company's live\nswitch is on?}
E -- no --> D
E -- yes --> F[Clara sends the first reply]
A new card gets one quick, read-only HTTP call to the hosted robot service's read endpoint, on its already-logged-in session, about a minute after the card lands. That call normally takes 1 to 2 seconds; a cold re-login happens rarely and takes up to 2 minutes when it does. Expected load at Western Slope: one to three look-ups a day.
| Piece | State |
|---|---|
| Feed that copies every new card into PropFlow every minute | Built, running |
| Robot read endpoint the feed calls | Built for other work; not yet wired to this feed |
| Recording who actually created each card | Not built |
| The switch that starts a reply when a new card lands | Exists in code, deliberately turned off |
| The message Clara sends as the first reply | Built, currently paused by Fede |
| The company-wide on/off switch for live replies | Built |
| Graded test run against real past cards | In review |
Ships dark, off by default, as a company setting. Before it's turned on, the team replays 47 real Western Slope cards through a safe copy of the system and publishes the graded result here. Turning it on for real prospects is Fede's call, made after reading that result.
| Question | Answer |
|---|---|
| Let the robot read each new card to check who created it? | Yes, read-only, via the hosted robot service's read endpoint. |
| Un-pause the work that drafts and sends the first reply? | Yes, un-paused 2026-09-18 evening. |
| Include Zillow's referral-network cards? | No, excluded. |
Once Clara's first reply goes out, two things happen back in AppFolio.
On Clara's first reply, the card's inquiry is assigned to Clara in AppFolio (Fede, 2026-09-18: "we should assign clara"). That needs a "Clara" user set up in Western Slope's AppFolio — that's the client's step, not ours. The target user to assign to is an org setting; if it isn't set yet, the assign write parks instead of failing or guessing a user.
Both mechanics — assign and notes — get wired and proven at the Willows (JP&Co test property 45) while the note cadence above is being decided. Never a test write into Western Slope's live database.
| Piece | Owner |
|---|---|
| Reply path — intake, composer, replay | Session 000 |
| AppFolio write path — note and assign intents, drain, robot | Session 003 |
Hand-off between them is one call at reply-send completion, carrying the card id, conversation id, outcome, reply body, and whether to assign.
| Step | State |
|---|---|
| Red main | Fixed (#9632) |
| Robot actor field | Merged, armed (#387) |
| createdBy stamp | Armed (#9633) |
| Lead-source setting | Fixing tests (#9484) |
| Composer | Armed (#9425) |
| Intake + seam | PR opening |
| Replay driver | PR opening |
| Note + assign write path (session 003) | PR opening |
| Aliases | Armed (#9643) |
| Rent-sweep | Armed (#9634) |
| Lock-in promise | Armed (#9635) |
| Weekend date | Armed (#9642); #9475 landing |
Read-only audit of the code that copies AppFolio guest cards into PropFlow, prompted by a Western Slope card that looked six days late. Nothing was changed. The fixes are a separate step.
The evidence, in order:
rows=9 on every tick from 12 Sep through 18 Sep 22:45:08 UTC. Then rows=10, created=1 at 18 Sep 22:46:10 UTC. From that tick on it reports unchanged=1.Received value and is never set to our own clock. It reads 18 Sep 22:45 UTC, which is 4:45 PM local, about a minute before we first saw the card. So AppFolio's report said the card was received on the 18th while the card's own activity log says the 12th. Those are two different AppFolio fields and they disagree by six days.Ruled out explicitly: a date window, a paging cutoff, a throttle, a failed run, and "the sync was switched on today". The 30 day rule that blocks creating a record from an old card never applied either, since the card presented itself as same-day.
What we cannot see is why AppFolio held it back. That needs someone to open card 2910 in their interface. The likely shapes are a card that sat in a state their report excludes until something promoted it, or a card their system re-received.
| # | What breaks | Where | Fix in one line |
|---|---|---|---|
| P0-1 | The first minute after a new AppFolio account is connected, every card received in the last 30 days is made as if it were a brand new lead, with nothing marking it as backfill. The day the reply trigger is switched on, that is a month of stale leads getting a first text at once. Not what happened here, but it is waiting for the next client. | guest-card.ts:1572, writers/utils.ts:161, lambda/appfolio-sync/handler.ts:4224 | Record when we first saw each card and make the reply trigger require that the card is new to us, not merely new in the list. |
| P0-2 | A lead's created date is AppFolio's Received value, so we cannot tell a card we just discovered from a card that just arrived. Every funnel number keyed on created date is AppFolio's clock. This case shows the field can also be six days off the card's own history. | guest-card.ts:1631, seeded once at dynamo/inquiries.ts:711 | Add our own "first observed" timestamp instead of overloading one field with two facts. |
| P1-1 | The writer states that a bookkeeping-only save leaves "last updated" alone. The storage layer overwrites it on every write regardless, so closed leads keep reading as "just updated" in every recent-first screen. Visible on this very record: made at 22:46, rewritten at 22:47 with nothing meaningful changed. | guest-card.ts:1501 against dynamo/inquiries.ts:605 | Let the caller opt out of the automatic stamp, or drop the claim from the writer. |
| P1-2 | The "already handled" marker is saved before the identity repairs run. If anything throws in between, the marker survives and every later minute skips that card. The person stays nameless or phone-less, silently and indefinitely. | guest-card.ts:1518 and :1527; the roll-back at :1857 cannot be reached from that throw | Save the marker only after the repairs report success, or move the roll-back into the catch. |
| P1-3 | A card that moves to another property makes a second lead under the new property and the first is never retired. Two live leads, one card. | guest-card.ts:1612 (the id includes the property); the snapshot at :928 is property-scoped | Look the card up across the client's properties before creating, and retire the record on the property it left. |
| P1-4 | Nothing on the synced card says who created it in AppFolio, so the "only reply to listing-site cards" rule in section 2 has nothing to read. The activity type that would carry it is defined and used nowhere. | domain/pms/types.ts:649-679; prospect-activity-log/contract.ts:296 | Carry AppFolio's creator field on the synced card, the way the application sync already does. |
| P1-5 | The per-property fetch throws away AppFolio's "there was more" signal. A very large property could silently lose rows with only a log line nobody watches. The purchase-order fetch guards against exactly this; guest cards do not. | pms-adapter.ts:797, appfolio/client.ts:537 | Use the metadata-returning fetch and fail the tick when the answer is truncated. |
| P1-6 | A card's status is judged by hunting for words like "inactive" or "application complet" inside whatever string AppFolio sends. AppFolio also sends a separate last-activity field we ignore entirely. A dead lead whose status wording contains no recognised word stays live in Clara's eyes. | guest-card.ts:504 and :519; unused fields at appfolio/types.ts:716 | Confirm what each client's status column actually contains, then match a known list instead of substrings. |
| P1-7 | A card deleted or merged in AppFolio simply stops appearing and we never notice, so the lead lives on pointing at a card that is gone. The same blindness that let this card's six-day absence go unremarked. | no disappearance handling anywhere in guest-card.ts | Compare each run's card numbers against what we hold for the property and flag the ones that vanished. |
| P2-1 | The roll-back save is the one save in the file that does not advance the row's version counter, and the stale row is then handed to the rest of the batch. A second card for the same person in the same minute loses the write race and is skipped for that tick. This is the issue the reviewer flagged. | guest-card.ts:1858-1859; every other save bumps at :1328, :1511, :1540, :1667 | Bump the version after the roll-back save, like every other save site. |
| P2-2 | When an identity repair fails, the reason is thrown away. The warning names the card but never says what went wrong, so the roll-back cannot be triaged. | guest-card.ts:817-821 | Log the error before swallowing it. |
| P2-3 | AppFolio sends the card's notes in the row we already fetch and we drop them. Notes are what the leasing team actually reads. | appfolio/types.ts:740, not mapped at pms-adapter.ts:1917 | Carry the notes field through, read-only. |
| P2-4 | If two lead rows ever carry the same card number, the lookup quietly keeps one and the other becomes invisible to the sync for good. | writers/utils.ts:10-17 | Detect the collision and log it rather than letting last-one-wins decide. |
| P2-5 | The schedule file still lists guest cards as every 15 minutes. That line stopped firing on 13 Sep and the real cadence is every minute. Six comments in the writer size their read cost "per 15-minute tick", so the load they are reasoning about is 15 times smaller than the real one. | the guest_cards entry in lambda/appfolio-sync/schedules.json; comments throughout guest-card.ts | Delete the dead cadence line and correct the comments. |
| P2-6 | Cards whose date did not parse sort to the front of the batch, so they write first and can be overwritten by better data later in the same run. Harmless today, undocumented. | guest-card.ts:951 | Sort dateless cards last. |
P0 means it can reach a customer. P1 means data goes wrong quietly. P2 is cleanup.
Read-only audit against main at 456eddbb, plus production records and sync logs. No prospect names appear on this page.
Section 5's replay, run: 47 real Western Slope guest cards (the 48th window read is included below; one card is out of window) replayed against org_ws_mirror_test, a sandbox copy of the Western Slope roster used only for this kind of test — never the client's own AppFolio account or database. The company's live-reply switch stayed off the entire time, so nothing was ever delivered to anyone; every reply Clara composed was held and written to a record instead of sent.
Replayed against commit 3f8aa1e67b70ca59a794e352f644858dcf976bd0 on fede/gca-assign-clara-engage; grading logic as of commit 65c53d93d7 on PR #9712.
| Metric | Count |
|---|---|
| Cards replayed (held intents graded) | 48 |
| Composed a real reply (draft body on file) | 14 |
| Not graded (never reached the send door) | 34 |
| Held (company not live) | 14 |
| Passed grading | 12 |
| Failed grading | 2 |
Prospect and staff names replaced with [prospect] / [staff]. Inbound is the prospect's own first words, taken from the AppFolio guest-card record, exactly as Clara read them.
Inbound: "We were told to come by between 2-4 on Thursday??"
Subject: Interested in Ember Estates HZ
Draft: "Yes, that's right — you're all set for Thursday, September 24th at 2:00 PM at Ember Estates HZ, 2858 C 1/2 Road, Grand Junction, CO. See you then!"
Nothing in the card or the inbound message names September 24th, 2:00 PM, or a confirmed booking — this is Clara's first-ever touch with this prospect, so there is no prior thread to be echoing. She invented a specific date/time and asserted it as already-booked. A second card in the same replay (address and other names redacted here) did the same thing with a different fabricated date. Both now fail grading.
Inbound: "Friday"
Subject: Interested in 2978 Krista Street
Draft: "Perfecto, el viernes 25 de septiembre tengo disponibilidad desde las 9:00 AM hasta las 4:30 PM. ¿Qué hora le acomoda mejor? Y para terminar de agendar, ¿me podría compartir su nombre?"
Proposes a time and asks — never asserts anything is already booked.
Inbound: "Hi, sorry. I've been applying to a lot of places. Which complex are you with?"
Subject: Interested in Northridge Apartments
Draft: "No worries at all! I'm with Northridge Apartments, at 565 Redcliff Cir in Ridgway, CO. You'd previously looked at a one-bedroom here. Want me to check on current availability, or would you like to come take a look in person? I've got openings tomorrow between 9 AM and 4:30 PM if that helps."
Inbound: "No thank you I think I found something else. Curiosity though how much is a two bedroom two bath or a one bedroom one bath"
Subject: Interested in Northridge Apartments
Draft on the intent record: (none — the intent stayed pending with no body)
The intent record looks like Clara never got here, but the conversation it points to tells a different story: it already holds a real, non-composed-of-nothing reply. The compose step worked; the step that was supposed to save that draft back onto this held-intent record never ran. Not a routing decision, not an empty reply — a bookkeeping gap, tracked as a follow-up.
The full 48-row inbound-and-draft table lives with the grader in PR #9712 (stacked on #9425's branch, not yet merged — review in progress), which also carries the corrected grader and the new hallucinated-booking rule.
Full detail (evidence, file references, options considered) → Western Slope go-live, section 5.
When you flip Western Slope's company switch, Clara starts answering — by email — new guest cards that AppFolio creates from syndicated listing sites (Zillow, Apartments.com). That's it; nothing else about the client's setup changes. Turning it on takes two settings: the company's live switch, and a lead-source setting telling Clara that Western Slope's leads come from AppFolio guest cards. Both of those switches are OFF for every org right now, including Western Slope, so nothing acts on a real card no matter what merged overnight. Everything below this line shipped dark tonight so it's ready to flip once the proof holds up — no overnight report exists, and none of tonight's proof runs have happened yet.
| Piece | Effect in plain words | Status |
|---|---|---|
| Sync reads the card and who created it | Every new guest card gets checked for whether a person typed it in or a listing site sent it automatically. | Merged and live in the sync worker (PR #9785 — fixes the key the sync job reads the card by) |
| Reply only to System-created cards | Clara only writes back on the automatic, listing-site cards — never one a staff member typed. | Merged (standing rule, shipped earlier) |
| Zillow relay cards skipped | Cards that came through Zillow's referral network, not a direct listing, are left alone. | Merged (standing rule, shipped earlier) |
| Company live switch holds every email | Until you flip Western Slope's switch, no email goes out, no matter what else is working. | Merged (standing rule, shipped earlier) — currently OFF for every org |
| Lead-source setting | Tells Clara "Western Slope's leads come from AppFolio guest cards" so she knows which pipe to trust. | Merged (standing rule, shipped earlier) — currently unset for Western Slope |
| Composer writes the exact draft that later gets sent | The reply Clara would send is written out in full now, not a placeholder filled in later. | Merged (PRs #9633, #9705, #9635, #9484, #9737, #9425) |
| Draft composed before hold so held drafts are reviewable | Even while the switch is off, you can read the exact email Clara would have sent. | NOT merged — PR #9721 has a green review; one red test is under triage |
| Real composer wired into the sync worker | The live card-reading job now calls the real reply-writer, not a test stand-in. | Merged and live in production, web and the sync worker (PR #9750) |
| First-touch gate on the mailbox | Clara only auto-replies to a prospect's very first email, never everything that lands in the mailbox. | Merged and live in production (PR #9761) |
| Booking-promise guard | Clara is blocked from telling a prospect a tour is booked unless a tool actually booked it. | Merged (standing rule, shipped earlier) |
| What was tested | Where | Result | Evidence |
|---|---|---|---|
| Clara answers a new syndicated guest card by email, end to end (the Western Slope email-reply goal) | Camellia, production (accepted as the proof for Western Slope — Fede, 2026-09-19, Option A) | PASSED. An Apartments.com lead became AppFolio guest card 115 (created by System), landed as a PropFlow inquiry at 11:16 AM MT with the prospect's question attached, and Clara's email reply went out at 11:17 AM MT; the thread ran 14 messages, tour hours were held, and nothing was held back. | Inquiry 296cad10-988d-486c-ac4b-47d169fdd558, conversation 529260b6-f2c1-4f86-a6f3-36f3b03006c5; Slack proof thread |
| Same replay at the Willows test property (Willows email proof) | Willows test property, JP&Co test database | BLOCKED, separate open issue: AppFolio confirms a Willows website inquiry but never creates a guest card for it (checked at 30 min, 2 h and 2.5 h), so there is no card for the sync to pick up. Fede decided not to syndicate the fake Willows test unit to public listing sites to get around this. The Camellia run above is the accepted production proof instead. | Tracker row "Email replies to System guest cards: review-before-send + tour-close fix" on the Western Slope go-live page |
| Sending the rental application after a tour (Willows application send) | Willows test property | Not run. Pending. The mode setting (PR #9779) merged this morning; the post-tour wiring (PR #9783) is being moved onto main; the drain step (PR #9784, session 003) has not merged. The robot's send route itself is live and was proven on a bench card — see the "SMS/Email Rental Application Sent" entry by Clara PropflowAI in that card's Actions Log. | — |
| Temporal tour-scheduling workflow, live run evidence | Willows test property | Not run. Pending. | — |
Deploy: production web is serving commit 30a22927, confirmed via the health endpoint at 2026-09-19T13:38:57Z UTC (7:38 AM MT) — that commit includes PR #9750 and PR #9761. The sync worker Lambda (propflow-appfolio-sync-prod) is running main@c7eb90d9, deployed 2026-09-19T12:58:00Z UTC (6:58 AM MT) per its function Description — that build includes PR #9750, #9761, #9785, and #9809. PR #9721 has not merged.
The root-cause fix (PR #9809) is merged and live in the sync worker as of the deploy above. The last 3 remaining Western Slope duplicate pairs were collapsed at 7:33 AM MT today, with backups kept; zero duplicate prospect groups remain in Western Slope's data. One thing this can't touch: AppFolio can still create two guest cards for the same person on its own side — that's a duplicate inside AppFolio itself, not something fixable from ours. The replay harness that catches this class of bug going forward (PR #9771) is open, but its own review check is currently failing (red) — not yet mergeable.
Follow-up issues: #9807 (9750 follow-up, backfill terminal no-inquiry-text holds); #9812–#9816 (9809 follow-ups, duplicate-fix defects); #9817–#9824 and #9855 (9771 follow-ups, replay-harness test defects).
How to turn off: flip the live switch off. The effect is immediate — no new replies go out from that moment on.
Overnight 2026-09-19 → 09-20, every real Camellia guest card in the corpus (170) was replayed through Clara's real first-email writer, compose-only against production reads (nothing sent, nothing written), and graded by the judge on Fede's email rules. The bar for switching review-before-send off is 0 DO_NOT_SEND and ≥90% SEND_AS_IS. Camellia is the no-side-effects regression set: one community, an onsite office, studios and one-bedrooms.
| Run | What changed | SEND_AS_IS | EDIT | DO_NOT_SEND |
|---|---|---|---|---|
| v1 (Sep 19 evening) | Baseline before the night's fixes | 61 (36%) | 16 | 92 |
| v3 | Writer always produces text (tool-round budget), card bedrooms reach the writer, no "I don't have your budget" prefaces | 146 (85.9%) | 17 | 7 |
| v3, fixed grader | Grader stops failing replies for unset "-1BR" sentinels and for cards whose move-in date is now in the past | 159 (93.5%) | 7 | 4 |
| v4 | Nothing of the asked size open → name what IS open with facts, never "the next one that comes up", never a bare tour ask | 164 (96.5%) | 2 | 4 |
| v5 | "I don't have your…" clause banned outright; nothing above the greeting | 167 (98.2%) | 1 | 2 |
| v6 | A confirmation of a card fact ("are you set on a one-bedroom?") is a re-ask, banned | 167 (98.2%) | 3 | 0 — bar PASS |
Judge: same rubric each run (understood / answered / accurate / sensible + 7 hard rules). Replays ran on the subscription, one compose and one judge call per card, in-process, from a detached checkout of main plus the unmerged prompt patch. Artifacts: s3://propflow-training-data/guest-card-corpus/runs/2026-09-20/. All six prompt/grader changes are merged to main.
Western Slope's slice (2026 cards) was replayed on Sep 19: 15 send-as-is / 4 edit / 31 do-not-send out of 50 with a message. 29 of the 31 are one defect — the home they wrote about is gone and Clara names no similar open homes — which is the similar-homes change (PR #9961, owned by another session, still in review). The seven "I don't have your…" prefaces in that run are fixed by tonight's rules. Once #9961 lands, the same one-command replay reruns Western Slope against the same bar; that number, not Camellia's, is the go/no-go for turning Western Slope's review-before-send off.
Rerun of the Western Slope slice on current main (commit 55ff76b229, checked out fresh into a detached checkout — never the shared worktree), same judge as this morning's v4 run (judge.ts, last touched by #10082). Compose-only against production reads, nothing sent.
| Run | Composed | SEND_AS_IS | EDIT | DO_NOT_SEND |
|---|---|---|---|---|
| v4 (this morning) | 50 of 1,116 (1,066 skipped, no PropFlow identity) | 17 (34.0%) | 15 | 18 |
| v5 (now, on today's main) | 48 of 1,116 (1,068 skipped, no PropFlow identity) | 23 (47.9%) | 10 | 15 |
Bar is 0 DO_NOT_SEND and ≥90% SEND_AS_IS — still NOT MET. The graded set shifts by a couple of cards run to run (live prod reads, not a frozen file), so treat 48 vs 50 as noise; the rates are the real signal. Send-as-is rate nearly doubled (34% → 48%) but 25 of 48 replies today still miss the bar.
| Group | Count | Fixed by a PR merged today? | What Clara did wrong |
|---|---|---|---|
| Named home ignored | 11 | Targeted, not fixed — #10119 and #10201 merged today (before this run) to fix exactly this, but the rate didn't drop | The card or subject names a specific home; Clara asks "which home?" or pitches other listings instead of answering about the one asked for. |
| Home's gone, no real alternatives | 10 | Targeted, not fixed — #10119 ("first-touch named-home fact … never nothing similar") covers this by design, but the rate didn't drop | Clara correctly says the home is no longer available, then offers zero similar open homes (or one home wildly outside budget) instead of a couple of real options. |
| Ignores what the prospect actually asked for | 1 | No matching PR found | Prospect named specific needs (central air, quiet); Clara gives generic listing facts and never addresses them, and offers a tour slot on the move-in day itself. |
| Promises beyond the record | 1 | No matching PR found | Clara reassures on a pet-limit exception ("wouldn't expect that to be a dealbreaker") that isn't anyone's call to make from the card alone. |
| States a data gap instead of omitting it | 1 | Partial — #10090 (merged, included in this run) bans "I don't have…" language about the prospect, but this instance discloses missing property data (internet speeds), a case #10090 doesn't cover | Clara says she doesn't have the internet-speed info instead of just leaving that line out. |
2 of the 25 misses don't cleanly fit one group (a subject/body contradiction on a two-property test row, and a case with two different homes offered against a one-home subject) — grouped as edge cases, not a sixth category.
First names only; no emails or phone numbers.
Card 2935, 2841 Morningside Court. Jair: "Hello, I'd like more information about 2841 Morningside Ct.."
Home facts given: 4bd/2.5ba, 1,518 sqft, Orchard Mesa, 2-car garage, fenced yard, $2,300/mo, cats & dogs OK.
The home at 2841 Morningside Court is available now — it's a 4 bed, 2.5 bath single-family home in Orchard Mesa with 1,518 sq ft, a 2-car garage, a fenced backyard, and recent upgrades throughout, renting at $2,300/month. Cats and dogs are welcome there too. I have tomorrow, Monday September 21st, open at 10:00 AM, 1:00 PM, or 3:00 PM — want to grab one of those to see it in person?
Judge: engages the exact home asked about, gives concrete listing facts, stays concise, closes with a real tour ask with specific times.
Card 2790, 374 Ember Lane. Laurie (no message text on the card).
Home facts given: 374 Ember Lane is gone; two similar open 3-bed duplexes at 367 and 372 Ember Lane, private garage, fenced yard.
Hi Laurie, The home at 374 Ember Lane is no longer available. We do have a couple of similar brand-new 3-bedroom duplexes open right now at 367 Ember Lane and 372 Ember Lane, each with a private garage and fenced yard. Happy to send more details on either if you'd like!
Judge: correctly handles a gone home — says "no longer available," offers two similar open alternatives with concrete amenities, no tour ask on the gone home, no disclaimers.
Card 2838, 367 Ember Lane. Kimberly: "We are interested in 367 367 Ember Ln. We have just sold our house and are need of a 9 to 12 month lease while we wait for our new home to be built. We are pet owners too :)."
Home facts given: 3bd/2ba, 1,317 sqft, brand new, quartz counters, attached garage, fenced yard, $2,750/mo, pet friendly, available now.
367 Ember Lane is a 3 bed, 2 bath home at 1,317 sq ft, renting for $2,750/month, and it's available now, so it'd line up well with your October 1st move-in. It's brand new construction with quartz countertops, stainless steel appliances, an attached garage, and a private fenced backyard, and it's pet friendly for both cats and dogs. On the lease term, a 9-month lease would be a leasing-team decision — 12 months is the standard term we offer. I'd love to get you and your family in to see it. I have openings tomorrow, Monday the 21st, at 10:00 AM, 1:00 PM, or 4:00 PM — do any of those work for you?
Judge: engages the specific home, pets, move-in and lease-term ask; handles the 9-month exception in one flat sentence with no follow-up promise; closes with a concrete tour ask.
Card 2943, 566 Placer Street. Ethan: "Hello, I'd like more information about 566 Placer St. Was wondering if you'd consider a 6 month lease with option to extend. Can prove funds for initial 6 months. Me and my girlfriend/partner are trying to relocate to the area. We need parking for a few vehicles and a 7x16 cargo trailer. We have one small well behaved cat. And both have excellent credit."
Home facts given: 3bd/2.5ba, 1,342 sqft, 2-car garage, fenced yard, $2,100/mo, available now, cats OK ($300 deposit + $35/mo).
Hi Ethan, Thanks for reaching out about 566 Placer Street — it's a great fit for a couple relocating to the area. It's a 3 bed, 2.5 bath, 1,342 sq ft home with a 2-car garage and fully fenced backyard, available now at $2,100/month, and cats are welcome (pet deposit is $300, with $35/month pet rent), so your cat is no issue at all. A 6-month lease with an extension option, and parking for multiple vehicles plus a cargo trailer, would both be leasing-team decisions. I'd love to get you both in to see the home — I have tomorrow (Monday) open at 10:00 AM, 1:00 PM, or 3:00 PM. Which works best for you two?
Judge: engages every point on the card, gives concrete listing facts, handles the lease-term/parking exceptions in one flat sentence, closes with a real tour ask.
Card 2891, Lincoln Apartments. Dawn (no message, no preferences on the card).
Home facts given: 2bd/1ba, 725 sqft, $1,293/mo.
Hi Dawn, Thanks for reaching out about Lincoln Apartments! Right now we've got a two-bedroom, one-bath home at 725 sqft running $1,293/mo. I'd love to learn a bit more about what you're looking for — bedroom count, timing, anything else on your list — so I can point you to the right fit. In the meantime, I'd love to get you in for a tour. I have openings tomorrow at 10:00 AM, 1:00 PM, or 3:30 PM — would any of those work for you?
Judge: card was blank; reply gave concrete inventory info, asked only for fields not on the card, closed with a real tour ask.
Card 2922, 566 Placer Street. Hunter: "I am interested in 566 Placer St. I'm interested in scheduling a tour for this property. I'm available: Tuesday 15 Sep 2026 at 12:00 PM."
Home facts given: 3bd/2.5ba, updated, 2-car garage, fenced yard, $2,100/mo, available now. (His requested date, Sep 15, had already passed.)
566 Placer Street is available now, and it looks like a great fit for what you're after — a newly updated 3-bedroom, 2.5-bath single-family home with a 2-car garage and fenced backyard, renting at $2,100/month. The Tuesday the 15th has already passed, but I can get you in the following Tuesday, September 22nd — I have 12:00 PM open, or any other time between 8:00 AM and 5:30 PM that day. Does 12:00 PM still work for you, or would another time be better?
Judge: engages the specific home, gives price/beds/availability, handles the lapsed tour date gracefully with a concrete alternative slot, closes with a real tour ask.
Card 2926, subject "Interested in 372 Ember Lane." Gage (no message text on the card).
Home facts given: none used — Clara pivoted to two other addresses instead of 372 Ember Lane.
Hi Gage, Thanks for reaching out — happy to help you find the right place. We've got a few different homes to choose from, including ones at 1250 Elm Avenue and 2841 Morningside Court, both in Grand Junction. Do any of those catch your eye, or can you tell me what you're looking for — bedroom count, budget, move-in timing? Once I know which home (or type of home) you're interested in, I can get you real pricing and set up a time to come take a look.
Judge: subject names 372 Ember Lane but the body never addresses that home's price or availability, pivoting to other listings and generic discovery questions instead.
Card 2913, 566 Placer Street. Lauren: "Hello, I'd like more information about 566 Placer St.. Specific Home Needs - central air and quiet." Move-in Nov 5.
Home facts given: 3bd/2.5ba, 1,342 sqft, 2-car garage, fenced yard, washer/dryer, $2,100/mo, available now, cats & dogs OK.
Hi Lauren, Thanks for reaching out about 566 Placer Street! It's a recently updated 3-bedroom, 2.5-bath single-family home with 1,342 sq ft, a 2-car garage, fully fenced backyard, and washer/dryer included, listed at $2,100/mo — and it's available now, so it lines up well with your November 5th move-in. Cats and dogs are both welcome here too. I'd love to get you in to see it in person. For Thursday, November 5th I have openings at 10:00 AM, 1:00 PM, or 3:30 PM — which works best for you?
Judge: solid facts and a real tour ask, but never engages Lauren's stated specific needs (central air and quiet), and offers tour slots on her move-in date itself.
Card 2909, 239 S Elm Street. Ashley: "Ashley Votaw is requesting a tour on 9/15 at 239 S Elm St Unit 2. Please reach out to confirm. Could I come and view this property." Move-in Oct 1.
Home facts given: none used beyond "no longer available" — Clara offered zero other homes.
Hi Ashley, The home at 239 S Elm Street Unit 2 is no longer available. Since you're hoping to move in around October 1st, could you let me know what bedroom count and rent range you're looking for? That'll help me point you toward what might be a good fit.
Judge: correctly says the gone home is "no longer available" but offers zero similar open homes, leaving the prospect with nothing.
Card 2879, 566 Placer Street. Aloha: "I am interested in this rental and would like to schedule a tour. Please let me know when this would be possible.."
Home facts given: none used — Clara asked which home instead of answering about 566 Placer Street.
Hi Aloha, Thanks for reaching out — happy to get a tour set up for you! Since we have a few homes available, could you let me know which one you're interested in? A couple of our current listings are 1250 Elm Avenue and 2841 Morningside Court, both in Grand Junction. Once I know which home you'd like to see, I can find some open times for you.
Judge: the prospect inquired about a specific home (566 Placer Street, per the subject), yet the reply asks which home they want and gives zero facts about the asked-for property.
| What | Before tonight | After tonight |
|---|---|---|
| Writers of a guest card's first email | 2 (engine + old writer) | 1 engine; old writer deleted from main (9:24 AM) |
| What decides whether Clara writes | Company "Clara live" flag, redraft and rerun checks | One read of the building's leasing mode |
| Replay: 120 cards that failed last night (pass) | 44 of 120 | 78 of 120 |
| Replay: same cards, do not send | 55 | 37 |
| Willows, Off mode | untested | passed on production |
| Willows, Supervised, approve, rerun and Live | untested | passed on a local run of the production build against production records (see Willows runs) |
Replay counts are after correcting four known grader mistakes; raw counts are in the replay section.
Until tonight Clara had two writers for a guest card's first email. The engine wrote every live one. An older writer still wrote in three edge cases with its own stricter rules. On Oct 18 one of those rules cost a Western Slope renter his reply.
Tonight the edge cases moved into the engine and the old writer stopped being called. One writer, one set of rules, proven on real cards and at Willows.
| # | When | What happened |
|---|---|---|
| 1 | Sep 19 | Old writer built so a dark company's held card had a draft to grade. It had no tools, so it could not book a tour. |
| 2 | Sep 24 | Engine merged behind a per-property switch. The old writer's deletion merged only onto a side branch, rebuilt without it within hours. |
| 3 | Sep 29 | Engine became the only writer of live first emails; switch deleted. Old writer kept for three edge cases. Its follow-up issue had no owner. |
| 4 | Sep 30 | A status report wrongly told Fede the old writer was retired. Fede: "idont want old paths around at all. one engine peroiud". |
| 5 | Oct 8 | The 17-PR chain closed that day was the team-notification engine, a different piece. Nothing in it applied here. |
| 6 | Oct 18, 4:37 PM | Card 3027: a renter asked about two homes two minutes apart. The engine answered the first. The second went to the old writer, whose listing-recital rule refused "a 4-bedroom" twice. He got nothing about that home. |
| 7 | Oct 18–19, night | Second homes moved to the engine (6:24 PM). The remaining three cases followed overnight. |
Session-by-session history: Multi-inquiry gap analysis, "How it came to be".
Default was drop unless a real renter harm is named.
| Rule | What it refused | Verdict | Why |
|---|---|---|---|
| Listing recital | Reading the listing's beds, baths, rent or size back to someone who found the home on that listing. | DROPPED | Tone, not harm. Cost card 3027 its reply. Fede: "all silly rules like that one to be questioned". |
| Office hand-off | "Call or email the leasing office." | DROP | The empty-reply check covers the harm. |
| Wrong listing fact | A bed, bath or size figure that disagrees with the listing. | LOOSEN | Real harm ("ZERO invented facts"), but the engine's fact check already strips untraceable figures. Gap: a figure from another home passes. |
| Shared guards | Thinking out loud, past tour times, wrong weekdays, empty hand-offs, fair housing, invented facts, prompt leaks. | KEEP | Real harm. Already shared by every email. |
| Unbacked fee or pet rule | A pet, deposit or fee rule the knowledge base doesn't back. | KEEP once | Runs twice today; keep one. |
| Old-writer leftovers | Greeting checks that only log, no-tools prompt patches, duplicate copies of engine checks, book-at-release. | DROP | Noise or duplicates. Two copies are how the paths drifted. |
| Renter's pet stated as policy | Prompt lines, not a check. | LOOSEN | Drop if the pet eval is green on the engine. |
| Engine's silent holds | Missing tour times, one rent for all sizes, no home named. The renter hears nothing. | LOOSEN | Fede: "silent drops with nothing in the app are the worse bug." After one redraft, send unless the defect is real harm. |
No diagnosed failure in either replay run pointed to a dropped rule.
No switch: this fixes behavior that is already live.
Mountain time. PR numbers are in the evidence table.
| Step | What it changes | State |
|---|---|---|
| 1 | Deleted the rule that refused to repeat a listing's beds, baths or rent (card 3027's lost reply) | merged 10:08 PM |
| 2 | Willows test tool runs each mode (Off, Supervised, Live), approves, reruns, and refuses any recipient outside our test mailbox | merged 10:31 PM |
| 3 | First email reads one leasing mode at the front door; the company "Clara live" flag folds into it | merged 11:29 PM |
| 4 | Home checks and tour-slot helpers moved out of the old writer, no behavior change | merged 11:29 PM |
| 5 | Tour-free redraft removed; a stale tour plan is held instead | merged 11:32 PM |
| 6 | "Returning renter" only when the renter actually wrote or toured before (card 3027's root cause) | merged 11:54 PM |
| 7 | Approve step no longer rewrites a stale draft through the old writer | merged 12:11 AM |
| 8 | Reruns go through the engine; prep calls no other writer | merged 12:50 AM |
| 9 | Replay tool: 150 real production first emails captured; grader reads Willows specials from the lease policy | merged 1:45 AM |
| 10 | A rate-limited first email retries 3 times over ~6 min before holding | merged 2:30 AM |
| 11 | "Nothing open" only when the availability read actually answered | merged 2:39 AM |
| 12 | Email safety check moved to its own file, part 1 | merged 3:05 AM |
| 13 | A renter who asks for more information gets rent, beds, baths and size (1 → 8 of 10 real cards ready to send) | merged 3:09 AM |
| 14 | Promises match what the turn did; a question handed to the team keeps its "I'll check" line | merged 3:17 AM |
| 15 | A home's own house record beats a group listing (4 Western Slope and 4 Situs homes no longer shown open) | merged 3:28 AM |
| 16 | Email safety check moved, part 2 | merged 3:35 AM |
| 17 | The greeting always opens the email | merged 3:35 AM |
| 18 | More helpers moved out of the old writer, no behavior change | merged 4:26 AM |
| 19 | Clara never tells a renter the tour calendar failed (9 real cases caught, 0 false alarms in 481 emails). The safety-check half landed in step 12 | merged 5:49 AM |
| 20 | Later-opening homes get their own date; the "from" price counts only homes open today | merged 5:30 AM |
| 21 | Email safety check moved, part 3 | merged 5:05 AM |
| 22 | Old writer deleted; a check fails the build if it comes back. Three PRs: replay tool, rerun tool and tests off the old writer (#21106, merged 7:07 AM); evals grade the engine prompt (#21109, merged 7:54 AM); the file itself deleted (#21112) | Merged 9:24 AM |
| 23 | Asked homes in full, the rest of a long list hidden; an in-budget home is still named when every asked home is over budget (fixes the regression below) | merged 6:17 AM, urgent |
| 24 | "Let me confirm" only when a real follow-up is booked behind it; the team note never carries the renter's name | merged 6:12 AM |
| 25 | Clara never says how many homes are open ("ten 1-bedrooms open now"); found by the Willows runs, 27 of 154 real replayed drafts did it | #21125 merged 10:37 AM, live everywhere on deploy; proven on Willows card 304 (local run of the fix): "has 1-bedrooms and two 2-bedrooms open right now", no totals |
| – | Never offer a listing with no unit on record | closed after 3 review rounds: production has zero such rows |
Last night's run: 354 cards (150 production first emails since Sep 24, plus 204 older cards). Situs has none: a dark company is skipped before a draft is written.
Last night, all 354 cards (before tonight's fixes)
| Company | Cards | Good as is | Needs an edit | Do not send | Held |
|---|---|---|---|---|---|
| Western Slope | 107 | 46 | 17 | 40 | 4 |
| JP&Co / Camellia | 181 | 135 | 37 | 7 | 2 |
| Willows (test) | 62 | 46 | 6 | 10 | 0 |
| Fairhaven (test) | 4 | 0 | 0 | 3 | 1 |
| All | 354 | 227 | 60 | 60 | 7 |
This morning, the 120 failing cards re-run with tonight's fixes
| Company | Cards | Good as is | Needs an edit | Do not send | Held |
|---|---|---|---|---|---|
| Western Slope | 57 | 7 → 34 | 15 → 4 | 35 → 18 | 0 → 1 |
| JP&Co / Camellia | 44 | 36 → 40 | 1 → 0 | 7 → 4 | 0 → 0 |
| Willows (test) | 16 | 1 → 4 | 5 → 0 | 10 → 12 | 0 → 0 |
| Fairhaven (test) | 3 | 0 → 0 | 0 → 0 | 3 → 3 | 0 → 0 |
| All | 120 | 44 → 78 | 21 → 4 | 55 → 37 | 0 → 1 |
What changed, three real drafts
| Before | After |
|---|---|
| Facts when asked. Western Slope, Northridge. Renter: "I'd like more information about 565 Redcliff Cir unit 585-201." | |
| "Unit 585-201 at Northridge Apartments (565 Redcliff Cir) is open now — a 1-bedroom. Want to set up a tour? Let me know what days and times work for you and I'll get something on the calendar." EDIT left out the rent | "Unit 585-201 at Northridge Apartments (565 Redcliff Cir) is open now — a 1-bedroom, 1-bath at $1,750/mo. I'd love to get you in to see it — I have today, Monday the 19th, at 10:00 AM or 2:00 PM. Which works better for you?" PASS |
| Calendar trouble. Western Slope, 2863 Florida St. Renter: "I'd like more information about 2863 Florida St." (fix armed, not on main yet) | |
| "The home at 2863 Florida Street is open now — a 3-bedroom, 2-bath home at 1,435 sqft, running $2,775/month. I wasn't able to pull up tour times just now — I'll check with the team." DO NOT SEND leaks an internal hiccup | "The home at 2863 Florida Street is open now — a 3-bedroom, 2-bath, 1,435 sqft home at $2,775/mo. I'd love to get you in for a tour — I have Monday, October 19 at 10:00 AM or 2:00 PM. Does either work for you?" PASS |
| Nothing open, unverified. Test property with no rent roll. Renter: no message. | |
| "Thanks for your interest in Harness Test Commons! Right now there aren't any apartments open at the property. If you can share what size you're looking for and your ideal move-in timing, I'll make a note of it…" DO NOT SEND says nothing is open when it didn't know | "Thanks for your interest in Harness Test Commons! Let me confirm what's open there and get you in to see it — what days work best for you this week?" DO NOT SEND empty "let me confirm"; fix pending (step 24) |
Bad drafts found and fixed in the engine
Willows test property only, our own test mailbox. Leasing mode was restored after each run. The Off run went through production. The Supervised and Live runs went through the same engine code on the same production build, started on Fede's Mac with production's records and sign-in tokens read straight from AWS (Fede, 9 AM: "why not just run locally"). Honest label: proven locally against production records; the deployed-build run was not done, because production's copy of the Willows token list is 2 tokens (fix tracked as #21121).
| Mode or case | What must happen | Result | Record |
|---|---|---|---|
| Off | Card recorded with a reason. No draft, no thread. | PASS Recorded, skipped as "leasing off". No draft, no thread, nothing in the test mailbox. | Card 284; production log 12:38 AM (06:38:08 UTC) |
| Supervised | Draft held, nothing sent. | PASS (local) Drafted and held for review, Clara assigned on the card, nothing reached the test mailbox in 3 minutes. | Card 299, hold eschold_d8bdf83b-5b3c-461c-a2ea-7f3edea259de, conversation 7485d137-cca4-4275-ac63-ce8a7b1e70c5 |
| Supervised, approve | Approve sends it. | PASS (local) Held, approved, sent, delivered to the test mailbox at 9:17 AM with one greeting; AppFolio got the "Email Sent" note. Two test-tool checks misfired, not the engine: the mailbox check read the template preheader "Reply from The Willows" as stray text (#21122), and the note check looked before the note was written. | Card 300, hold eschold_fa60dbca-b3f3-4934-a40d-f1297a9b85e0, conversation eeb4fbe0-6119-445f-aac8-7f0d1ac38a29, mailbox message AAMkAGQx…ZFBnHAAA= |
| Supervised, reject | Tour cancelled, nothing sent. | NOT RUN Same cause. | none |
| Live | Sent. | PASS (local) Card 303: sent with no hold, delivered to the test mailbox at 9:43 AM, one greeting, Clara assigned, "Email Sent" note recorded. Only the preheader check misfired (#21122). First attempt (card 301) was drained first by production's own cron, which rate-limited on its 2-token list and held the card as "compose failed"; that card was recovered with the rerun door below. | Card 303, intent c6ec2b2e-e668-5ee9-98fc-f557aa52e046, conversation 4819765e-2792-4c89-b7e6-ae26d5ee1cbf, mailbox message AAMkAGQx…ZFBnKAAA= |
| Reviewer redraft | Feedback replaces the held text; approve sends the new text. | NOT RUN Same cause. | none |
| Rerun | A held card re-prepared in place, held, then approved. | PASS (local) Card 301 had been held as "compose failed" (see Live). Re-prepared from the local pool, held, approved, delivered to the test mailbox at 9:34 AM, Clara assigned, "Email Sent" note recorded. | Card 301, intent b88d5a78-0c86-5ab3-b825-091761ad7307, hold eschold_eceacefb-0ddf-4185-bafe-3593d4ba0c48, conversation ada93e47-3fed-4e22-a922-1376b2245f2e |
| Count-free draft (fix #21125, before merge) | Draft names no totals. | PASS (local, fix branch) Held draft reads "has 1-bedrooms and two 2-bedrooms open right now"; the two named 2-bedrooms are a small group listed on purpose. Nothing sent. | Card 304, intent 1910c71b-1c4d-5786-967b-71ace66d4a7b, hold eschold_02c2cb23-a296-42a1-a67b-8fc31171c480 |
| Second home | Both homes answered. | NOT RUN Same cause. | none |
| What | Where |
|---|---|
| Old writer built | PR #9721 (merged Sep 19) |
| Fede's "act the same as other channels" plan, incl. "No flag" and "Retire the composer" | Issue #11694 |
| Old writer's deletion, side branch only | PR #11794 |
| Engine on main behind the switch | PR #11827 (Sep 24); company switch #11847 (closed) |
| Engine-only; old writer kept for three cases | PR #13519 (Sep 29); follow-up issue #13520 |
| Listing-recital rule | PRs #11262, #11314 |
| Oct 8 chain (team-notification engine) | PRs #17715–#17780 (closed) |
| Second homes moved to the engine (card 3027) | PR #20952 (merged Oct 18, 6:24 PM) |
| Tonight, steps 1–18 (merged) | #20963, #20972, #20994, #21003, #21005, #21009, #21018, #21027, #21050, #21057, #21058, #21067, #21070, #21085, #21065, #21089, #21087, #21002 |
| Tonight, steps 19–21, 23–24 (merged) | #21055, #21066, #21092, #21102, #21101 |
| Tonight, step 22 (old-writer deletion, three PRs) | #21106, #21109, #21112 (merged) |
| Closed, not merged | #21062; follow-up #21084 |
| Grader mistakes | #21072 (UTC day), #21073 (stale never-offer), #21074 (rent on no-message cards), #21075 ("1,518 sqft") |
| Replay tool gaps | #21076 (shared sandbox), #21077 (several inquiries on one thread), #21100 (house-record rule skips test homes) |
| Engine follow-ups | #21056 (same fact twice), #21078 (ignores afternoon ask), #21079 (self-guided wording), #21080 (rate limit on other lanes), #21088 (promise check on texts), #21095 (no application link), #21096 (gone home not said), #21097 (wrong facts), #21098 (asks known pet type), #21099 (false hold) |
| Other follow-ups | #21081 (Willows specials test row), #21082 (batch-trains workflow) |
| Willows Off verdict | Production log 2026-10-19 06:38:08 UTC, card 284: skipped, "guest_card_leasing_off" |
| Willows held cards | 285 (intent addcf415-a73c-506a-9b6f-99eaae402b7f), 286 (intent b7713c6b-9a51-5733-8f81-8973cb8d0378) |