Replying to syndicated guest cards

Approved by Fede 2026-09-18

Brief standalone version of the design in section 5 of the Western Slope go-live page.

Decision

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

  1. The rule
  2. The one exact signal
  3. How it works
  4. What exists vs. what is left
  5. Rollout
  6. Three decisions, answered
  7. After Clara replies: the card in AppFolio
  8. Tonight's build
  9. Guest-card sync audit (2026-09-18)
  10. Replay results (held drafts)
  11. Readiness report — 2026-09-19
  12. Full-corpus replay — 2026-09-20 (bar passed on Camellia)
  13. Western Slope: 10 sample replies on today's main (2026-09-20)
  14. One engine for the first email (2026-10-18)

1. The rule

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.

2. The one exact signal

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."

3. How it works

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.

4. What exists vs. what is left

PieceState
Feed that copies every new card into PropFlow every minuteBuilt, running
Robot read endpoint the feed callsBuilt for other work; not yet wired to this feed
Recording who actually created each cardNot built
The switch that starts a reply when a new card landsExists in code, deliberately turned off
The message Clara sends as the first replyBuilt, currently paused by Fede
The company-wide on/off switch for live repliesBuilt
Graded test run against real past cardsIn review

5. Rollout

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.

6. Three decisions, answered

QuestionAnswer
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.

7. After Clara replies: the card in AppFolio

Once Clara's first reply goes out, two things happen back in AppFolio.

Assign

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.

Notes — cadence pending Fede

Where this gets proven

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.

Who builds what

PieceOwner
Reply path — intake, composer, replaySession 000
AppFolio write path — note and assign intents, drain, robotSession 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.

8. Tonight's build

StepState
Red mainFixed (#9632)
Robot actor fieldMerged, armed (#387)
createdBy stampArmed (#9633)
Lead-source settingFixing tests (#9484)
ComposerArmed (#9425)
Intake + seamPR opening
Replay driverPR opening
Note + assign write path (session 003)PR opening
AliasesArmed (#9643)
Rent-sweepArmed (#9634)
Lock-in promiseArmed (#9635)
Weekend dateArmed (#9642); #9475 landing

9. Guest-card sync audit (2026-09-18)

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.

What the six-day gap actually was

The evidence, in order:

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.

How it runs today

Findings

#What breaksWhereFix in one line
P0-1The 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:4224Record 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-2A 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:711Add our own "first observed" timestamp instead of overloading one field with two facts.
P1-1The 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:605Let the caller opt out of the automatic stamp, or drop the claim from the writer.
P1-2The "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 throwSave the marker only after the repairs report success, or move the roll-back into the catch.
P1-3A 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-scopedLook the card up across the client's properties before creating, and retire the record on the property it left.
P1-4Nothing 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:296Carry AppFolio's creator field on the synced card, the way the application sync already does.
P1-5The 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:537Use the metadata-returning fetch and fail the tick when the answer is truncated.
P1-6A 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:716Confirm what each client's status column actually contains, then match a known list instead of substrings.
P1-7A 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.tsCompare each run's card numbers against what we hold for the property and flag the ones that vanished.
P2-1The 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, :1667Bump the version after the roll-back save, like every other save site.
P2-2When 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-821Log the error before swallowing it.
P2-3AppFolio 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:1917Carry the notes field through, read-only.
P2-4If 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-17Detect the collision and log it rather than letting last-one-wins decide.
P2-5The 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.tsDelete the dead cadence line and correct the comments.
P2-6Cards 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:951Sort dateless cards last.

P0 means it can reach a customer. P1 means data goes wrong quietly. P2 is cleanup.

Questions the audit answered along the way

Read-only audit against main at 456eddbb, plus production records and sync logs. No prospect names appear on this page.

10. Replay results (held drafts)

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.

MetricCount
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 grading12
Failed grading2

Four representative held drafts (inbound → draft)

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.

1. Hallucinated tour confirmation — FAILS the new rule

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.

2. Bilingual (Spanish) tour scheduling — passes

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.

3. General availability follow-up — passes

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."

4. Not graded — reply pipeline succeeded, bookkeeping write never landed

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.

11. Readiness report — 2026-09-19

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.

What ships tonight

PieceEffect in plain wordsStatus
Sync reads the card and who created itEvery 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 cardsClara only writes back on the automatic, listing-site cards — never one a staff member typed.Merged (standing rule, shipped earlier)
Zillow relay cards skippedCards that came through Zillow's referral network, not a direct listing, are left alone.Merged (standing rule, shipped earlier)
Company live switch holds every emailUntil 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 settingTells 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 sentThe 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 reviewableEven 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 workerThe 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 mailboxClara 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 guardClara is blocked from telling a prospect a tour is booked unless a tool actually booked it.Merged (standing rule, shipped earlier)

What still blocks real cards

Proof

What was testedWhereResultEvidence
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 databaseBLOCKED, 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 propertyNot 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 evidenceWillows test propertyNot 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.

Duplicate prospects

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).

What is NOT included tonight

How to turn on in the morning

  1. Read the proof results above. The email-reply proof is accepted (Camellia, production); the application-send and tour-workflow rows are still pending.
  2. Set Western Slope's lead-source setting to "AppFolio guest cards."
  3. Flip Western Slope's company live switch on.

How to turn off: flip the live switch off. The effect is immediate — no new replies go out from that moment on.

Known limits

12. Full-corpus replay — 2026-09-20 (bar passed on Camellia)

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.

RunWhat changedSEND_AS_ISEDITDO_NOT_SEND
v1 (Sep 19 evening)Baseline before the night's fixes61 (36%)1692
v3Writer always produces text (tool-round budget), card bedrooms reach the writer, no "I don't have your budget" prefaces146 (85.9%)177
v3, fixed graderGrader stops failing replies for unset "-1BR" sentinels and for cards whose move-in date is now in the past159 (93.5%)74
v4Nothing of the asked size open → name what IS open with facts, never "the next one that comes up", never a bare tour ask164 (96.5%)24
v5"I don't have your…" clause banned outright; nothing above the greeting167 (98.2%)12
v6A confirmation of a card fact ("are you set on a one-bedroom?") is a re-ask, banned167 (98.2%)30 — 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.

What the last three EDITs are

Where Western Slope stands

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.

13. Western Slope: 10 sample replies on today's main (2026-09-20)

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.

RunComposedSEND_AS_ISEDITDO_NOT_SEND
v4 (this morning)50 of 1,116 (1,066 skipped, no PropFlow identity)17 (34.0%)1518
v5 (now, on today's main)48 of 1,116 (1,068 skipped, no PropFlow identity)23 (47.9%)1015

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.

Where the 25 misses cluster

GroupCountFixed by a PR merged today?What Clara did wrong
Named home ignored11Targeted, not fixed — #10119 and #10201 merged today (before this run) to fix exactly this, but the rate didn't dropThe 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 alternatives10Targeted, not fixed — #10119 ("first-touch named-home fact … never nothing similar") covers this by design, but the rate didn't dropClara 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 for1No matching PR foundProspect 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 record1No matching PR foundClara 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 it1Partial — #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 coverClara 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.

10 real samples

First names only; no emails or phone numbers.

1. Home open — SEND_AS_IS SEND

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.

2. Home gone — SEND_AS_IS SEND

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.

3. Pet ask (+ lease term) — SEND_AS_IS SEND

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.

4. Lease-term ask — SEND_AS_IS SEND

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.

5. Missing card facts — SEND_AS_IS SEND

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.

6. Tour ask — SEND_AS_IS SEND

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.

7. Named home ignored — EDIT EDIT

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.

8. Ignores the stated ask — EDIT EDIT

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.

9. Home's gone, no alternatives — DO_NOT_SEND DNS

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.

10. Named home ignored — DO_NOT_SEND DNS

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.

14. One engine for the first email (2026-10-18)

WhatBefore tonightAfter tonight
Writers of a guest card's first email2 (engine + old writer)1 engine; old writer deleted from main (9:24 AM)
What decides whether Clara writesCompany "Clara live" flag, redraft and rerun checksOne read of the building's leasing mode
Replay: 120 cards that failed last night (pass)44 of 12078 of 120
Replay: same cards, do not send5537
Willows, Off modeuntestedpassed on production
Willows, Supervised, approve, rerun and Liveuntestedpassed 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.

How we got two writers

1 2 3 4 5 6 7 Old writer every first email edge cases only: dark company, tour-free redraft, rerun, second home delete Engine behind a switch every live first email Sep 18 Sep 24 Sep 30 Oct 6 Oct 12 Oct 18 Oct 19
#WhenWhat happened
1Sep 19Old 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.
2Sep 24Engine merged behind a per-property switch. The old writer's deletion merged only onto a side branch, rebuilt without it within hours.
3Sep 29Engine became the only writer of live first emails; switch deleted. Old writer kept for three edge cases. Its follow-up issue had no owner.
4Sep 30A status report wrongly told Fede the old writer was retired. Fede: "idont want old paths around at all. one engine peroiud".
5Oct 8The 17-PR chain closed that day was the team-notification engine, a different piece. Nothing in it applied here.
6Oct 18, 4:37 PMCard 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.
7Oct 18–19, nightSecond homes moved to the engine (6:24 PM). The remaining three cases followed overnight.

Before and after

BEFORE (until Oct 18) Guest card arrives Prep picks a writer Company live? Redraft? Rerun? Engine live first emails · has tools, can book Old writer dark company · tour-free redraft · rerun no tools · its own stricter rules AFTER (tonight) Guest card arrives Read the leasing mode one answer per building company not live = Off, until its own row exists Off / Setting up card recorded with a reason · no draft, thread or tour Supervised → engine draft held for approval approve sends · reject cancels the tour Live → engine writes and sends, unchanged

Every old rule, questioned

Default was drop unless a real renter harm is named.

RuleWhat it refusedVerdictWhy
Listing recitalReading the listing's beds, baths, rent or size back to someone who found the home on that listing.DROPPEDTone, 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."DROPThe empty-reply check covers the harm.
Wrong listing factA bed, bath or size figure that disagrees with the listing.LOOSENReal harm ("ZERO invented facts"), but the engine's fact check already strips untraceable figures. Gap: a figure from another home passes.
Shared guardsThinking out loud, past tour times, wrong weekdays, empty hand-offs, fair housing, invented facts, prompt leaks.KEEPReal harm. Already shared by every email.
Unbacked fee or pet ruleA pet, deposit or fee rule the knowledge base doesn't back.KEEP onceRuns twice today; keep one.
Old-writer leftoversGreeting checks that only log, no-tools prompt patches, duplicate copies of engine checks, book-at-release.DROPNoise or duplicates. Two copies are how the paths drifted.
Renter's pet stated as policyPrompt lines, not a check.LOOSENDrop if the pet eval is green on the engine.
Engine's silent holdsMissing tour times, one rent for all sizes, no home named. The renter hears nothing.LOOSENFede: "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.

What Fede asked for

No switch: this fixes behavior that is already live.

Tonight's PRs

Mountain time. PR numbers are in the evidence table.

StepWhat it changesState
1Deleted the rule that refused to repeat a listing's beds, baths or rent (card 3027's lost reply)merged 10:08 PM
2Willows test tool runs each mode (Off, Supervised, Live), approves, reruns, and refuses any recipient outside our test mailboxmerged 10:31 PM
3First email reads one leasing mode at the front door; the company "Clara live" flag folds into itmerged 11:29 PM
4Home checks and tour-slot helpers moved out of the old writer, no behavior changemerged 11:29 PM
5Tour-free redraft removed; a stale tour plan is held insteadmerged 11:32 PM
6"Returning renter" only when the renter actually wrote or toured before (card 3027's root cause)merged 11:54 PM
7Approve step no longer rewrites a stale draft through the old writermerged 12:11 AM
8Reruns go through the engine; prep calls no other writermerged 12:50 AM
9Replay tool: 150 real production first emails captured; grader reads Willows specials from the lease policymerged 1:45 AM
10A rate-limited first email retries 3 times over ~6 min before holdingmerged 2:30 AM
11"Nothing open" only when the availability read actually answeredmerged 2:39 AM
12Email safety check moved to its own file, part 1merged 3:05 AM
13A 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
14Promises match what the turn did; a question handed to the team keeps its "I'll check" linemerged 3:17 AM
15A home's own house record beats a group listing (4 Western Slope and 4 Situs homes no longer shown open)merged 3:28 AM
16Email safety check moved, part 2merged 3:35 AM
17The greeting always opens the emailmerged 3:35 AM
18More helpers moved out of the old writer, no behavior changemerged 4:26 AM
19Clara 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 12merged 5:49 AM
20Later-opening homes get their own date; the "from" price counts only homes open todaymerged 5:30 AM
21Email safety check moved, part 3merged 5:05 AM
22Old 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
23Asked 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 namemerged 6:12 AM
25Clara 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 recordclosed after 3 review rounds: production has zero such rows

Replay: real cards through the one engine

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)

CompanyCardsGood as isNeeds an editDo not sendHeld
Western Slope1074617404
JP&Co / Camellia1811353772
Willows (test)62466100
Fairhaven (test)40031
All35422760607

This morning, the 120 failing cards re-run with tonight's fixes

CompanyCardsGood as isNeeds an editDo not sendHeld
Western Slope577 → 3415 → 435 → 180 → 1
JP&Co / Camellia4436 → 401 → 07 → 40 → 0
Willows (test)161 → 45 → 010 → 120 → 0
Fairhaven (test)30 → 00 → 03 → 30 → 0
All12044 → 7821 → 455 → 370 → 1

What changed, three real drafts

BeforeAfter
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 runs, one per mode

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 caseWhat must happenResultRecord
OffCard 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)
SupervisedDraft 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, approveApprove 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, rejectTour cancelled, nothing sent.NOT RUN Same cause.none
LiveSent.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 redraftFeedback replaces the held text; approve sends the new text.NOT RUN Same cause.none
RerunA 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 homeBoth homes answered.NOT RUN Same cause.none

Open for Fede

Evidence

WhatWhere
Old writer builtPR #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 onlyPR #11794
Engine on main behind the switchPR #11827 (Sep 24); company switch #11847 (closed)
Engine-only; old writer kept for three casesPR #13519 (Sep 29); follow-up issue #13520
Listing-recital rulePRs #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 verdictProduction log 2026-10-19 06:38:08 UTC, card 284: skipped, "guest_card_leasing_off"
Willows held cards285 (intent addcf415-a73c-506a-9b6f-99eaae402b7f), 286 (intent b7713c6b-9a51-5733-8f81-8973cb8d0378)
PropFlow Docs