Tours need a phone

Zillow leads book tours with no phone, no-show, and nobody can call them. Clara asks for a number, then books.

Decided D1–D5 (Fede, Oct 19) — building, local branches

2026-10-18, updated Oct 19 night (virtual tours section) · Western Slope · local branches, nothing live · details in the folds below

Virtual tours: what broke on Oct 19 and the gaps

Getting the notes from the Zoom call itself: Virtual tour notes, from the Zoom call to the guest card (Oct 30, proposed).

A Western Slope Zillow lead, Jaleigh, asked for a virtual tour. She was told "virtual, here's the Zoom link." Everyone on our side was told in person. Not a new break: the booking has worked this way since July.

Oct 19, Mountain time

6:28 PMZillow lead arrives. Relay email, no phone.
6:35 PM"Do you do virtual tours?"
6:51 PMNext email gives the time: Wed 11 AM. No word "virtual" in it.
BookedSaved as in person. Jaleigh alone is told virtual + Zoom link.

Why: the booking only reads "virtual" from the email that sets the time. The earlier ask is ignored.

WhereLast nightAfter the fix
Message to Jaleighvirtual tour confirmed + Zoom linksame
Team email"In person"virtual + link
Kat's booking textno Zoom linkvirtual + link
Kat's calendarstreet addressZoom link
AppFolio showingno tour typevirtual
Remindernone (text only, she has no phone)carries the Zoom link too

Gaps we still have

  1. No phone before booking. Zillow sends none and nothing asks yet. The ask-then-book chain is half in: first piece live, second armed, more open.
  2. Relay email is not on her PropFlow contact. On purpose, since an August fix for split duplicates. The AppFolio card has it.
  3. Reminders are text only. A lead with no phone gets none.
  4. The virtual ask never reaches the AppFolio card as a note.
  5. Wrong label. Her team email was tagged "resident inquiry" (a lookup fixed since July). Fixed and merged: prospects now tag Leasing.
  6. Zoom waiting room unknown. The link opens with no sign-in and no registration; we can't see whether a waiting room is on.
  7. Calendar body has no video link. Kat's calendar event body has no video link (only the title and location say virtual); the staff booking text has the link but not the word "virtual".

What is being done

ItemStatus
Carry the earlier virtual ask into the booking: tour type, team email, Kat's text, calendar, AppFolio showing all say virtual with the link; link on the reminder; replay of this exact lead with a planted-defect controlbuilding · fix PR: #21353 (merged, live in production, Willows end-to-end run PASSED 12:30 AM MT Oct 20)
Ask for a phone before bookingpart live, rest in review
Prospects tagged Leasing, not residentfix open
Recovery: Clara emailed Jaleigh and Spencer (another Zillow lead, in person Tue 9 AM, no phone) at 9:57 PM asking for a numbersent; replies watched; numbers go to PropFlow and AppFolio on arrival
Decision: switch Jaleigh's tour to virtual tonight, so Kat's calendar shows the Zoom link, and resend Kat's booking text? Recommend yes.

Unrelated check: Kat's note on Elizabeth, a different prospect, did land on her AppFolio card.

Evidence (for engineers)
ClaimEvidence
Lead and timinginquiry 78ecf4f1 (person pers_9b79fa4b), source zillow, created 2026-10-20T00:28:52Z, stage tour_confirmed, notes "Virtual tour. / Tour requested: Wednesday at 11:00 AM", AppFolio card 3028
Saved in persontour a50c6039 mode in_person, via email_state_machine; team email "— In person"; staff text to Kat without virtual
Jaleigh told virtualClara reply 00:51:36Z, link from org settings.videoTourLink
Mode read per email only, since Julyprocess-tour.ts:1003,1472; apply-tour-intent.ts:1052,1096; since #3166
AppFolio showing, no typeoutbox: create_card (no email/phone), assign, record_communication, schedule_showing → showing 599; no append_note for virtual
Contact empty, relay kept offCONTACT primaryEmail "" primaryPhone ""; relay only as session claim claim_992ed6f4; by design mintable-email.ts:50-55, portal-relay.ts:32-54
Card 3028 has the relaywritten by the drain (#18418); PMSREC row checked
Phone chain#21250 merged + deployed (no effect on this lead); #21254..#21308 open
"Resident inquiry" labelpm_escalation_email msg_a1e93f08 (maintenance.pm-forward); lease→resident_inquiry in handle-forward-to-property-manager.ts:130-145 (#3981); fix #21342 merged
Kat's Elizabeth note landedstaff conv c9f809af, tour 3923bf1d; append_note sent 2026-10-19T23:45:56Z to card 3023
Zoom link settingsus06web.zoom.us/j/83036599249; passwordRequiredForParticipants true, embedPasscode true, authenticatedUsersCanJoin false, isRegister false; waiting room not visible
Recovery emailsJaleigh: conv 32408500, sent_graph 2026-10-20T03:57:12Z from leasing@westernslopepm.com, threaded · Spencer: conv 30520fc7, sent_graph 03:57:21Z, threaded
Fix PRfix PR: #21353 (merged, live, Willows run passed 12:30 AM MT Oct 20)
Jaleigh repair (07:35 AM MT Oct 20)tour a50c6039 mode in_person → virtual (no history entry, no version bump); Kat's calendar event re-rendered "Virtual tour: Jaleigh · 2841 Morningside Court · 249", location "Virtual Tour (video call)"; correction text to Kat with the Zoom link, SID SM7969780023a374a741f19b7db178d1e8 (staff conv c9f809af); card 3028 note "Virtual tour (video call) — Wed Oct 21, 11:00 AM MT" queued as append_note intent gc:append_note:…:mode_virtual:a50c6039 (first attempt ran on the laptop without the robot token and failed; redriven, written by the production robot 2026-10-20T13:45:47Z, state sent); showing 599 untouched; T-24h/T-1h reminders render virtual at send time
Willows live run (rung 4): tour saved virtual when the ask came in an earlier email; renter confirmation, team email subject, staff text, calendar event and both reminders carry the video linktour 5ed948d8-7556-41a5-9f0a-bde0cf65749e (mode virtual, Wed Oct 21 11:00); renter confirmation 06:30:46Z; staff text SM90ce3e45466273bcbf03c35ebc4b0d91; calendar event title 'Virtual tour: Rowan · The Willows · 204', location 'Virtual Tour (video call)'; PMS showing not proven at Willows (unit 204 has no AppFolio unit id, test-data gap)

Kat, Oct 16: "I just had another no show and if I could've called them I could have found out if they were still planning on coming."

Tours since go-liveNo phone at bookingZillow relay onlyRecorded no-showsReminders sent
33762, both no-phone leads0

Today

1Zillow lead arrives with a relay email, no phone.
2Prospect picks a time. Clara books it. Nobody asks for a phone.
3No reminder, nobody can call. They no-show.

Ask, then book

1Clara answers everything as before. No phone needed to talk.
2Time picked, no phone: Clara asks for a number in one sentence. Nothing is written yet.
3Number arrives: Clara books at that moment, saves the number, fills the AppFolio card, schedules the reminder texts. A known number merges duplicates.
What Clara says, the replay, the decisions

Clara: "Friday, October 23 at 3:30 PM works. What's the best number to reach you? I'll book it once I have it."as built, passes the booking-promise guard · name and phone both missing: "Can I get your full name and the best number to reach you?" · Spanish: "El viernes, 23 de octubre a las 3:30 PM funciona. ¿A qué número le puedo contactar? Lo reservo en cuanto lo tenga."

If the slot is gone when the number arrives, Clara offers the next times. No number: no tour exists, nothing to expire, nothing blocked. A phone already on file: booked as today, no ask. Staff bookings: no ask.

Replay, 33 real toursAsked for a numberStaff booking, no askBooked as today
Ask-then-book, real records6 (5 people)126
Ask removed (control)0033
Did any of the 5 ever send a number later?No. Every prospect message in their threads was read (31 messages, all email): none contains a phone number, before or after the tour. The ask would have been the only way to get one.

Decisions (Fede, Oct 19, his words)

D1 Who gets asked: "every lead with no phone number", not Zillow-only.
D2 Hold or not: "ask then book". No hold, nothing written until the number arrives.
D3 Phone into the guest card: "why would we want a setting?" Fills any linked card everywhere, no gate. The separate per-property "guest card after a call" switch is deleted in its own PR, #21124, armed ("we always want this"); note it gated every guest-card write, so notes, call logs, claiming and inactive marking also start at Camellia, Situs and the 20 single homes on merge.
D4 Reminders: "of course we want reminders". The 24h and 1h texts go to the number given for the tour; a number only seen in a signature gets no texts, as today.
D5 Different number than on file: decided by default. Clara never asks when a phone is on file; a volunteered second number is kept and used for the tour.
Harness: 13 cases on the lead-outcome replay, each with a planted-defect control

Extended the existing lead-outcome replay family (no new harness), synthetic records through the real reply and booking path. Red on main (12 of 19 assertions fail), green on the merged branches (19 of 19), and each of 8 planted defects turns the file red. Registered as mutation controls; atlas row added.

#CaseMainBranchPlanted defect that reds it
1No phone, time picked: one ask, nothing writtenredgreenphone treated as on file
2Number arrives: booked, card row queued, reminders armedredgreenconsent skipped; outbox dropped
3Slot gone when the number arrives: next times, nothing bookedredgreencalendar not re-read
4No number ever: no tour, no recordredgreenphone treated as on file
5Phone on file: booked as today, no askgreengreenphone treated as missing
6Staff booking: no askgreengreenstaff context dropped
7Number typed in the reply vs only in the signatureredgreenconsent skipped / consent on a found number
8A different number volunteered (known gap: not saved yet)redgreenphone treated as missing
9Spanish thread: Spanish askredgreenphone treated as on file
10Duplicate person with a known number merges; card row targets the survivorredgreenmerge skipped; outbox dropped
11Zillow relay-only lead end to endredgreenoutbox dropped; consent skipped
12Two prospects, same slot: the second gets the next timesredgreencalendar not re-read
13Team-offered time accepted with no phone, then the number: that tour confirmsredgreenphone treated as on file

Defect the harness found, now fixed: a number typed in an email reply and saved through one prospect tool got no reminder-text consent (only the booking call's phone field granted it), so such a prospect would have been booked without the 24h and 1h texts. Fixed on the branch; that case is red before the fix and green after, and a number pulled from a parsed lead email still gets no consent. 19 of 19 assertions green on the merged branch. Known gap kept: a second, different number is not saved (D5 follow-up). Case 12 proves the calendar is re-read, not the race for the slot itself.

The seven real tours Clara would have asked
TourHomeProspectContact on fileBooked viaOutcomeWith the change
Oct 7, 12:00 PM2841 Morningside CtSara D.Zillow relay only (a duplicate record of hers, from an Apartments.com card, had her phone; merged Oct 9, after the tour)emailunrecordedasked; her number would have merged the two records at once
Oct 7, 1:00 PM2841 Morningside CtBowmuxlow M.Zillow relay onlyemailno-showasked
Oct 9, 5:30 PM2841 Morningside CtBowmuxlow M.Zillow relay only (rebooked after the no-show)emailunrecordedasked
Oct 14, 12:00 PM2980 Emerald CirNatalie H.Zillow relay only; two guest cards (3016, 3017), neither linkedemailunrecordedasked once for both homes
Oct 14, 1:00 PM2841 Morningside CtNatalie H.sameemailunrecordedsame visit day
Oct 16, 3:30 PM2841 Morningside CtAlicia J.Zillow relay only; card 3015 never linked, a second person record minted Oct 16emailno-showasked
Oct 17, 10:00 AMNorthridge AptsTiffani H.Gmail, no phone (Rent. lead); card 3012 has no phonestaff textunrecordedstaff booking: bypass, team already had the thread

Replay result (local build, Oct 18 evening). A script rebuilt each person's contact records as they stood when the tour was created and ran the real gate decision from the local branch over all 33 tours.

RunHeldStaff bypassBookedResult
Gate on, real records61 (Tiffani H., staff text)26every no-phone tour held or bypassed; 0 disagreements with an independent read of the same records
Control: gate forced off0033the check turns red when the rule is removed
Control: a fake phone planted on Alicia J. before her booking5127she flips to booked

What Clara would have said, from the real leasing prompt and the real booking tools fed the real prospect message (the replay harness cannot set its clock back to a past slot, so this is the reply path, not the full loop). All three asked for a phone; the control run fed the line production actually sent said "You're all set, tour confirmed" all three times.

Alicia J. wrote "Does Friday after 330 work for u ?" · Clara: "I've got Friday, October 16th at 3:30 PM held for you. To book it, what's the best phone number for the leasing team to reach you about the tour?"Bowmuxlow M. ("Could I do 1") and Natalie H. ("I can come today at 12 if that works") got the same shape

Two gaps the replay builder found by reading the gate, both sent back to the gate builder to fix on the branch: (1) a held tour could still be confirmed with no phone if the prospect's next message asks to move the time, because the reschedule path never checks contact (main has the same hole for the name rule); (2) a prospect id passed by the model counts as a staff booking and skips the rule. Neither fired in the 33 real tours.

Saving the phone to the guest card, and the duplicate check (D3: no gate)

Today

  • A phone reaches the AppFolio card only when Clara creates the card. Learned later, it stays on our side.
  • Phones flow from AppFolio into PropFlow every 15 minutes, never back.
  • A phone typed into an email reply is never stored as a phone at all; the tools have no phone field.
  • Same phone in one company is already one person (automatic merge), but it needs the phone first.

After

  • New neutral write: "update contact" → outbox → PMS adapter → the robot's existing fill-empty contact route (robot #490, Oct 9). Fills an empty phone or email; refuses to overwrite.
  • The outbox row carries no phone; the current value is read from the person at send time.
  • Loop guard: a phone that arrived from the AppFolio sync is never pushed back.
  • Duplicate check: the given number goes through the identity path, so a match to a person we already know merges on the spot (Sara D.'s case). A matching guest card at the same home links instead of creating a new one; across homes is a known gap on the hub (section 22).

Local build, Oct 18 evening (branch fede/guest-card-contact-intent, 8 small commits). A new "update contact" write kind and its queueing function (one row per card per field; the row carries the field name, never the number). A set-contact method on the shared guest-card writer, its AppFolio version, and a robot client call to the fill-empty route. The drain reads the person's current phone or email at send time and ends in one of four states: filled, no change, already set, no value. The trigger fires only when a field was newly filled and the number came from the prospect or a staff edit, never from the AppFolio sync (that is the loop guard, with a planted-defect control that turns red when the sync's provenance is added to the push list). It rides on the existing per-building "guest card after a call" setting, no new switch.

CheckResult
Drain, writer, trigger and queueing unit tests23 of 23 pass, exit 0 (unit-covered, not "tested")
Mutation control (sync provenance pushed back)green as shipped, red when planted
No AppFolio name outside the adapter layerself-review gate passes
Size1,091 lines over the whole branch; splits into a chain of 4 PRs of about 330 lines when Fede says go
Integration with the phone rulefirst cut hooked the wrong save path; the rule saves through the identity path that merges duplicates, so a second trigger is being added there
Real cardnot yet: next rung is a count of rows it would queue per customer, then one live fill on a Willows bench card

Ownership: the write intent and the outbox are in the PMS layer this session owns (lane 8); the after-reply adapter and side-effects belong to lane 10 and are not touched. No AppFolio name outside the adapter files. The robot checkout on this machine is behind; the route exists on its main.

Local proof (ask-then-book shape, Oct 19)

Both branches are rebased onto current main. Unit tests are listed as unit-covered, never as "tested"; the only validation that counts is the end-to-end run in production after merge, at Willows first.

CheckPhone ask (fede/tour-phone-gate, 10 commits)Guest-card fill (fede/guest-card-contact-intent, 10 commits)
Replay, red before / green afterZillow relay replay: red on main (books confirmed with no phone), green on the branch, 6 of 6: ask once, nothing written, a typed number books it with reminder consent, a signature number books it without, one person. Team-offered time accepted with no phone: red without the fix, green with it.29 of 29 across drain, writer, trigger, queueing, identity-path trigger; no settings row → still queued; survivor after a merge.
Planted defect (mutation control)ask removed → redsync provenance pushed back → red
The two togetherintegration checkout rebuilt from current main plus both branches; the test "a relay-only prospect gives a phone → one guest-card fill row; a known number → the surviving record; sync provenance → no row" passes 3 of 3.
Real-record replay33 Western Slope tours: 6 asked, 1 staff no-ask, 26 booked as today; ask removed → 0; none of the 5 ever sent a number latercount of rows it would queue per customer: with the harness
Harness13 cases on the lead-outcome replay family, red on main, green on the branches, 8 planted defects red (fold above); one D4 consent gap found, fix in progress
Self-review gateeverything passes except size (1,655 and 1,345 lines); each ships as a chain of one-concern PRs of about 300 lines. 0 AppFolio names outside the adapter layer.
Willows live runafter the chain merges: a real relay-only email to the Willows inbox, the ask, a number, the booking, the card fill, the reminder texts, with record ids

Known follow-ups: a volunteered second number (D5) is not built, reminders go to one number per person; a time the team must confirm (no calendar connected) is still recorded as a request with no phone; the harness's fake robot does not know the new fill route.

Finding for later, no decision asked: every note Clara wrote to guest cards since Sep 18

Read-only inspection, Oct 18, of every note Clara queued for a guest card since the outbox began on Sep 18 (596 note-bearing writes across every customer; counts are rule-based over all of them, not a sample). Western Slope is the only live customer with notes: Situs and Camellia have none, and Camellia's 83 cards in the Oct 9 export carry no notes at all.

179
notes landed on Western Slope cards
Sep 18 to Oct 19, 11 failed
41%
are Clara's first email, copied in full
74 of 179; 103 are a whole message word for word, median 66 words
0
notes for a deferral, cancel, reschedule or "not interested"
and 0 link back to the thread
6 words
median staff note at Western Slope
389 notes, 2022 to 2025, Kat wrote 335

Gap count, the 20 Western Slope prospects who wrote back since Oct 1. "Happened" is read from the real threads; "got a note" is any note on the card for that milestone.

MilestoneHappenedGot a noteWhat the card shows instead
Tour booked14411 appear as a scheduled showing, no note line
Rescheduled40the old showing row
Cancelled10nothing
Deferred or not a fit40nothing (one case reached the card only through Kat's after-tour answer)
Application sent98the same sentence every time
Call32a call summary
Went quiet (3+ days)160nothing; the card still reads as "Clara reached out once"
Total51149 of the 20 prospects have only the first email on their card

The deferrals that got no note: Angele T. ("I will reach out"), Ethan S. (busy "for a couple weeks"), Natalee M. ("let me get back to you on timing"), and Erika L. (a home "out of my first range"). One prospect, Tessa L., wrote 20 times and called once, and her card has nothing from Clara at all. Every email logged on a card is a first email; Clara's later emails and texts are never logged.

Clara's note today (real, scrubbed)

"9/18 – Riley C. contacted The Willows to inquire about the availability and earliest move-in date for a one-bedroom unit. The agent, Clara, was unable to confirm current availability from the records as the office was closed. Clara informed Riley that the team would be notified to follow up with the requested details. Riley confirmed the contact number and expressed continued interest in moving forward once the information is provided." (70 words, first contact)

"Email · Subject: Marisol is requesting information about 1601 Blake Street … Hello, is this one still available? Could I tour it?" (the inbound email pasted in)

How the team writes (Kat, real, scrubbed)

"Have a showing scheduled 3/10/24"

"Samantha didn't show up. I texted her and she responded and said she would let me know if she wanted to reschedule."

"I spoke with him today, his max budget is $2,500. I told him about the 1 month free. He will be back in town next week and reach out if he wants to move forward."

"No longer interested." · closes with AppFolio's own reason, e.g. "Reason: Found a Different Location"

The best Clara notes on any card today are Kat's answers to the after-tour check-in, relayed by Clara ("Post-tour check-in from Kat: good fit, looking for end of December move-in"). That is the shape the design below copies. Full report, per-note sheet and 15 scrubbed samples are kept with the session; the Fairhaven test property shows 127 failed notes (no time zone on the property), a test-data defect, not a customer one.

References and queries
PropFlow Docs