The prospect record across homes

Why the team's tour card says only "From Email", why Anthony has three prospect rows, and why the source is wrong in our app and in the customer's AppFolio. Four reports, one hole, three decisions and one fix.

2026-10-29 · updated Oct 30 · Decided · Fede, Oct 30 — all 6 PRs live · audit read-only; the build plan is in Plan

One finding. Per-home rows are the design (Fede, Sep 22: "one card per person, one inquiry per home"). Where the person's facts live was never decided. The code puts the summary, preferences, Zillow facts, move-in and source on whichever row the current thread sits on, and every writer and reader touches exactly one row. The thin card, the wrong source and "why three prospects" are the same hole.

On this page
  1. Anthony's card vs what we knew
  2. Every real tour card, 118 of them
  3. Why one renter has three rows
  4. Lead source: wrong here, blank in AppFolio
  5. Decisions
  6. Bad patterns and where they came from
  7. Method and limits

1. Anthony's card vs what we knew

What the team got (Oct 29, 9:50 AM)

Tour confirmed · Ember Estates Anthony Mccleary — In person When: Thu, Oct 29 · 2:30 PM Unit type: 3 Bed Proposed unit: 363 Ember Ln Phone: +1 (970) ••• 9369 About the prospect: From Email

What we knew six minutes earlier

  • Two kids, wants a yard to play and a garage to work from
  • Wants to move before winter, sooner than the November date he first gave
  • Zillow: credit 300 to 579, income about $86k, has pets, 12-month lease
  • Asked "do you also have any in the Redlands"
  • Came from Zillow, not email
How the rows connect, and the bug inside the refused booking
ONE PERSON Anthony (person) ONE ROW PER HOME 2841 Morningside Ct · Zillowkids · yard · garage · credit · income · pets 2980 Emerald Cir · Zillowone-line summary, move-in Nov 10 363 Ember Ln · "email"empty · made at booking, 15:47:01 WHAT READS IT Team tour cardreads this one row only → "From Email" AppFolio card 3043one card, three inquiries. Correct.Ember and Emerald rows: source blank
AppFolio follows the Sep 22 design. Our rows follow it too, but the person's facts sit on one row and the card reads another.

The empty Ember row and a third household were written inside the first booking attempt, the one refused for lack of a phone, 0.8 seconds in and before the phone check. That part is a plain bug. Fixing it alone would not have helped him: his facts would still be on the Morningside row.

2. Every real tour card, 118 of them

Every real tour card, 118 of them
639
tours ever
501 test (Willows, Yale sandbox, Fairhaven, demo)
138
real customer tours
118 with a stored card · 18 never sent · 2 sent, not stored
54%
cards said only "From …"
Western Slope 79% · Camellia 42%
19%
complete
everything we knew was on the card
WHY THE CARD WAS THIN (a card can have several causes; n = 118) Facts only in the conversation, never on any record34% (keyword match, upper bound) Wrong source label18% Zillow facts (credit, income, pets) existed, not shown15% (no write timestamp, upper bound) Clara summary existed, freshness gate dropped it12% confirmed · 39 more cards undecidable Richer sibling row for the same person6%, all Western Slope (Anthony's pattern)
WHEN IT BROKE: SHARE OF BARE CARDS PER CARD VERSION to Jul 1915% Jul 20 to Sep 360% Sep 12 to 16100% (3 cards) Sep 17 to 2570% Sep 26 to now66% Before Jul 20, 85% ofcards carried asummary paragraph.
Versions: old text card (to Jul 19), glance card (Jul 20), freshness gate (Sep 12), person notes removed (Sep 17), "About the prospect" fallback (Sep 26). The July 20 redesign cut the summary; each later change kept the card bare. In 13 of the 14 confirmed freshness drops the summary was written 0.2 to 6.7 minutes before the booking, in the same conversation.

Examples the team never saw: "do you require a credit check" and "four-bedroom in Orchard Mesa" (Western Slope, Oct 7); a student calling from abroad on her mother's phone (Camellia, Sep 23); "is it only street parking" and short-term lease pricing (Camellia, Jul 21). Also: a Zillow "pets: Not answered" renders as a chip reading "Not answered".

3. Why one renter has three rows

Why one renter has three rows

By design, one row is one renter at one home. Western Slope has 45 people with two or more rows, Situs Group 32, Camellia none. All 109 extra rows trace to a cause:

WHAT CREATED THE 109 EXTRA ROWS 82 · AppFolio sync: one card lists several homes, we make one row per home 11 · 2nd lead 8 · card 6 app 8 = a second AppFolio card for the same person · 6 = a rental application row 1 = a phone call at another home · 1 = a tour booked at a new home (Anthony) 3 people have two rows at the same home: real duplicates.
Stopping the booking from minting rows fixes 1 of 109. The fix is where the facts live, not how many rows exist.

4. Lead source: wrong here, blank in AppFolio

Lead source: wrong here, blank in AppFolio
#Bug, plain EnglishProven real rowsFix belongs in
1Adding a home to an existing AppFolio card sends no source4 of 4AppFolio adapter
2Clara-made Zillow cards show "No Source Recorded" in AppFolio5 of 15Robot; read back and repair
3A new row (new home, front door, after-call) takes the channel (email, text, phone) as its source, never the person's origin. Four create paths.WS 3 · Camellia 3Core: new-row factory
4A renter's first row labelled by channel though the lead was a Zillow relay or a website formWS 2 · Camellia 2Lead intake
5Merging two rows keeps the survivor's label and drops the Zillow origin2 (both with tours)Core: merge
6A booking writes "Email" / "Text" / "Phone" into AppFolio's Source; a call always writes "Phone Call" even when we know Zillow12 cardsAppFolio adapter
7Two different text-to-source mappers disagree (Trulia → Zillow in one; Zumper, Realtor, Facebook → "website" in one)not countableAdapter: one mapper
8Team card, cancellation card, owner report and dashboard count one renter once per home, under each row's labelevery multi-home renterCore readers

Also found: a Zillow lead whose new row is labelled "email" may not get automatic text consent, because consent checks the row's source (inference, not measured). Rows keep no source history, so "a later email overwrote the origin" is visible in code but not countable.

5. Decisions

Decisions

D1. Where do the person's facts live? Decided · Fede, Oct 30

Fede chose the person record: "if it's a fact about the person, that's separate from the home… it belongs on the person record." Both reviewers (Fable 5.1 and Opus 5.5, Oct 29) had argued against that destination. Their objection was the migration risk: 8 writers, 82 readers, and a one-shot fold that loses data. So the move is gradual, and nothing switches over at once:

  1. Dual-write. New facts go to the person record and to the per-home row they land on today, so no existing reader goes stale.
  2. One read view. It reads the person record first and folds the rows as the fallback (rules below).
  3. Move on touch. The first time the card or a conversation turn reads a renter, the fold is written onto the person record.
  4. One sweep for the remaining ~700 real renters.
  5. Fallback deleted a week later.
FieldFallback fold, when the person record is empty
SummaryNewest, by its timestamp
Credit, income, pets, lease, bedrooms, move-inBooked home's row first, else the newest row that has it; "Not answered" dropped
Wants, other homesUnion of every row
SourceEarliest real listing or website origin; never a channel (D3)

Checked on Anthony's real rows: the newest summary is the rich Oct 29 one. Fede: "I just don't want anything to break. So feel free to run for 24 hours or however long you need."

Owner: lane 8 (identity, prospects, mirror).

D2. What the team card shows about the prospect Decided A · Fede, Oct 29 ("this is great")

Tour confirmed · Ember Estates
Anthony Mccleary — In person
When: Thu, Oct 29 · 2:30 PM
Unit type: 3 Bed
Proposed unit: 363 Ember Ln
Phone: +1 (970) ••• 9369

About the prospect
Two kids, wants a yard to play and a garage to work from. Wants to
move before winter, sooner than the November date he first gave.
Also asked about 2841 Morningside Ct and 2980 Emerald Cir.

Credit 300 to 579 · Income about $86k · Has pets · 12-month lease · From Zillow

For the team at the tour
• Asked whether you have anything in the Redlands right now.

Summary, facts, origin and homes come from the person view (D1). "For the team at the tour" lists questions the renter asked that nobody answered. The asks declaration live today ran on first emails only for five weeks and and caught 4 asks while 26 real questions arrived on later turns (1 answered, 1 left hanging, 1 dismissed, 1 silently lost). The person-level notes log stays off the card (Sep 17 rule).

Detail changed by both reviewers: the freshness gate is deleted, not retuned (its own code comment says it always fires on an instant booking). Unanswered team asks: read from the raw messages at card time, up to three quoted questions, nothing new stored (Opus; recommended, lowest risk, matches the raw-transcript rule). Alternative (Fable): declare asks on every turn and open team-note matters; more notes to the team; deferred to a later cleanup.

D3. Lead source model Decided · Fede, Oct 30

Fede: "Lead source means it's not how they wrote to us. It's where they're coming from… a well-defined problem. We don't need to be reinventing the wheels… AppFolio already has it. If AppFolio is a system of record and it says something, we shouldn't try to mess with it." And: "when we create the guest card, like Clara, we should make sure that the lead source attribution is correct."

  • Source = origin: where the renter found the home. How they wrote to us (email, text, call) goes in its own field, sourceChannel.
  • The card Clara creates carries the real origin: Zillow relay → Zillow; web form → website; a caller who names a site → that site; a caller who names nothing → Phone Call.
  • An existing AppFolio Source is never overwritten.
  • One mapper, using the PMS's own Source list as the labels.
  • Read-back repair after a write fixes only blanks we left.

Rows proven wrong today: 19 (Western Slope 8 and Situs 6 by a sibling record's listing-site evidence; JP&Co 5 from the raw conversation), 2 reached a team card.

Plan: 6 PRs, cut by proof

#WhatKindProof before the next starts
1Person view (fold only, nothing written) + the approved card on both layouts, freshness gate deleted, "Other homes" lineimprovementReplay all 118 real stored cards offline: every fact the old card showed is still there; the 7 multi-row cards show the diff. Fairhaven live: a second-home booking by email, card shows the summary and facts
2Team asks on the card, read from the raw messages at card time (up to 3 quoted unanswered questions), nothing storedimprovementReplay the deferred-asks cases plus a planted answered-question control; Fairhaven live
3Person facts record + one write helper; the 8 writers write there (and still to the row); the view reads it first; move on touchimprovementReplay: for every real renter the view reads the same before and after, field for field. Fairhaven live: a new renter's facts land on the person record; a booking at a second home shows them
4Sweep for the remaining renters (ledgered, run once PR 3 has been live 24 h); fallback removal a week later as its own PRops + cleanupDry-run counts first; sample 20 renters before and after
5Source on our rows: new rows inherit the origin, the channel goes to its own field, merges keep the earliest origin, two mappers become onefixReplay every real row: the 19 proven-wrong rows come out right, all others identical. Fairhaven live: a Zillow relay email gives origin Zillow, channel email
6Source in the AppFolio write-back: add-home carries it, the PMS value is never overwritten, read-back repairs our blanks, calls keep a listing originfixWrite-back history replay; JP&Co Willows test property 45: create, add a home, read back; a planted blank gets repaired

Order: 1, then 2, 3 and 5 in parallel; 6 after 5; 4 after 3 has been live a day. Acceptance test for 5 and 6: replay every card Clara created and show the Source each would carry now vs then. Fede: run for 24 hours or however long you need; nothing breaks.

6. Bad patterns and where they came from

Bad patterns and where they came from

The "prospects are strictly per-property" rule came from an Apr 17 PR written by Claude, merged with no review and no ADR, reversing the previous day's ruling that identity belongs to the person. Fede's own words that night were "you could call 2 different properties, that should be 2 different prospects" then "well lets think about it more"; in the May ADR Fede appears only as an AI persona stand-in.

Both reviewers' plans keep Fede's Sep 22 rule: one person, one inquiry per home. "Later cleanup" means its own PR after the plan.

#PatternWhere it came fromWhat it costs todayRemoved in
1"Prospects are strictly per-property" written as a design rulePR #175 (Apr 17), Claude-written, no review; repeated in the May ADRsEvery fact filed under one home: 109 extra rows, 7 cards read an empty sibling, four other readers take "first row"PR 3 rewrites the rule to Sep 22's; the four readers in a later cleanup
2Person facts stored on the per-home row (summary, Zillow facts, preferences, homes list, move-in)May 26 ADR moved the old record's fields there; PR #4196 (Jul 20), PR #6703 (Aug 31), PR #10796 (Sep 22) added more15% of cards miss facts a sibling row holds; Zillow facts carry no datePR 1 reads them as one; storage moves only if the replay shows real conflicts
3Three different "fold the person" rulesPR #10115 (Sep 20) for the prospect page; PR #10292 (Sep 20) for the list; none for the cardBare card; every new screen re-invents the foldPR 1; the prospect page moves onto the view in a later cleanup
4A freshness gate that always fires on an instant bookingPR #8083 (Sep 12); its own comment says "always, not sometimes"14 cards lost their summary; 39 more undecidablePR 1 (deleted)
5The person summary cut from the cardPR #4027 and PR #4190 (Jul 17 to 20), glance redesign; PR #9102 (Sep 17) hard-coded no notesCards with a summary fell from 85% (to Jul 19) to 11% (since Sep 26)PR 1; the approved card is the newer ruling
6The channel (email, text, phone) written into SourceAn early April fix wrote "email" for email leads; PR #2741 (Jun 30) wrapped it; four create paths copy it19 proven-wrong rows; "From Email" on Anthony's card; possibly missed text consent (inference)PR 3
7Two AppFolio label mappers that disagree, and stored labels never re-mapped after a fixPR #5578 (Aug 9) beside the older shared mapperTrulia and HotPads become Zillow; Zumper, Realtor.com and blanks become "website"; 7 stale rowsPR 4
8The AppFolio write-back drops or invents the sourcePR #9105 (Sep 17) fixed "Phone Call"; PR #9581 (Sep 18) add-home with no source fieldAll 4 homes added at Western Slope show a blank source; 2 Zillow calls written "Phone Call"PR 4
9A merge keeps the surviving row's label, not the originPR #9860 (Sep 19) carries engagement fields only2 renters, both with tours, lost "Zillow" and "website"PR 3
10An asks tool that only echoes the forward toolPR #12346 (Sep 25), first email turn only59 calls, 4 non-empty, all already forwarded; 0 notes sent; 26 later-turn questions missedLater cleanup, after PR 2
11A team question marked "resolved" by an unrelated staff textHow team-question matters are closedA lease-term question closed unansweredLater cleanup, its own fix
12One household per homePR #5377 (Aug 4), "one household is one deal"Anthony has three householdsLater cleanup; the card does not need it

7. Method and limits

Method and limits
  • Read-only pull of production on Oct 29, key queries only. Every real customer tour, not a sample. Test companies and personas excluded by rule; the rules are listed in the run files.
  • "Facts only in messages" is keyword matched: an upper bound. Lead facts carry no write timestamp, so "existed at send time" is inferred from row age. Booking channel was inferred for 59 of 118 cards from the renter's last message.
  • Situs Group has one real tour. Yale 25 Station has no customer tours yet. "Never sent" for the oldest Camellia tours predates the card log and is an inference.
  • Both classifiers passed a planted-defect control (a complete card scored complete; a planted sibling gap scored as such; a planted source disagreement flagged, an agreement not).
  • Fix proof log:
    • #23575 merged and live. Fixes Clara's tool path; not exercised live, because the email pipeline answers first.
    • #23610 merged and live, failed live: the router passes no conversation id when the thread sits under another building, so rows went 61 → 62.
    • #23635 in CI: the gate finds the person without a conversation id.
    Two fixes passed their red/green tests and failed live. The test has to model the real route, and only the live rung counts.
  • Design history: Fede's rulings of Sep 19 to Oct 10 and PRs #10796, #18286, #18305, #18673, #20952, #21619, #21631. Code at main c43845abef. Raw customer records pulled for the audit (per-person dumps, message text, database pulls) were deleted after publishing; the two classifier reports with file and line references were kept for the builder.
PropFlow Docs