Five ways to structure the phone agents behind dozens of client companies. What each buys, what kills it, and the four options for Fede to pick from.
Decided for the voice agents only — Option A, Fede, Sep 5 (one shared set of agents; when the line points at a company, Clara asks which building). Fede, Sep 6: the binding mechanism (how a number and the voice tools bind to a company, a group, or a building) is left OPEN, to be set by the overall architecture study · written 2026-09-05 · for Fede and Gera · built from eight research files: ElevenLabs capabilities, a competitor survey, a read-only audit of our own code, and one adversarial review per pattern · companion to onboarding-plan-situs-western-slope
The answer: keep one shared set of phone agents for every client, and add one new thing — the agent is allowed to not know which building the caller is calling about, and to ask. That is pattern P2, and it is not a rebuild: Camellia and Yale already ring the same agents today, told apart only by data we hand ElevenLabs when the phone rings. The real work is not the agents at all — it is server-side: letting a phone number point at a company instead of a single building, letting Clara stamp the building once the caller names it, and fixing two data-access holes we found tonight. Until that work lands, no shared line should point at a real portfolio. Western Slope can go live first on a single-purpose prototype line that is designed to be deleted.
Every one of the five patterns was scored, attacked, and defended. They mostly converge on the same place, because the thing that actually blocks Western Slope and Situs is not the shape of the agents. It is this:
Everything follows from the answer.
Western Slope is ~14 scattered homes on one line. Situs is ~30 buildings with one maintenance line. Both say "a company". So does the biggest player in the market: EliseAI, with over a million units on centralized operations, resolves the building by asking the caller — and the industry association's own research says centralizing is hardest exactly for third-party managers serving many owners with different policies, which is our shape.
The one thing that could still make the answer "a building" is a customer preference nobody has asked about yet. Latchel — the only vendor in the whole survey that publishes its mechanism — gives each property manager one unique hotline number. If Jay and Hugo are happy with a number per property, the cheap path opens back up. That is a customer question, and it is unanswered.
"One shared agent, the building supplied as data" is not a proposal. It is production, at two brands, today.
Each was given to a separate reviewer whose job was to attack it and then argue its best case honestly. VERIFIED means it traces to a cited file, page or line of our code. ARGUED means it is the reviewer's reasoning.
What it is. The ~11 agents we have now, serving every client. Each call is personalized in the split second the phone rings: which building, its hours, its policies, who is calling.
Strongest case. It is not a design, it is a description — Camellia and Yale run it. Zero migration. Zero new maintenance. Every voice vendor's own documentation recommends exactly this and warns against copying agents per customer. It is also the only option that leans on no unbuilt platform work.
The killer objection. VERIFIED It cannot serve a centralized client at all — not slowly, at all. Point the shared line at one nominal building and every resident of the other buildings is silently treated as a stranger, because our code drops a caller's tenant record when it disagrees with the building the number maps to. Leave the number unmapped instead and the call adopts the caller's building — which is a leak we already have filed and are trying to close.
Scores — scale 2 · maintenance burden 5 · isolation 3 · time to first centralized client 1 · migrating Camellia/Yale 5 · reliance on unbuilt work 5 · vendor lock-in 4
What it is. P1 with one addition. A number can point at a company. When it does, the front-door agent holds an unresolved building, asks which one, and stamps the answer onto the call. Every specialist and every tool afterwards reads that stamp.
Strongest case. VERIFIED The stamping machinery already exists and already runs — a call that does not yet know its building gets one filled in mid-call, and then it is locked and cannot be moved. The safest class of our tools already re-reads it fresh on every single tool call. Gera specified the same design independently from the data side, months before this review. And it turns a filed leak into a designed branch.
The killer objection. VERIFIED It must deliberately re-open the exact gate that a real incident closed nine days ago. On 2026-08-26 a Yale caller was answered as somebody else, because a person who rents at both Camellia and Yale was matched to whichever tenancy came back first. The fix was to lock identity to one building. P2 widens that lock from "one building" to "the buildings in this company" — the precise inverse — and the nearest surviving guard only compares the person, so it cannot tell the two leases apart. ARGUED Also unproven: whether the facts we hand the agent at ring time survive a hand-off to a specialist. Our own design note admits it inferred this rather than testing it, in a document that already contains one wrong assumption of the same kind.
Scores — scale 4 · maintenance burden 4 · isolation 2 · time to first centralized client 2 · migrating Camellia/Yale 5 · reliance on unbuilt work 1 · vendor lock-in 2
What it is. Fork the agents in two. On-site clients get one family; centralized clients get another, with its own greeting, its own turn-taking, its own tool set.
Strongest case. The two really do have different jobs. A company greeting with a routing question is a different speech act from a building greeting. A front door that must hold "not known yet" across turns and one that knows at ring are genuinely different shapes, with different speed budgets. And tonight's Western Slope prototype is already, accidentally, family number two.
The killer objection. VERIFIED Situs is in both families on day one — one central maintenance line, ~30 buildings that each need their own leasing identity. The split is not a property of the client; it is a property of each phone number, which is a three-valued field, not a code fork. And the cost lands on the one thing already failing: on 2026-09-03 five of our seven live prompts were running thousands of characters behind the code, with every test green. P3 doubles that surface. VERIFIED The promise that a shared core would be rendered into both families is already false — the prototype shares one of five common blocks and about 1–2% of its text.
Scores — scale 2 · maintenance burden 1 · isolation 2 · time to first centralized client 3 · migrating Camellia/Yale 4 · reliance on unbuilt work 1 · vendor lock-in 2
What it is. What we do for the Willows test property, done for every customer: their own copies of the eight or eleven agents.
Strongest case. Blast radius, and it is real. A bad prompt on a shared set is a thirty-client incident; on a private copy it is one client's problem. Per-client voice, language mix and rollback become plain configuration. And it costs nothing extra at ElevenLabs — their pricing has no per-agent line, so 240 agents cost what one costs. If a large manager ever contractually demands isolation, this is the answer that sounds true.
The killer objection. VERIFIED The isolation is cosmetic where it matters: all 240 copies call the same server code with the same scoping, so a data leak leaks identically. It reverses a written standing rule, it needs cloning tooling that does not exist (our one existing copy cannot even be rebuilt from the repo), and the copy multiplier lands on 45–85 prompt changes a month. And it does not answer Western Slope's phone: a copied agent still resolves a number to exactly one building.
Scores — scale 2 · maintenance burden 1 · isolation 3 · time to first centralized client 1 · migrating Camellia/Yale 2 · reliance on unbuilt work 1 · vendor lock-in 2
What it is. ElevenLabs "Workflows": instead of separate agents handing off to each other, one agent contains a graph of nodes, and the call walks the graph. Branches can be decided by data rather than by the model's judgment.
Strongest case. Two genuine upgrades. Routing stops being a judgment call — today the front door decides where to send a caller by reading English instructions, and a graph edge can decide it deterministically. And a node carries its own knowledge and tools as fixed configuration, which sidesteps a real platform limit: personalization can only be applied when a call starts, never when it is handed off mid-call.
The killer objection. ARGUED, well-grounded That headline advantage solves a problem we do not have — we never put per-building data in ElevenLabs' knowledge base; it lives in our server and our tools, which re-read it on every call. Meanwhile we have never used Workflows once: the flag for it is off in all 18 places it appears across our config. Adopting it means rewriting every drift check, every sync lane and every frozen snapshot — the exact machinery that is currently the only thing standing between us and prompts silently going stale. It does not move the first centralized client one day closer.
Scores — scale 4 · maintenance burden 2 · isolation 3 · time to first centralized client 2 (≈4 for the narrow "front door only, keep the 11 agents" version) · migrating Camellia/Yale 1 · reliance on unbuilt work 2 · vendor lock-in 1
1 = bad, 5 = good, in every column — including "reliance on unbuilt work", where 5 means it depends on nothing that still has to be built. Scores are each pattern's own adversarial reviewer, not a single hand.
| P1 shared agents, data per call | P2 P1 + "building not known yet" | P3 two families | P4 copy per client | P5 one flowchart agent | |
|---|---|---|---|---|---|
| Scales to dozens of clients | 2 | 4 | 2 | 2 | 4 |
| Cost of keeping the core working | 5 | 4 | 1 | 1 | 2 |
| Keeping clients' data apart | 3 | 2 | 2 | 3 | 3 |
| Time to the first centralized client | 1 | 2 | 3 | 1 | 2 |
| Cost of moving Camellia and Yale | 5 | 5 | 4 | 2 | 1 |
| Freedom from unbuilt platform work | 5 | 1 | 1 | 1 | 2 |
| Freedom from ElevenLabs lock-in | 4 | 2 | 2 | 2 | 1 |
| Agents to run at 30 clients | ~11 | ~11 | ~22 | ~240 | ~1–3 |
Every option that lets one number serve many buildings needs the same server-side program. None of it is agent work; all of it is ours, not ElevenLabs'.
ChannelBinding design: an incoming address resolves to a scope. A scope wider than one building yields an unresolved building — which is a normal state, not an error.A third, already known: the company-scope envelope Gera specified and Fede approved has zero of its four rollout stages built, and it does not cover the phone surface at all. P2's safety story leans on it. Budget it; do not assume it.
The rule, in one sentence: a phone number, mailbox, or email address belongs to exactly one client company, and everything that happens on it, identity, role, persona, knowledge, units, tours, work orders, is limited to that company. People do not leak between companies either (Fede, Sep 5): the lookup that decides who a caller is reads only the owning company's rows. This is the Operating Constitution, Article VII.2: "a request for one organization can never read or write another's rows; org resolution happens once, at the edge." Where the older identity design (one person record shared across companies, looked up globally by phone) conflicts with that on a read path, the constitution wins.
Camellia and Yale are untouched: their numbers stay building-scoped, so the "which building?" branch never fires for them.
Three of the five steps already work. The two that don't are both server-side.
Week figures are our estimates, not measurements from any source; the only sourced estimate is the 3–5 weeks for per-client cloning tooling in Option D.
| # | Open question | The one probe | What it changes |
|---|---|---|---|
| Q1 | Do the facts we hand the agent at ring time survive a hand-off to a specialist? | One test call on Willows: hand off mid-call and read the specialist's transcript for a value only the ring could have set. | If no, P2's specialists are permanently stuck with whatever the ring guessed, and P5's node approach wins on merit. |
| Q2 | Can scattered-site callers actually name their property? ("the blue house on Elm") | Ask Jay what callers say today, and check the recordings from the prototype line's first week. | If they usually can't, "ask which building" is a dead end and numbers-per-property is the honest answer. |
| Q3 | Would Western Slope and Situs accept one phone number per property? | Ask Jay and Hugo directly. The competitor survey names this as the single most decision-relevant unknown, and no vendor publishes an answer. | A yes makes Option B strictly the best path. A no confirms Option A. |
| Q4 | What is ElevenLabs' enterprise ceiling on simultaneous calls, and the price per extra one? | One email to ElevenLabs. Self-serve stops at 40 at once; nothing published above that. | Every pattern shares one pool. This sets when we must renegotiate, independent of the architecture. |
| Q5 | Inside a flowchart agent, does node configuration quietly override our per-call personalization — and can we score each node? | Ask ElevenLabs in writing, then confirm with the shallow graph in Option C. | If node settings shadow our overrides, P5 silently removes the lever P1 and P2 both depend on — and we would find out in production. |
Update 2026-09-08: this section describes the prototype as it was on Sep 5. It now binds the four production leasing tools, books real tours and reads its homes from the customer's public listings page. The plan for executing its removal trigger is section 11.
It is a demonstration on a separate number, not the first brick of an architecture. One self-contained agent on +1 970-822-0641, with no tools and a hand-maintained list of homes, deliberately kept out of the agent registry and every sync list so no fleet update can reach it. It answers leasing calls forwarded from their line when nobody picks up, takes the details, and passes them to the team.
Read strictly, it is a second family of agents — the thing this study recommends against. What keeps that honest is the removal trigger its own author wrote into it:
It also earns its keep as a probe: it is the cheapest place to hear what real scattered-site callers say when asked which home they mean, which is question Q2.
| Area | Fact |
|---|---|
| Concurrent calls | Free 4 · Starter 6 · Creator 10 · Pro 20 · Scale 30 · Business 40 · Enterprise custom. Burst up to 3× the plan limit, excess billed at 2×. |
| Cost | $0.08/min overage all tiers ($0.16 burst); Business $990/mo including 12,375 minutes. LLM cost is passed through separately. |
| Agents | No documented cap and no per-agent pricing axis — many agents cost the same as one. |
| Multi-tenancy | None. No sub-accounts or child workspaces; only seats (Scale 3, Business 10) plus Enterprise terms. |
| Numbers | One phone number = one agent (agent id on the phone-number record). |
| Per-call overrides | Prompt, first message, language, voice, model, tool list and knowledge base can all be swapped per call — but the tool and knowledge overrides replace the defaults, each field must be pre-enabled, and overrides apply only when a conversation starts, never at a mid-call hand-off. |
| Hand-offs | transfer_to_agent preserves the full transcript and the conversation id; the child runs its own prompt, voice, tools and knowledge. Since 2026-06-01 it can target a specific node and the destination agent id may be null. Whether custom variables survive the hop is NOT CONFIRMED by the vendor. |
| Knowledge base | 20MB per file; full-context only if the extracted text is ≤ ~300,000 characters, otherwise retrieval, which adds ~250ms. Index budget 1GB on Business. |
| Workflows | Subagent / tool / transfer / end nodes; edges can be model-judged, deterministic expressions, unconditional, or result-based. Node/edge limits and deep-graph latency NOT CONFIRMED. Stored in the agent config, so repo-authorable and CI-writable. |
| Telephony fork | Out-of-band keypad tones are SIP-only and ignored on Twilio Native; warm transfer messages, post-dial digits and caller-ID-preserving blind transfer are Twilio-Native only. You cannot have both on one number. |
Source pages: pricing/agents · overrides · twilio-personalization · agent-transfer · agent-workflows · rag · phone-numbers. Most pages were read through a summarizing fetch, not verbatim HTML — spot-check exact field casing before hard-coding.
Headline finding: nobody publishes their architecture. Across ~25 vendors there is not one engineering blog, patent or technical doc explaining how an inbound call is matched to a property, unit or resident. Only four mechanisms exist in the wild, each used alone by someone:
| Mechanism | Who | Evidence |
|---|---|---|
| One tracking number per property or per manager | Latchel (explicit, best-documented in the survey); Knock/ILS ecosystem (inferred); Zuma inherits the client's existing numbers | Latchel phone numbers · Zuma VoiceAI |
| Ask the caller, on a building-group number | EliseAI — 500–600+ clients, 1M+ units on centralized operations | support article (403s on fetch; detail rests on a search snippet — re-verify) · Thesis Driven deep dive |
| Caller ID matched to a resident record | EliseAI for known residents; inferred for Yardi and AppFolio | AppFolio Smart Maintenance · Yardi Chat IQ |
| A human looks it up in the PMS | EZ Repair Hotline (a human call centre, not AI-first) | Zapier writeup |
Nobody publishes a hybrid or a confidence ladder — that is unclaimed ground. Other load-bearing findings: every voice platform's own docs say personalize per call rather than clone per customer (ElevenLabs, Retell, Bland, Vapi); clone-per-client is the white-label agency billing model, not an engineering pattern; EliseAI's published staffing model is 1 specialist per 1,200–2,500 units across 7–10 properties with AI absorbing ~90% of initial inquiries (Centralized Leasing 101); the NAA reports centralization is hardest for third-party managers serving many owners with different policies (NAA); tracking-number pools collide and forwarded numbers accumulate carrier spam flags (WhatConverts, Caller ID Reputation); wrong-tenant retrieval has a standard fix — filter by tenant before ranking (OWASP, Pinecone). No public voice-AI cross-tenant leak post-mortem exists for any vendor; a circulating 2024 Salesforce story could not be traced to a primary source and must not be cited.
Read-only analysis of ~/.claude/propflowai at origin/main, tip 828c013f67, 2026-09-05.
| Claim | Where |
|---|---|
| Camellia and Yale share one triage agent; three production numbers total | config/elevenlabs-phone-numbers.json:8-30; agents/clara/lib/agent/specialists/fleets.ts:91-123; .github/workflows/sync-camellia-fleet.yml:13-20 |
9 specialist slugs, 11 active modules, one cloned fleet (appfolio-45), 25 live agents | specialists/registry.ts:56-130; voice-agents/index.ts:303-317; specialists/fleets.ts:65-78; docs/voice/drift-test-audit-2026-09.md:170-177 |
| ~70 facts injected once, at ring, for one building | src/app/api/voice/personalization/route.ts:195,247-263,423-425,1943-2272 |
| A number maps to one property; the outbound map is the inbound map inverted | src/lib/domain/properties/phone-lookup.ts:122-129,167-289,327-345,446-450 |
| Mismatched tenants are dropped — every resident of other buildings becomes a stranger | personalization/route.ts:457-466 |
Unmapped number adopts the caller's property — filed fail-open, Trello 4XfnI8gO | personalization/route.ts:538-576 |
| The building stamp already exists: filled only when empty, then locked | src/app/api/voice/tools/[tool]/route.ts:695-735 (semantics at :704-706); tools read it at :349 |
| Yale #2 — cross-property identity leak inside one org, fixed by gating harder | PR #6334, 2026-08-26; src/lib/domain/identity/resolve.ts:443,460; src/lib/data/store.ts:662,735; src/lib/data/dynamo/property.ts:883; regression at src/__tests__/voice/yale-caller-identity-cross-property-same-org.test.ts:139 |
| Defect 1 — renewal tools on voice: ownership check never runs (no conversation id passed), and when it runs it compares person only | agents/clara/lib/agent/tools/renewal/handlers.ts:1637-1658; src/app/api/voice/tools/[tool]/route.ts:1020; src/lib/domain/identity/assert.ts:29-51; prompt instruction at renewal-inbound.ts:204 |
| Defect 2 — appliance lookup reads the whole fleet and matches on unit-number suffix; no guard watches it | src/lib/tools/handlers/handle-get-unit-appliances.ts:44-56; guard scope at src/__tests__/unscoped-fleet-read.drift.test.ts:44-49 |
| 10 grandfathered unscoped fleet reads across 8 caller-facing files; a ratchet, not an approval; blind to indirect reads | unscoped-fleet-read.drift.test.ts:1,25-32,57-90; prior audit PR #5737, 2026-08-13 |
| Five different trust models for how a building reaches a tool, side by side in one agent | handle-get-lease-terms.ts:112-146; handle-get-tenant-balance.ts:214-216; handle-create-work-order.ts:695-726; tools-leasing.ts:791-833,5110-5136; handle-find-turnover.ts:39-58; src/lib/integrations/voice/tool-helpers.ts:100-106,128 |
| Prompt sizes: triage ~89K live characters, leasing ~79K | drift-test-audit-2026-09.md:40-57; source files 101,154 and 92,163 bytes |
| 2026-09-03 drift audit: 5 of 7 live prompts 3,892–7,308 characters behind main, whole suite green; CI had no ElevenLabs key | docs/voice/drift-test-audit-2026-09.md:18-57,115; fixed at .github/workflows/ci.yml:929 |
| Change velocity: 173 commits to the voice agents since June 1 (Jun 25 / Jul 86 / Aug 45 / Sep-to-5th 17) | git history of agents/clara/lib/voice-agents/ |
| Merge reaches Willows only; real customers need a hand-typed dispatch; renewal prompts reach no fleet and the test pins that | sync-specialists.yml:262-290; sync-camellia-fleet.yml:54-57; drift-test-audit-2026-09.md:212-217 |
| Three agents have no test clone; the isolation test prints SKIP rather than failing | scripts/__tests__/fleet-isolation.test.ts:63-79; drift-test-audit-2026-09.md:206-209 |
| No agent may be changed by hand, anywhere, including test fleets — no bypass flag | ~/.claude/scripts/el-agent-cicd-guard.sh:15-36 (Fede, 2026-08-28) |
| The written rule against per-client clones | docs/voice/AGENTS-PLAYBOOK.md:15-18 and :114 |
"A type error in the model, not a missing row"; the ChannelBinding proposal; "the model should be allowed to not know yet" | docs/data-model-migration/architecture/entity-model.md:276,542,547,556-572,586; why-folders-break-multi-tenant-software.html (Gera, ~2026-09-01) |
| Company-scope envelope decided but unbuilt: zero of four stages landed; the phone surface is outside it entirely | ADR-0120 :73-84,103-112; src/__tests__/property-scope-surface-registry.drift.test.ts:21,171-191; Gera's 2026-09-01 audit in entity-model.md |
| Company and building checks both run; company membership does not imply access to every building | ADR-0019 §3.1 :10,225-233; ADR-0020 invariant #6 :84 |
| Absence is not neutral — an unconfigured property is born fully live, with escalations reaching nobody | computations.ts:79-83 |
| Workflows have never been used here — the flag is false in all 18 places across 7 config files | *.config.json, is_workflow_node_transfer |
| Hand-offs carry the conversation id and the child runs its own config — but "carries dynamic variables" is inferred, not probed | ADR-0048 :41 |
| The Western Slope prototype: 41,652 bytes, one shared import, zero tools, outside every registry, with a written removal trigger | agents/clara/lib/voice-agents/prototypes/western-slope-portfolio-triage.ts |
| Bucket | What it is | Where it stands |
|---|---|---|
| Camellia production | Clara-first greeting, unit-first qualifying, tours, notifications, transfers. | Untouched. Byte-identical prompt, proven daily. |
| The prototype (Western Slope 970 line, customer-approved) | Company-name greeting with the Spanish offer and "leasing, maintenance, or something else." Area then move-in qualifying, two homes with prices, no budget, no count, name at the offer, both tour formats. Message desk after hours. Its own knowledge document. | Live for the customer on its own agent. Retires at cutover. |
| Brought over into the shared staging agent | Everything in the prototype row, as rules in one slot on Camellia's agent, switched on only for company-wide lines. Tours, reschedule, cancel through Camellia's tools. | Merged, on the staging line. Greeting shape being matched to the prototype (PR #7801). First-reply order awaits Fede's A/B call. |
| Experimental — never meant for either line | Spanish auto-switch per turn, call-integrity directive, bedroom-range directive, hangup backstop, guest-card reconcile, name-agreement prospect matching, summary grounding (8 gates behind isTest). | Leaked onto both test lines because they are test properties; PR #7801 moves them to the Yale sandbox only. |
Two things were neither code nor experiment, just broken data on the test properties: zero inventory on the Willows and SMS off on the bench. Both fixed 2026-09-11 morning.
Anything odd on a staging line is one of three: the experiment bucket (going away), an order tweak Fede has not decided, or a real bug (carded).
The plan now lives as rows on Gera's Phase Dock (/a/portfolio-architecture-phases, ids v0-…v9-…); this section is the record.
The drawn version, with the deployment strategy step by step: Voice merge, drawn.
Status: Proposed — pending Fede's review. Investigation and design only; nothing here changes product behaviour. Written overnight 2026-09-08 from the week's pages (this page, the onboarding plan, Gera's HOW page, the phone-line page), the raw standup and design-session transcripts of Sep 6–8, the code on main, the live ElevenLabs workspace, and the ElevenLabs documentation. Every claim has a row in the evidence table at the end of this section.
| Finding | What it means for the merge |
|---|---|
| 1. The prototype has already merged most of itself. The one-line-per-home inventory block, the listing-to-row resolver, the public-listings rent roll, the two-homes-on-one-call guards, the multi-home receipt text, the desk-number fields and the same-visit booking logic all live in shared code and already run for every property. | What is left to merge is the prompt, the turn/voice settings, the deploy lane, and four duplicated mechanisms (below). Not the data path. Not the tools. |
| 2. "Many listings" and "many properties" are two different shapes, and Western Slope is both. The prototype line runs on a placeholder record: one property whose 16 listed homes are unit rows with street addresses for numbers (the "umbrella" shape, X16 option B). That shape is supported end to end today. But Western Slope has 14 properties in AppFolio, the plan's target is one record per property (X16 option A, recommended) with the 970 line bound to the company (X1 option A), and nothing on the call path supports a line that means several records yet: every resolver returns exactly one property. Situs is the same shape from day one. | The merge can be done in two steps or one, and that is decision D6. Step one (the shared agent on the umbrella record) needs no new storage and no change to how a call binds. Step two (the line bound to the company, building chosen in conversation) is Gera's address-to-scope program and lands on its own dates. |
| 3. Production is not one agent; it is a front door plus five specialists reached by silent hand-off, and Camellia and Yale already share every one of them. The prototype is a seventh, standalone agent that does all three branches in one prompt with no hand-offs. | "One agent" has two readings; that is decision D1 below. The recommendation keeps the shared roster and treats the prototype's single-prompt shape as a prompt block, not a new agent. |
| 4. Four mechanisms exist twice. Office hours (prompt text reasoning from the clock vs the shared after-hours desk driven by a server-computed value); message-taking (transcript only vs the note tool with a notify path); a tour-request text for a line that could not book (its own retirement condition was met on Sep 7 and the flag is still on); and a second turn/voice configuration outside the fleet's drift guard, which is why the line silently missed the Sep 4 fleet fix for talked-over callers. | Each one is deleted in favour of the shared mechanism. The constitution's one-mechanism-per-signal rule and the repo's no-parallel-implementations rule both apply. |
| 5. Client facts are compiled into the prototype: the company name, its main line, the maintenance centre number, the office hours. The module argues it sits outside the customer-identifier fence because it is a prototype. | That argument disappears on merge. Every one of those becomes a field on the property record, read at ring time, with a fail-closed default. |
| 6. The prototype line died for eight hours on Sep 6 because tools were bound while the call-start webhook was off, and ElevenLabs validates tool variables at connect time. The revert also silently dropped unrelated rules. The invariant now has five guards on the prototype lane. | On the shared fleet this class of failure is already structurally impossible (the webhook is workspace-wide and every fleet tool's variables are pinned by a drift test). The guards move with the prompt block; the lane-specific ones die with the lane. |
| 7. The shared identity path still has two open cross-company lookups on the voice surface: the post-call handler looks the caller up before it knows which number was dialled, and the leasing tool's prospect fallback picks the most recent prospect across all companies. A build-time counter pins the open sites at 20; texting on the 970 line is off until they close. | These two are the gate for the cutover, not for the build. They are where a shared line files a caller under the wrong property. |
| 8. There is no staging for voice. Every real call hits production; ElevenLabs branches cannot be reached through a hand-off or the simulator; the call-start webhook is one URL for all 26 agents; the prototype binds the exact same four tool records as production leasing. | Isolation has to come from the number and the property record, which is what the design does, and from what we choose not to share (below). |
The intake mode is computed once, server-side, when the phone rings, in the same place every other per-call fact is computed, and it is derived from data the property record already carries. It is not a new setting and there is no if/else in the tools: the tools already accept a property named by the model, and the resolver already returns the row a home belongs to.
| What the dialled number means | Intake mode | What Clara does on a leasing call | Example |
|---|---|---|---|
| One property, apartment building | single | Exactly today: bedroom bands from the pre-loaded block, "which unit are you interested in", two concrete times, book. | Camellia, Yale, the Willows |
| One property record, many homes at different addresses | portfolio | Bedrooms, area, budget, one at a time, any order, skip what was given; answer as soon as all three are known; name at most two homes with rent and beds; same-street homes are one visit; then book on the one home the caller chose. | Western Slope today (placeholder record, 16 listed homes) |
| Many property records behind one line | portfolio + building unknown | Phase two. Same qualify-first script, but the property is bound once from the caller's answer and every tool re-reads that stamp. Not built in this plan. | Western Slope on its 14 records; Situs Group |
How it reaches the prompt. Two variables, following the fence we already live under (ElevenLabs substitutes a variable's value before the model reads the prompt, so a rule can never be written against a variable's name): a labelled fact line, Leasing intake: portfolio, and a server-rendered directive block that carries the qualify-first rules verbatim from the prototype when the mode is portfolio and is empty when it is single. This is exactly the shape the closed-office directive and the hand-off policy already use. The shared leasing prompt gains one slot; the front-door prompt gains nothing, because its greeting is already a per-property variable.
SAME AGENT, TWO LINES — the only difference is the property record behind the number
Camellia caller Western Slope caller
dials +1 844 510 1007 dials +1 970 822 0641
| |
v v
ElevenLabs: number -> Clara front door ElevenLabs: number -> Clara front door (same agent id)
| |
v call-start webhook (one URL for all) v
number -> Camellia record (reach 1, building) number -> Western Slope record (reach 1, scattered homes)
intake_mode = single intake_mode = portfolio
available_units = bedroom bands available_units = one line per home (already shipped)
intake_directive = "" intake_directive = beds / area / budget rules
office hours, desk number, timezone from row office hours (with lunch gap), desk number from row
| |
v v
"which unit are you interested in?" "how many bedrooms?" -> area -> budget -> two homes
two concrete times -> schedule_tour caller picks a home -> two concrete times -> schedule_tour
| | (same-street pair = one visit, two bookings)
v v
same tool, same handler, same calendar path same tool, same handler, same calendar path
same post-call webhook, same receipt text same post-call webhook, receipt names every home booked
What moves where. Each row is one small pull request that ships dark; each names what it deletes.
| In the prototype today | Where it lives after the merge | Deleted |
|---|---|---|
| Qualify-first leasing rules (beds, area, budget; two homes per turn; quote every home; only the block is inventory; same-street = one visit; name capture; caller ID silent) | The shared leasing prompt, inside the portfolio directive block; the tests that today assert the prototype's copies against the production prompt become the tests of that one block | The ~1,000-line prototype prompt |
| Office hours written in the prompt and reasoned from the clock (Mon–Thu 9–4, Fri 9–3, Monday lunch, holidays) | The property's office hours on its knowledge record, which gains a midday gap so the Monday lunch hour can be expressed; the shared after-hours desk computes "closed today / closed for the day / out at lunch" the way it does for every property | The prompt-text hours and the model's clock arithmetic |
| After-hours message that exists only in the transcript | The existing note tool and its notify path, the same one every fleet front door uses; the missed-call style email Fede already decided to turn on carries it | Nothing to delete; a gap closes |
| Maintenance hand-off to two PropFlow-owned bridge numbers (English/Spanish) that dial the customer's maintenance centre and press a digit | Two fields on the property record (maintenance intake number per language); the front door's existing person-transfer leg reads them. The bridge trick stays as a general mechanism for any customer whose intake needs a keypress | The compiled-in numbers |
| In-hours "talk to a person" via the desk number and its availability verdict | Already shared (built Sep 8). Stays; the front door's person-transfer leg is the one that fires | The prototype's own wording of the same rule |
| Turn, voice, transcription and language settings copied by hand | The fleet's settings, guarded by the fleet drift test, which the line joins | The private copy |
| Tour-request text for a line that could not book, behind its own flag | Gone. A tour is confirmed or it is not; the confirmed-tour receipt already names every home booked | The flag, the "proposed" copy, the post-call module |
| Its own deploy lane, provisioning script, kill switch, nightly binding check, "unsynced" roster row | The one fleet writer that already runs on every merge; the phone-number sync that already owns number-to-agent | All of it: the lane, the 943-line script, the roster row |
| Company name, main line, maintenance centre as constants | The property record. Product facts stay in code; customer facts become rows | The constants and the fence-exemption argument |
The seam phase two needs, and phase one must not close. Today every resolver on the call path returns one property, and a conversation's property is its storage partition. Gera's design gives a number a reach (the properties it answers for), starts a company-line call with the building unknown, and binds it once from the caller's answer. Phase one keeps that possible by (a) deriving the intake mode from the line's reach and the property's shape, not from the property's shape alone, so a reach of two later means "portfolio" without another rule; (b) never widening the existing one-property resolver in place, since every caller of it assumes one id; a sibling resolver that returns a reach sits beside it when phase two starts; (c) leaving the mid-call binding, the unbound conversation partition and the company-scoped identity lookup to phase two, where the Yale-incident regression test and the two unfixed tenant-side defects (renewal ownership, appliance fallback) are the gate.
ADDR#…/HEAD nameplate row, one row per address, org + scope carried on it) lands, the same answer comes from the number's own row instead. Only that source swaps — the fork itself, the two leasing scripts, the shared tools, the fact sheet and the prompt slot do not change at the re-key.
Fits Gera's phases:
There is no staging environment for voice and there will not be one for this: every real call rings production. Isolation comes from four things we can point at and check.
| Line | Rings which agents | Bound to which record | Role during the merge |
|---|---|---|---|
| Camellia's toll-free line | Shared production fleet | Camellia | Never dialled by any test. Its number-to-agent row and its property record do not change in any step of this plan. Its call is proven byte-identical by a test, not by a promise. |
| Willows bench line | Willows clone fleet (same prompts, synced on every merge) | The Willows (apartment building) | Proves single mode is unchanged after each merge: the existing robot-call scenarios run here. |
| Portfolio harness line, bought Sep 7, not yet imported into ElevenLabs | Willows clone fleet (a sandbox fleet, never the production front door, as the number's own note requires) | A new bench portfolio property: scattered-home type, test flag on, in the test-property list so the bench codes and test callers work, its 16 homes refreshed from the same public listings page, a bench calendar of its own | Proves portfolio mode on the shared prompt before any customer line is touched. Real robot calls, real tools, real bookings into a bench calendar, mandatory cleanup after. |
| The 970 client line | Prototype agent, until cutover | Western Slope | Keeps taking the client's forwarded calls exactly as now while the shared path is proven elsewhere. Cutover is one row: this number's agent becomes the shared front door. Rollback is the row put back. |
The proof ladder for this change, rung by rung. Rungs stack; none substitutes for the one below it.
| Rung | What runs | What it proves | What it cannot prove |
|---|---|---|---|
| 0 · Build-time guards | A new drift test renders the call-start variables for an apartment property before and after and asserts them byte-identical except for one new empty slot. The variable-condition fence checks the new directive is branched on by value. The emit-to-consume wiring test pins the new slot in the leasing prompt. The customer-identifier fence stays green with the prototype tree gone. | Camellia's ring-time context is unchanged by construction. No rule is written against a variable's name. | Anything about what the model says. |
| 1 · Simulated conversations | The existing 164 leasing scenarios run unchanged in single mode (regression floor). The prototype's booking and quoting scenarios, plus the same-visit eval pack, are re-pointed at the shared leasing agent with portfolio variables and mock tools. Every new case fails on the old prompt. | Tool choice, wording, routing in both modes, with zero caller impact. | Latency, real telephony, the hand-off from the front door (the simulator runs one agent). |
| 2 · Real-history replay | The 50 prototype calls of the last week and Camellia's last 30 days of leasing calls replayed through the simulate driver on the old and new prompt; the diff is the proof in the pull request. | No regression on real callers' shapes; the portfolio script does on the shared prompt what it did on the prototype. | Real audio timing. |
| 3 · Robot calls, after merge | Voice-eval robots dial the Willows bench line (single) and the portfolio harness line (portfolio): the leasing-tour scenario, the two-homes-one-street scenario, the after-hours message scenario, the in-hours person scenario. Sentry swept for the call window. Unknown-caller cleanup after every run. | End to end over real telephony: front door to leasing hand-off, live tools, real bookings into the bench calendar, dead-air windows. | Behaviour on the customer's actual forwarded traffic. |
| 4 · Off-switch drill, then cutover | Before cutover: repoint the harness line away and back once, to prove the number-to-agent row is a working rollback. Then, with Fede's go: the 970 line's row moves to the shared front door. The prototype agent stays parked, tools unbound, for seven days. | Rollback works before it is needed. The customer's line runs the shared path. | — |
| 5 · Soak | Seven days of the client's real calls; every one graded per turn as today; disagreements become corpus cases; the missed-call email is the only thing that reaches the client. | The lane's definition of done: scorecard clean, then delete the prototype agent and its number note. | — |
Inputs from the Western Slope kickoff call, 2026-09-09 (raw transcript), for the portfolio block and the bench scenarios: no tour with less than one hour's notice (Sean); a booked tour meets Kat, their leasing agent, at the home, and the confirm line says so (Jay). Both become bench scenarios and directive rules in step 6; a tour booked inside the hour is offered the next slot instead. Also from the kickoff (Fede, via the prototype-line session): the qualifying order was ruled timing first ("when are you looking to move in, and are you flexible?"), then budget, then open-ended questions, then homes, with the ban on move-in questions before homes removed; then put on hold the same night pending competitor and best-practice research, with the order to become a company-level "qualifying order" setting rather than a prompt fork. Update, 2026-09-10 early: research in, Fede said "ship dark": the order becomes a company-level setting whose documented default is the ruled order (acknowledge a named home or area, or ask "anywhere in particular?"; move-in date and flexibility; one open-ended "tell me what you're looking for"; homes with prices by turn three or four and a tour offer; budget never a gate; pets from the open question or once when naming homes; never credit, income, occupants or reason). The block renders only when the company setting is present; Western Slope stays unset until Fede's turn-on. Constraint recorded for the prototype: its prompt is static text, so it must not import the new rules at compile time (that would change the 970 line on merge); it gets the same per-call server-rendered slot as the shared fleet, empty until the setting is set, and its inline copy is deleted only in the turn-on change. Then Fede, later the same night: "no, don't do settings yet": no lane adds a setting, registry key or per-property field until the settings cleanup is ruled on. So the order is the module's constant default and the dark gate derives from what exists: the block renders for a scattered-homes property only while that property is in the code-level test-property list (the bench is, the Western Slope placeholder is not); the turn-on is a one-line code change on Fede's word that renders it for every scattered-homes property. Design note for that block (Fede, 2026-09-10): no checklist questions for pets, yard and the like; after timing and budget, one open-ended question ("tell me a bit about what you're looking for"), the caller volunteers dog, yard, fence, commute, everything lands on the guest card, and direct follow-ups are asked only for what was not said. Jay on the kickoff: "I encourage her to ask as many open-ended questions as possible." Separately, a dark bug fix is in flight: on a property with no office phone and no emergency phone the transfer tool fired with an empty destination and the failure notice reached no one; the server now tells the agent to take a message instead and the notice falls back to the property's notification address, with a regression test and a byte-identical Camellia control. One source for the rules: a shared, client-agnostic module (src/lib/domain/leasing/portfolio-intake-rules.ts) that the prototype prompt imports today and the server-rendered directive imports in step 6, so the prototype and the shared block can never be two versions. Jay's ask for a more urgent alert than an Outlook notification when a tour books is a product ask on the onboarding lane, not this one.
Fits the three-week cut Fede and Gera set on Sep 8: week one on today's rails (steps 1–2 alongside the client launch on the prototype line), Sep 14 to Oct 2 dark (steps 3–6), cutover when the ladder is green, before the November flip that Situs needs.
Fede, 2026-09-09: "what I'm mostly worried about is tools not working, because they are attached to a property, like tours; we need to untangle that. It needs to be part of the architecture proposal." This is the untangling, written as a proposed addition to the solution. Gera owns that page; this section is the hand-off. One row per tool on the front door and the leasing agent, read from the code on 2026-09-09.
Where the property comes from today. Every tool gets its property one of two ways: from the conversation row stamped at ring time with the dialled number's property (hard-wired), or from a property field on the tool that ElevenLabs fills automatically from the same dialled-number variable before Clara ever sees it (a slot that exists but that the model cannot fill). The server already prefers a model-supplied property over the line's when one arrives; nothing produces one.
| Tool | How it knows the property today | Portfolio problem | Smallest honest change |
|---|---|---|---|
| Front door: seven tools, none takes a property | |||
| Recognise the caller | Searches one building's resident list, from the stamped property | Does not recognise a resident of a sister home | Search the line's reach (company-scoped), never the platform; return identity without binding a home |
| Hand off to a human | Stamped property → the PM's email | With no home chosen, nobody to email | Company-level fallback owner from the org hook when the home is unknown |
| Take a message from an unknown caller | Same | Same; and today it captures the message and tells nobody | Same fallback; the missed-call email is the delivery until then |
| Reschedule a vendor's visit | Via the work order; vendor recognised inside the dialled building | Vendors serve many homes | Recognise the vendor inside the company's reach |
| Opt out of messages | Person or phone keyed | Probably fine | None |
| Bench readout, bench clear | Test only | None | None |
| Transfer to a person, end call, skip turn (ElevenLabs' own) | Dial destination = the building's office phone variable | A company line needs a company destination | Destination resolved by the walk: home → group → company, nearest hook wins |
| Leasing: fourteen tools, seven carry a property field, seven do not | |||
| Available units | Property field auto-filled from the line; then fetches one building's units | Needs the whole reach's homes | Fetch the reach's units; group by area in code; the model may pass the home it means |
| Property details, amenities, neighbourhood, term pricing | Same shape | Per-home facts on a scattered portfolio | Same: home argument the model fills after the caller picks; facts read off that home's record |
| Book a tour | Same field; fetches one building's units to match the named home; books on that building's calendar | Two problems: one building's home list, and the building's calendar | Widen the fetch to the reach; file the tour under the home's record; calendar from the leasing person's hook (see below) |
| Link on behalf | Same field | Follows whichever home is chosen | None beyond the shared change |
| Reschedule tour, cancel tour | No property field; keyed on the tour, falls back to the stamped property, with a code comment warning the fallback "silently widens to ALL" | That widening becomes dangerous once calls start company-scoped | Fall back to the company's reach, never wider; keyed on the tour first |
| Look up an expected visitor | No field; reads one building's prospect list | Visitor could be for any home | Read the reach's prospects |
The five changes, in the order the code wants them.
Already right. The two-homes-on-one-call duplicate guard was fixed for portfolios on Sep 8. The conversation row already has a legal "no home yet" state and a live guard that fills the home only when empty and refuses to move it later, which is the shape a mid-call binding needs. In Gera's terms: the nameplate gives the reach; home_state starts unknown on a company line; the model names the home; bindHome writes it once; every tool above reads the reach until then and the stamp after.
Confirmed in code on 2026-09-10 before any call: the counts above are exact (seven front-door tools with no property argument; fourteen leasing tools, seven with a property slot), and reschedule-tour and cancel-tour declare a property id in their inputs but do not expose it on the voice schema, so on a call the property is never passed at all. That is a blocker row for portfolio mode in the tool table.
Phase one needs none of this (one record, home known at pickup; ElevenLabs keeps auto-filling the property from the line). It is what the portfolio block in step 6 may rely on. Phase two is the five changes.
| Decision | Options | Recommendation |
|---|---|---|
| D1 · What "one agent" means DECIDED A — Fede, 2026-09-09 "It's the same triage pattern. We extend the leasing script to account for centralized: if the triage agent gets 'I'm calling about leasing', we transfer to the leasing subagent. That subagent now handles direct property (Camellia) and centralized, to get more questions to triage." Camellia's path does not change. | (A) One shared roster for every customer, the prototype's single-prompt intake becomes a block on the shared leasing prompt, front door and specialists stay. (B) Collapse the roster into one coupled front-door agent for everyone, retiring the hand-offs. (C) Keep the prototype as its own agent family. | A. It is the Sep 5 decision as reconfirmed Sep 6. B is a separate lane with Camellia in its blast radius (maintenance, renewals and resident-services tools all hang off the specialists) and should be argued on Camellia's corpus, not smuggled in here. C is the drift this page's own removal trigger forbids. |
| D2 · The bench for portfolio mode DECIDED A — Fede, 2026-09-09 A bench property behind the harness number (+1 720-807-1724) with its own calendar; Western Slope's record and the 970 line untouched. Calendar: the Microsoft 365 sandbox tenant created 2026-09-09 (the leasing shared mailbox's calendar, or Clara's, whichever the calendar code supports today), shared with the onboarding run, so test events are coordinated between sessions. | (A) New bench portfolio property behind the harness line, Willows clone fleet, own calendar. (B) Point the harness line at the Western Slope record itself. (C) Test on the 970 line directly. | A. B books test tours into the calendar the client's line uses. C is the customer's line, which the constitution says is not a bench. |
| D3 · Cutover and rollback DEFERRED DEFERRED — Fede, 2026-09-09: "we will worry about it later when we are ready. we got lots of testing to do." The 970 line stays on the prototype agent, untouched, until bench testing is done. | (A) Repoint the 970 number's row on merge; park the prototype agent seven days; drill the rollback on the harness line first. (B) Delete the prototype the same day. (C) Run both agents on two numbers and ask the client to change forwarding. | A. B removes the rollback target. C moves the risk to the client's phone tree. |
| D4 · How the mode is chosen DECIDED A — Fede, 2026-09-09 Derived, no new field. The real signal is the number's nameplate scope in Gera's design (the phone-address head row: company + scope; scope = company → portfolio, scope = building → single) plus the reach, with "the row that answered" stamped on the call. Phase one's use of the existing "scattered homes" type field on the placeholder record is a temporary stopgap Gera's model does not have; phase two migrates the derivation to the nameplate row the moment his phone-address rows exist. No new per-property field is added at any point. | (A) Derived at ring time from the line's reach and the property's shape; no new field. (B) A new explicit per-property voice-intake setting on the admin page. | A. The shape field already exists and is the one switch. B is a second switch behind the first. |
| D6 · Sequence DEFERRED with D3 (same cutover), 2026-09-09. DEFERRED with D3 (same cutover), 2026-09-09. | (A) Two steps: the shared agent takes the 970 line on the placeholder record now (portfolio mode, one record), and the line moves to the company binding with 14 records when Gera's program lands. (B) One step: keep the prototype agent until the company binding exists, then merge once. (C) Skip the company binding for Western Slope and keep the umbrella record permanently. | A. It retires the second agent family in September rather than November, which is the part Fede said he is most nervous about, and it proves the shared prompt, the tests and the bench on real Western Slope traffic before the harder step. The qualify-first script does not change between the steps. Cost: the umbrella record's home rows must be reconciled into the 14 property records at step two, a data debt the plan already records. B leaves two agent families running for two more months. C is the drift X16 recommends against and blocks maintenance and residents on that line forever. |
| D5 · Gate for cutover DEFERRED with D3 (same cutover), 2026-09-09. The two open voice lookups are already closed and live; the probe is done when the cutover is scheduled. DEFERRED with D3 (same cutover), 2026-09-09. The two open voice lookups are already closed and live; the probe is done when the cutover is scheduled. | (A) The two open voice lookups closed and a clean probe from a number that is a resident elsewhere, before the 970 line moves. (B) Move first, close in parallel. | A. It is the same gate already recorded as X3 on the onboarding plan and the standing rule in the counter test's header. The tenant-side defects (renewal ownership, appliance fallback) gate Situs, not Western Slope, which is leasing-only with no residents on the line. |
| Hazard | Guard in this plan |
|---|---|
| Widening the one-property resolver in place; a caller that takes the first of a list recreates the Jun 13 wrong-property incident | Never widened. Phase two adds a sibling resolver; phase one derives the mode and leaves the return type alone. |
| A change to the ordering or grouping of the pre-loaded units block for apartment properties | The byte-identical render test on an apartment record (rung 0); the portfolio formatter is only ever reached through the shape field, exactly as today. |
| A new variable emitted without its slot, or a slot without its variable: ElevenLabs drops it silently and the block is invisible | The emit-to-consume wiring test, extended to the new pair. |
| A rule written against a variable's name, dead on every call | The variable-condition fence; the fact-line-plus-verdict shape. |
| Loosening the tenant-drop and property gate in identity resolution (inside one company only the building filter separates Camellia from Yale) | Not touched in phase one. Phase two's gate is the Yale-incident regression test. |
| Any hand-run write to an agent to "just try it" | Structurally refused since Sep 7; every write in this plan is a merge. |
| Deleting the prototype's tools by unbinding them; the four tool records are the same ones production leasing binds | The prototype is retired by moving its number, never by editing the shared tool records. Its parked state leaves the records alone. |
| Test callers reaching a real property | The bench property is in the test-property list; test senders are refused on every other record as today; robot scenarios hard-abort on any property but a bench. |
Read against the solution: the nameplate (one row per number), the hook (one attachment on one node), the walk, the resolver that mints TenantContext with a reach, and the change catalog. Five findings, none of which changes the recommendation; two change wording in the steps.
| Gera's design says | Effect on this plan |
|---|---|
A reach of one binds the property at pickup; more than one starts the call with the home unknown and bindHome writes it once. | same That is phase two here, unchanged. Phase one is a reach of one. |
| A scattered home is its own building row with an asset type; "one portfolio's scattered homes sit under a single placeholder building" is listed as a today-defect, and every Western Slope lead born under that placeholder key must be re-keyed later (his K16 one-way door). | conflict, already priced in D6 Choosing D6-A keeps the shared fleet answering on the placeholder key until his step 3b/4, so leads keep needing re-keying. D6-B has the same debt, because the prototype line files there today. Neither option creates the door; the flip closes it. Also: the "scattered homes" type field this plan derives the mode from in phase one retires with the placeholder; in phase two the mode comes from the reach, which is the alignment rule already stated. |
| Step 3b: the number-to-agent binding is written on the nameplate head and the config files become generated dumps. | sequence note Stage B cutover repoints the 970 number through the config file only if 3b has not landed yet. If 3b lands first, the cutover is a claim-row change and the file must not be hand-edited. Step 10 reworded to say so. |
Step 4 lands home_state, the two-step area-then-home ask, bindHome, and the shared agent's tools; owners are Fede for the prompt and fixtures, Gera for the webhook. | one mechanism, not two The qualify-first block in step 6 here IS the prompt half of his step 4. It ships once, in phase one, driven by the intake directive; step 4 adds home_state and the area menu as more variables into the same block. Nobody writes a second "which home" script. |
| The voice fail-open (an unknown number adopts the caller's building) is ripped out in his step 4 and replaced by a refusal on a nameplate miss; the text-message posture keeps its refusal on his new resolver. | complementary The two lookups closed in step 4 here go through the existing resolver function, so when his resolver replaces it they follow without a second edit. The fail-open itself is not touched in phase one. |
Section 9 describes the prototype as having no tools and a hand-typed list of homes. Since Sep 7 it binds the four production leasing tools, books real tours into a connected calendar, and reads its 16 homes from the customer's public listings page every fifteen minutes; it took 50 calls in the last seven days, all from our own test numbers. The runbook's claim that the fleet drift check blocks a merge is also false; the audit of Sep 3 proves it is advisory. Neither changes the recommendation; both are recorded so the next reader is not misled.
| Claim | Where to check |
|---|---|
| Western Slope has 14 properties in AppFolio; the prototype line runs on one placeholder record; one record per home is the recommended target (X16), the company binding the recommended line scope (X1) | Onboarding plan, "Western Slope portfolio go-live: engineering areas" and decisions X1, X12, X16 |
| Fede's ruling: one shared agent set; company line asks which building (Sep 5, reconfirmed Sep 6) | #transcripts, Sep 6 thread, "Precise version, from Fede"; this page §6; onboarding plan X1 |
| Per-property difference comes from dynamic variables, not withheld deploys | .github/workflows/sync-specialists.yml:56-62 |
| Camellia and Yale share the same front-door agent; three numbers tracked, all match live | config/elevenlabs-phone-numbers.json; live workspace read 2026-09-08 (16 numbers, 26 agents) |
| One resolver returns one property for voice, text and the Lambda | src/lib/domain/properties/phone-lookup.ts:337; the map at :145-153 |
| Scattered-home shape and one-line-per-home block already shipped and shared | src/lib/domain/leasing/listing-resolution.ts:50-56; src/lib/integrations/voice/leasing-context-injection.ts:1236-1241 |
| Listing-to-row resolver is the named fan-out seam with two callers | listing-resolution.ts:170-196 and its header; callers in agents/clara/lib/agent/tools-leasing.ts and src/lib/integrations/voice/postcall-tour-request.ts:213 |
| Tools already accept a property named by the model, resolved as a matched pair | src/lib/integrations/voice/tool-helpers.ts:86-131 |
| Fact-line plus verdict variable shape (render trap) | src/app/api/voice/personalization/route.ts:2135-2142, 2265-2278; ADR-0116; voice-prompt-var-condition.drift.test.ts |
| Emitted variables without a slot are dropped silently; the Aug 22 six-day blind spot | src/__tests__/voice-agents-context-wiring.drift.test.ts; commit c56160bd2 |
| The prototype: one 1,575-line module, four production tools bound by name, own lane, own kill switch, own turn config outside the fleet guard | agents/clara/lib/voice-agents/prototypes/western-slope-portfolio-triage.ts (header L1–90; tools L1508; DISARMED L1527); .github/workflows/provision-western-slope-proto.yml; scripts/provision-western-slope-proto.ts; #7333's body on the turn guard |
| Prototype live prompt equals main byte for byte; it binds the same four tool records as production leasing | Live workspace diff 2026-09-08; tool ids tool_1701m0rx18k…, tool_3401m0rx18k…, tool_9201m0rx18k…, tool_9501m0rx18k… on both agents |
| Office hours duplicated in prompt text; no after-hours variable declared by the prototype | Prototype L1560–1574 (placeholders); agents/clara/lib/voice-agents/after-hours-message-desk.ts |
| Tour-request flag's retirement condition met Sep 7 | #7135 body ("folds into the tour-booking work the moment this line can book"); #7297 bound the booking tools |
| The Sep 6 outage: tools bound with the call-start webhook off; connect-time validation; revert dropped unrelated rules | #7142, #7157, #7188; the five guards in #7297 |
| Two open cross-company lookups on the voice surface; counter pinned at 20; texting on the 970 line off until closed | src/__tests__/inbound-org-scope.drift.test.ts (rows for call-ended/route.ts and tools-leasing.ts) |
| Branches unreachable through hand-off or the simulator; webhook branch id a no-op | ADR-0048; docs/runbooks/voice-test-ladder.md:37-59, 161-168; the Jun 4–6 false starts |
| Duplicate-tool incident from an inline-tools copy | Jul 19 incident record; reference_el_ab_copies_tool_ids |
| Per-call overrides are off by default per field; the call-start webhook is one workspace URL | ElevenLabs overrides documentation (read 2026-09-08); live GET /v1/convai/settings |
| No preview lane for voice; robot calls always hit production; never dial Camellia or Yale from a test | docs/runbooks/real-path-e2e.md:85-89; voice-test-ladder.md:75-76, 152-157 |
| Portfolio harness number bought Sep 7, not imported, must bind a sandbox agent | config/phone-registry.json (+1 720 807 1724); config/elevenlabs-phone-numbers.json index note |
| Bench property list is a hardcoded id list, separate from the test flag | src/lib/platform/security/test-data-guard.ts:24, 281 |
| Deploys on merge only, no hand-run path, no bypass; number bindings included | Constitution IX-A; scripts/lib/elevenlabs-deploy-lane.ts; .github/workflows/sync-phone-numbers.yml |
| Comprehensive harness is 267 scenarios, 164 of them leasing; simulate needs the person-transfer number variable or returns an opaque 500 | scripts/voice-harness/scenarios/; scripts/voice-harness/docs/bug-catalog.md:132 |
| Three-week cut: week one on today's rails; Sep 14–Oct 2 dark; November flip | Gera's HOW page D10; #agent-smith Sep 8 thread |
| Gera's objection to a hard-coded single-vs-portfolio branch | #transcripts, Sep 6 thread, Gera on "if else like statements" |
Detail: night/findings.md (F1–F50), night/overnight-report-LONG.md, night/sentry-sweep.md. Adversarially claim-checked before delivery (night/report-verification.md); its corrections are applied. Where a number could not be traced to an artifact, it says so. ~1,120 words against a 700 target, deliberately. The overage is the corrections the claim-check produced plus the closing rule; cutting to length means dropping the parts that were hardest to establish. The five-line verdict at the top is the 700-word version.
transfer_to_agent fired before cancelling) — each read back via Graph, including the 404 showing the slot was released. 6 live calls. Deviation Fede should know: the read-back used the property's own stored delegated token, NOT the propflow/m365-sandbox app credential he specified — that credential cannot reach this line's consumer-Outlook calendar.attendees: []), the bench calendar leg, and the leasing desk in replay.--apply is refused by the prod-property guard, which requires the release phrase from Fede's own environment — no session may set it for itself, and no worker reported doing so. Once he runs it, the binding branch still has to be pushed, merged on green and read back from ElevenLabs (9-step checklist) before the first call — call it tens of minutes, not minutes.At least nine times tonight an instrument reported something other than what it claimed — ten counting the adversarial checker of this report, which flagged a correct scenario count as invented by conflating two harness tiers. Four read exactly like a product finding: a driver that failed every tour and accused Clara of confirming a booking she had actually made (false #3207 — that file sat on a typecheck quarantine, so a missing required argument never compiled); a green test grading a founder's personal cell under a comment claiming it graded the portfolio line; a grader marking Clara's verbatim standing instruction as a forbidden promise; a ban firing on street addresses, 3 of 4 firings false. Also: a data-store read returning 0 conversations while reporting the dynamodb backend (2473 via db); getConversationMeta returning messages: [] for a call with 18; a cleanup script printing tour-scan properties: appfolio-45 then deleting outside it. Separately a ban fix auto-merged at its first commit, holes live 22m47s — sequencing, not an instrument. Had the bench opened at midnight, 27 portfolio calls would have run through that apparatus and produced a confident, wrong readiness picture.
The rule underneath all of it, worth keeping past tonight: an ambiguous symptom is not evidence until something distinguishes its two causes. A consent screen looks identical whether the grant is absent or our connect path is broken; a tour check returned "missing" for a tour that existed; get-item printed nothing for an absent row; a Sentry endpoint ignored its window; a toggle printed RESTORED before checking; a monitor rendered "could not check" as "changed"; and a deviation was inferred from an order not visible in one agent's view. Each was an ambiguous symptom read as whichever cause was easier to reach for, and the easy reading was usually wrong. It is far easier to see in an instrument than in a person — the conductor made this error three times, once about a colleague — which is the argument for writing it down. It is also why every instrument here was proved in BOTH directions before its output was believed: a reader that has only ever printed "none" cannot tell you which "none" it means.
reschedule_tour/cancel_tour declare a property but never expose one on the voice schema.{{pm_phone_number}}; the bench has no desk phone. #7498 held for Fede.resolveVoiceConversationId's phone fallback has no property scope — watched bind a Willows call to a western-slope-proto conversation at 02:47Z; the 03:02Z control returned its own. Sibling fail-open at personalization/route.ts:580 adopts the caller's property — one config line from writing our bench run into another lane. Most serious product finding of the night; not fixed (hot path, daylight work).night/replay/PROGRESS.md)Simulate replays, not live calls. camellia run 1 (corpus pulled 02:47:07Z): 87 replayed / 56 passed, baseline. western-slope-proto (pulled 02:47:34Z, run 02:57:44Z): 51 / 47, baseline. camellia run 2, same corpus: 87 / 57, 10 tool-sequence changes, 1 flip, 0 broken. 225 replays. No 429 appeared in any log — but the mandated 429-log.txt was never created, so that is an absence of observation, not an instrumented zero. Bar not met: 10 of 87 — but those 10 occurred with the change set held empty: same code, agent, corpus, all transfers. It would have false-alarmed on the first fix landed tonight; Fede rules on any replacement (noise floor persisted, #7520). 64.4% is not a health number: a Triage-only replay cannot reproduce post-transfer tools, and Camellia's transfer destinations differ from the Willows'. Triage-only — it does not cover the leasing desk.
Merged: #7505, #7506, #7513, #7520. Held for daylight, labelled, auto-merge off: #7502 (standing CHANGES_REQUESTED 03:33:54Z, never lifted) and #7509 (CHANGES_REQUESTED 03:33:08Z → APPROVED 03:43:52Z` — so its hold is now a choice, not a blocked review; it is mergeable in daylight). We dismissed no review: that reviewer was right repeatedly tonight and we were wrong.
14 issues / 75 events: NEW-BUG 2, KNOWN 7, NOISE 0, LOCAL-DEV 5 — read proven by controls first (the project endpoint silently ignores the window; the org endpoint was used). Touching us: 2 issues / 5 events, both known since 2026-08-17, both a mail gate firing after a successful tour. No tour-handler errors, Graph failures or throttling. The 03:44:41Z AADSTS65004: User declined to consent is reported closed by the Onboarding lane — their own deliberate cancel against a throwaway property id, not the bench connect (never attempted). We did not verify that independently; our own sweep artifact still reads "unresolved".
Sandbox tenant grant timeline (Onboarding lane): granted 03:49Z → scope-stripped ~04:31Z → deleted ~05:40Z → restored 06:00Z; current state read back good. Exposure for this lane: none, and stated rather than left to be worked out. The single-mode calendar proof ran 03:02–03:08Z on the Willows, against a consumer Outlook calendar via that property's own stored delegated token — not the sandbox tenant — and the bench connect was never attempted. No result in this report ran under the stripped or deleted grant. Reading rule for whoever connects next: a connect that prompts for approval means the grant is missing, not that our side is broken.
2026-09-10. Reference: Camellia today. Bench line +17208071724 → portfolio-bench, sandbox Triage (binding read back from ElevenLabs 15:59:20Z).
Willows control, conv_1901m26268gffdva945ck0b1dg7p, 8 turns:
Clara: "Hi, it's Clara at The Willows — para español, diga 'español' — what can I help you with?" Caller: "Hi, I'm interested in a two-bedroom." Clara: "We don't have any two-bedrooms available right now, but I'd love to keep you in mind when one opens up. What's your name…?"
Single-property ordering — straight to the unit question, no town or area qualifying — is proven. The which-unit half was not exercisable: the scenario injects two-bedrooms at $1,800–$1,950; the live Willows has none, so Clara had no unit to offer and moved to a waitlist capture. Not counted as a pass on something that never came up.
Byte-identical evidence: PR #7494 (merged, green) — a route-level golden test of the 66-key personalization variable map for an apartment property, against fixtures from origin/main's own route.
Qualify to one home, cold caller — call one, conv_6801m260rrgxfkt8fqt4ce4w19mm:
"…several three-bedroom homes available right now, from about nineteen-fifty to twenty-seven seventy-five a month, spread across Grand Junction and Ridgway." …"There are two on Ember Lane in Grand Junction — 367 and 372."
Towns named unprompted; two homes on one street distinguished by number. Every address matched a listings snapshot taken minutes earlier; none invented.
Narrowing to one town — conv_5101m261z01bfznsvram7p8sdyn6. Stays inside Grand Junction throughout; names no home from outside it:
Caller: "Is there anything else available in that same area?" Clara: "Yes! Also in Grand Junction, there's a four-bedroom on Emerald Circle at two thousand three hundred fifty a month… available now and pets are welcome."
Caveat: a returning caller — same number, same person by design — so she had prior context and short-circuited the qualifying. The cold qualify is call one, and the both-towns line above is call one's, not this call's.
Book / reschedule / cancel: tour 825c5084 booked 16:01Z on 367 Ember Lane — the home the caller chose — moved 2026-09-10T17:00Z → 2026-09-11T17:00Z on the same event id, cancelled 16:17:46Z. Calendar event confirmed gone by fetching its id through Graph (ErrorItemNotFound), not inferred from an empty list. Tour d514b75f (2841 Morningside Court) confirmed and untouched throughout.
Leasing notifications: four emails to clara@ — 16:04:08Z confirmed, 16:10:09Z rescheduled, 16:14:18Z confirmed, 16:17:58Z cancelled. Matches Camellia's leasing side.
Shadow mode on, no message row, log line not obtained.
The conversation cross-bind (a tool call binding to another property's conversation) stays open; carded.
How it came to be. On Aug 23 a severe tour-reschedule bug had to be tested on a phone line without reaching Camellia. ElevenLabs has no staging: every call rings production and a merge is the deploy. So an eight-agent copy of the shared fleet was created for the Willows test property, with its own agent ids and its own tool records, and the Willows number was pointed at it (#6184). Fede's ruling at the time: "Camellia frozen until he tests." For two weeks only the copy received merges.
What that cost. The freeze opened a gap between the repo and the live line: prompt fixes merged, reached the Willows, and did not reach Camellia, including a dead-air fix stranded three days (#7212 records it). Drift checks were pointed at hand-kept baselines so the gap read green, and one Willows sub-agent silently missed every prompt change since it was built (#7195). On Sep 7 the owner ruling reversed it: every fleet deploys on every merge; per-property difference comes from data, not from withholding deploys. A build test now fails if any writer skips a fleet.
So today: production and the test copy are the same instructions by construction. What differs is agent ids, tool ids, and which number rings which. Camellia is protected by the new behaviour being off for her record and by a test that fails on any byte moved in her call setup.
Alternatives considered (§11.3): ElevenLabs agent branches cannot be reached through the front-door hand-off or the simulator, so they cannot test the path a real caller takes; a separately edited duplicate is how 15 duplicate tools were minted on Jul 19 and live agents rebound onto them; per-call whole-prompt overrides share one webhook URL for all agents, so a mistake reaches both lines at once.
Options. (A) Keep the mirror, recommended for this go-live: what is tested is what production runs; no swap step to get wrong; Camellia's safety is a test, not a promise. (B) Isolated clone from a branch: matches the intuitive "clone, edit, test, promote" model, but it is the Aug 24 – Sep 7 setup that produced the stranded fixes and faked-green drift checks; promotion becomes a big-bang instruction swap; about a day to build a branch deploy path. (C) Do nothing now, revisit after Western Slope is live.
Clean-up worth doing regardless (small, session work): (1) the renewal-voice prompt sync is still shared-fleet-only, so the Willows renewal copy drifts; give it the same per-fleet second run. (2) After the 970 cutover, delete the prototype agent after its seven-day park. (3) Name the fleets by role in the config and ElevenLabs ("Production" / "Sandbox (Willows)") so nobody has to know an agent id to read the setup.
Merged: step 6 (#7608): the shared leasing prompt's intake directive now renders the qualify-first rules for portfolio-mode calls and stays empty for single mode; Camellia's variable map byte-identical (golden test green). Also merged earlier: bench binding #7595, harness verifier fix #7502, after-hours person-ask test #7509.
Closed without merging, on Fede's direction, branches kept: #7504 Research Caller agent (vendor-research tangent); #7613 renewal prompt sync per fleet (clean-up, not go-live); #7498 never-transfer-to-a-blank-number (a bench-only case: the test company has no desk phone, every real line has a destination).
Step 6 verification, 19:00–19:40Z: two robot calls on the bench line FAILED the qualify-first order (Clara named a home before asking area). Call one: the per-property injection cache (up to 26 hours old) predated the merge, so the rules never reached her; cache refreshed. Call two: the rules reached her verbatim inside the home-list data block and she did not follow them. The prototype obeyed the same rules because they sat in its own instructions via a declared slot; the shared prompt never declared that slot, and ElevenLabs drops undeclared variables silently. Finishing step 6 the right way now: the shared leasing instructions declare the slot (empty for single-building calls, so Camellia's rendered instructions are unchanged), rules live in exactly one place, one more call to verify. Follow-up finding: a deploy should invalidate that injection cache.
Open PRs in this lane: none. Fleet setup question (§11.11): default is option A, keep the mirror, unless Fede picks otherwise.
Next, in order: Fede calls the two test numbers (+1 844-285-3526 single building, +1 720-807-1724 many homes) and tries to break Clara; fixes and re-tests as needed; Western Slope's real facts on its record; then, on Fede's go only, the 970 line moves to the shared Clara.
Once the ladder is done, a call rings in and the resolver reads the number's address head — which org, which scope: company-wide or one building; an unknown number is refused, not adopted by a default. Everything downstream reads inside that org's wall; attachments (phone line, leasing mailbox, a calendar, a PMS login, office hours) resolve by nearest hook, the org's own where the building has none; the fact sheet is built for that scope; one shared agent and tool set handles every customer, forking only on which leasing script to speak from; our server books the tour on the resolved calendar (the building's own, else the company's) and notifications follow the inbound channel; conversation and prospect rows are keyed under the org. Western Slope's 970 line ends up exactly where Camellia, Yale, Willows and the bench already are — a number on the shared agent, differing only in scope. Full diagram and the retirement list by phase: §0.2 of the picture page.
Fede's words: "the architecture is decided... build it in a way that doesn't affect the Camellia production agent yet... a full copy that's safe that we can test thoroughly for both... one production agent and one staging."
The five-PR chain:
resolveLineScope: which org, company-wide or one building — today from the placeholder/registry, later from Gera's address head.Promotion = production flips to the front door (a no-op for single-building customers) and the 970 line moves to the production agent — both Fede-only.
Note: the READ-FIRST carve-out PR #7641 is superseded and will be closed. The Willows stays the single-building test number on staging.
The five-PR chain (§11.13) is on main: #7678, #7679, #7680, #7691 all merged; #7691's merge commit is 351ceb02ed. The specialist sync ran for it (GitHub Actions run 34546671325, push to main, success) and a direct read-back from ElevenLabs confirms the split: the STAGING Leasing agent (agent_5901m0r9azzdetn8b1w2zyjy3v44, "Clara — Leasing [WILLOWS]") carries the literal {{many_homes_front_door_directive}} token in its prompt; the PRODUCTION Leasing agent (agent_8601krxqwzd2fhyrpqcfm71ndzh3) does not. Nothing here touched Camellia's or Yale's text, and neither was dialled.
Willows (+18442853526), single-building: a real robot call at 2026-09-10 22:03 MDT (after Willows' office hours) asked to tour a one-bedroom. Property and intake-mode resolution both verified correctly (stamped appfolio-45 / single), and the call correctly transferred Triage → the Willows' own STAGING Leasing agent — proof the staging front door is live on this line. Because the call landed after hours, Clara's after-hours message-desk logic took over (took a callback message instead of booking a tour) — expected system behavior colliding with test timing, not a defect in the front door. No tour was booked on this call; a clean single-building pass needs a re-run during Denver daytime business hours.
Bench (+17208071724), many-homes: a real robot call at 2026-09-10 22:06 MDT asked for a three-bedroom home. Clara asked area first (caller was flexible), then move-in timing, then the caller volunteered a budget UNPROMPTED — Clara never asked for one. She named two specific homes with prices by the fifth turn (Krista Street $1,950 and Ember Lane $2,750 in Grand Junction), described them only as "a couple of options" (no home count stated), and booked an in-person tour on Krista Street for Fri 9/11 9:30am. DynamoDB shows TOUR#3d2d3fc7-f644-4b7f-958e-f542c7fec6d2 under portfolio-bench, confirmed, with a populated Outlook/Graph event id — a real write to the clara@ sandbox calendar, not just a spoken confirmation. PASS against every bar in §11.13's live-proof step.
Also found, not fixed here (proof-only scope): (1) the harness's l4-e2e-postcall.ts driver has a stale module-load-ordering bug — src/lib/data/dynamo-client.ts's module-level TABLE_NAME constant gets frozen to .env.local's propflow-dev before the driver's own table override runs, so a bench provisioned only in propflow-prod (like portfolio-bench) falsely reads as "not found" unless the shell exports DYNAMODB_TABLE_NAME=propflow-prod itself; worked around for this run, worth a small harness fix. (2) the same driver's hand-off assertion hardcodes the shared/production Leasing agent id, so it now reports a false FAILED on the Willows call even though the call correctly reached the Willows' own dedicated staging Leasing agent — the assertion needs to accept a property's dedicated-fleet agent id, not only the shared registry id. (3) the "Clara behavior evals (promptfoo domain sweep, ci mode)" check that showed red on #7691 was a pre-existing, time-clustered flake — the same check failed at the same ~20-minute window (2026-09-11 00:08–00:28 UTC) on two unrelated branches, including one touching only unit-vacancy-sync.ts, then went green again for 15 consecutive runs afterward; not caused by #7691's changes.
Final results. The overnight test round is complete. This replaces the interim publish that went out mid-round with PARTIAL/PENDING rows — every row below is a settled result from real phone calls and real database/calendar/mailbox checks, not a prediction.
| Question | Answer |
|---|---|
| Did the single-building line (Camellia's kind of property) stay exactly as it was, unbroken? | YES — proven three ways: it receives both of the many-homes scripts completely empty; every one of its three real calls was internally tagged as the single-building type, with no exceptions; and on those real calls Clara never once asked a caller what town or area they wanted. |
| Does the many-homes version have its basics working? | PARTIAL — the right script is selected every single time, and real homes at real addresses with real prices are offered from live data. But four of the six bench test calls broke one of the script's own rules about how and in what order to ask questions. |
| Can callers book, reschedule, and cancel a tour? | YES, on both lines — booking, moving, and cancelling a tour were each proven on real phone calls, and each one was independently confirmed against the real calendar, not just against what Clara said out loud. |
| Do notifications (texts/emails confirming what happened) work? | YES on the bench — five real lifecycle emails landed in the mailbox, each checked three independent ways. Absent by design on the Willows — that test property's email address is a dead end that nothing can actually deliver to, so no email was expected there. |
| Does the switch between "one building" and "many homes" behave the same way every time? | YES — every real call landed on the branch it was supposed to, with no call ever getting the wrong script. |
Before dialing anything, the two lines' underlying setup was compared directly. This is the strongest evidence in tonight's round because it comes straight from the records that decide what Clara says, not from a conversation that could go any number of ways.
| The Willows, one building (+1 844-285-3526) | Portfolio bench, many homes (+1 720-807-1724) | |
|---|---|---|
| Property record | appfolio-45, type apartment, org_sandbox | portfolio-bench, type scattered_site, org_portfolio_bench |
| "Talk about multiple homes" flag present in what Clara is given | false | true |
| Length of the qualifying-questions script (area, timing, budget, in that order) sent to Clara | empty | 9,133 characters |
| Length of the many-homes override text sent to Clara | empty | 6,814 characters |
Conclusion: the fork works — it correctly tells the two lines apart — and the single-building line receives nothing new at all. It is not "the new material but turned off"; it is genuinely empty for that line.
Camellia's safety, proven two independent ways. (i) The leasing script Camellia's real callers actually hear right now was compared, word for word, against the copy sitting in the code repository — a difference check came back completely empty, meaning they are identical. Then the new many-homes marker text was searched for in both: it appears once in the staging (test) copy and zero times in Camellia's live copy, which proves the check is actually capable of telling the two apart rather than trivially matching everything. (ii) Separately, by how the system is built: the new qualifying script only ever switches on for a property recorded as "scattered-site" (homes spread across different addresses), and both Camellia's building and Yale 25 Station are recorded as ordinary apartment buildings — so structurally, it cannot switch on for either of them, no matter what a caller says. One note: Camellia's live script still carries an empty placeholder slot left over from earlier work, but it is gated off and empty. Both of these checks were done by reading the property records directly in the database, not by placing a real call to the live phone system serving a real customer.
Camellia's regression test: one test file run, 5 tests passed, 336 skipped, 0 failed.
Every call, tagged and checked. Each real call leaves a stamp on its own conversation record saying which script it got. Across every real call checked this way — three on the Willows and seven on the bench (six from tonight's round plus one carried forward from the preceding proof run) — the stamp was present on every single record, the Willows calls were tagged the single-building type every time, and the bench calls were tagged the many-homes type every time. No call ever got the wrong one, and there were no mixed or missing values anywhere in the set.
Two identical runs were made over the same stored history of 95 real Camellia conversations, replaying each one through the current system to see if it reaches the same conclusions. Both runs came back 44 passed, 51 failed, with all 95 conversations accounted for in both. Between the two runs, the sequence of actions Clara's replayed self took differed on 5 of the 95 conversations, and the final pass/fail verdict changed on 0 of the 95.
This is a measurement of the test tool's own noise, not a health score for Clara. Running the identical input twice and getting a different action sequence 5 times out of 95 means the tool itself isn't perfectly repeatable — so a raw pass rate out of this lane should never be read as "how good is Clara." Most of the 51 failures are concentrated in actions that are only supposed to happen after a call has been handed off to a live specialist — a step this replay lane cannot reproduce by its nature, since there is no real specialist on the other end during a replay.
Side note on the source data: the conversation history was pulled as 99 conversations recovered out of 100 attempted. The one that didn't come through was not lost — it was correctly refused by the privacy-scrubbing step, because two names had survived the scrub and it declined to write a record that still contained them.
Fourteen calls in total: twelve dialled tonight, plus two carried forward from the preceding live-proof round (marked below), since they land on the same two lines and the same questions.
| Call | Line dialled | What it tested | Result | Evidence |
|---|---|---|---|---|
| Earlier proof run | Bench, many-homes | Name a home, book a tour | BOOKED | A real tour, with a real calendar event, from the round before tonight's. |
| Earlier proof run | The Willows, single-building | Book a tour | NOT BOOKED — not a failure | Called after office hours; the after-hours message-desk took the call instead of Clara's booking flow, as designed. |
| A1 / A5 / A6 | The Willows, single-building | Availability, and asking about a specific home first | NOT RUN | Blocked by a real defect: the Willows currently computes zero homes to offer, so Clara could only ever say "nothing available." Dialling it would have proven nothing. The test scenarios were written and merged but deliberately not dialled. |
| A2 | The Willows, single-building | Book a tour | PASS on the booking path | A real tour, plus a real calendar entry. The call failed the round's own stricter pricing test only because there were no homes to quote a price on. |
| A3 | The Willows, single-building | Reschedule a tour | PASS | The same calendar entry survived and its start time moved — one entry, no duplicate, and not moved onto the apartment that was deliberately being withheld from Clara. |
| A4 | The Willows, single-building | Cancel a tour | PASS, both halves | The booking reached cancelled, and the calendar entry was confirmed gone by a direct check on that exact entry returning "not found" — so the half-hour was genuinely released, not just marked cancelled. |
| B1 | Bench, many-homes | Name a town, then book a tour | BOOKED, but failed four rules | Stated a count of homes in banned wording, never offered the video-walkthrough option, never asked move-in timing, never asked the open question. |
| B2 | Bench, many-homes | Reschedule a tour | PASS on the outcome | Moved to a time the caller chose, one action fired — but she offered new times before looking up the caller's existing booking. |
| B3 | Bench, many-homes | Cancel a tour | PASS, clean | Found and cancelled the booking without booking a replacement, asking for an email, or asking about budget. |
| B4 | Bench, many-homes | Caller names one specific home | FAIL | She stalled with "let me check" instead of acknowledging the address the caller had just named. Her next reply was fully correct, so the information was right and the order was wrong. |
| B5 | Bench, many-homes | Availability across two towns | FAIL | Named three, then four, then four homes per turn against an explicit two-per-turn cap, and skipped the qualifying questions entirely. Did correctly avoid asking about budget. |
| B6 | Bench, many-homes | Name a second town, then book | BOOKED, but failed four rules | Including never collecting the caller's name — something the script treats as an absolute requirement on any booked call. |
Not one of tonight's failures was in the plumbing. Every real call picked the correct script for its line, read real homes at real prices straight from live data, and every booking, every move, and every cancellation landed correctly in the database and on the real calendar, with a real notification email going out where one was expected. What actually broke is the script Clara is following, not the system underneath it: the order she is supposed to ask questions in gets skipped, the rule capping her to two homes per answer gets broken, counts of homes leak out in wording that's supposed to be banned, the video-walkthrough option never gets offered, and on one call she never asked the caller's name at all. Those are wording problems, not wiring problems — a much better failure to have than the reverse. In line with the brief for tonight, the prompt text itself was deliberately not touched; these become follow-up cards and a ruling for Fede, not overnight edits.
Nine defects came out of tonight's round. Six of the nine are faults in the test harness itself, not in the product — and four of those six fail in the direction that looks like success, meaning a clean-looking check could be silently wrong. The real headline of this run is less "how did Clara do" and more: the instruments used to check Clara's work were not themselves fully trustworthy, and that had to be discovered and corrected before its results could be trusted.
| What's wrong | Why it matters | Card |
|---|---|---|
| The Willows test property currently cannot be used to test leasing at all. | It is pointed at a new source of vacancy data, while its actual home records are still old-style test entries with no connection to the real property-management system — so the list of homes Clara has to offer computes out to zero, every time. Any recent "green" test result for availability or pricing at the Willows was probably meaningless: it passed only because there was nothing to offer, not because pricing worked. | trello.com/c/J6SfXjrV |
| The phone-call test harness has no lock stopping two test runs from dialling the same line at the same time. | This nearly happened twice tonight, for two different reasons — a misread clock, and two people starting a runner within five seconds of each other. Both were only caught by a person manually checking what was running; nothing in the system itself made it impossible. | trello.com/c/a4aJbdmy |
| The test-call system has no code-level block against accidentally dialling a real customer's number. | Camellia, Yale, and the Western Slope line are all reachable today if a test scenario's target number has a typo — the existing guard only checks "is this a registered property line," and Camellia is one. | trello.com/c/BoRnrWjZ |
| The system that builds "everything Clara knows" at the start of a call has no read-only mode — every check it runs writes a real conversation record. | Just verifying tonight's fork left real conversation records behind on test properties, as a side effect of checking, not testing. | trello.com/c/aJqpfY2F |
| The live-tool-call budget — meant to cap how many real actions (like a database write) a single call can take — is not enforced on real phone calls at all. | It currently only applies to the text-based simulation lanes, not to an actual live call, so "exactly one tour booked, never two" is only true because nothing went wrong tonight, not because anything is stopping it from going wrong. | trello.com/c/lnSwpKXl |
| The setting meant to dry-run a property's emails without actually sending them does not work on tour confirmation, reschedule, and cancellation emails. | Those three emails bypass the dry-run switch entirely and always really send. Harmless tonight because it was our own test mailbox, but on a real customer property with the dry-run setting on, real emails would go out to the property manager that nobody intended to send. | trello.com/c/98VLE9sP |
| A reschedule call fired the real reschedule action twice for a single request. | The first attempt was rejected for landing outside office hours and the second succeeded, so the end result was correct — but a mutating action fired twice on one caller request, and nothing would have caught it if both attempts had succeeded and moved the tour twice. | trello.com/c/XWN1uYyV |
| A verification script can silently read the wrong database and report the result as clean. | One of the scripts used tonight to check "are there stray test tours lying around" was, for a while, quietly reading a local development copy of the data instead of the real one — and an empty local copy looks identical to a genuinely clean result. It reported "0 tours" when there were actually 131. | trello.com/c/kmSepyd8 |
| The harness can credit a call with booking a tour that it never booked. | The check that confirms "a tour got booked" looks the caller up by phone number, and every test call dials from the same handful of shared numbers — so it can find a tour left over from an earlier call and credit it to the current one. One call that booked nothing at all was recorded as having booked a tour that actually belonged to a different call the day before. | trello.com/c/6m9TXQlJ |
Before trusting the calendar read-back used to confirm bookings, it was demonstrated reporting all three of its possible states correctly: a real event it found, an empty window correctly reported as genuinely empty, and a deliberately broken login correctly reported as "could not check" rather than "nothing there." That distinction matters — a tool that can't tell "I don't know" from "there's nothing" would make every negative result untrustworthy. Separately: calendar events showing up with no attendees listed is expected and correct, not a bug.
The mailbox read-back used to confirm notification emails was proven the same way before it was trusted: it correctly found a real email in a wide search window, correctly reported "nothing here" for an impossible subject line in a window that searched successfully, and correctly reported "could not check" rather than "nothing there" when pointed at a deliberately broken login.
The bench line's test tours are not covered by any automatic cleanup, so clearing them was done by hand, through the same booking system a real caller uses to cancel — never by editing the database directly. Cleanup is now complete: every tour record the round touched is cancelled, and — the stronger proof — a direct read of the bench calendar for Sep 10–12 came back with zero events in that window, so the calendar side is confirmed empty, not just inferred from the tour records saying "cancelled." The Willows calendar has no matching events either.
3d2d3fc7 — cancelled before the round started, at 05:17:59Z. Left alone it would have corrupted the reschedule and cancel tests, since it sat on the very phone line those tests were about to dial from.d514b75f — also cancelled before the round started, at 05:18:01Z, for the same reason: a leftover tour on a line the round was about to use.4b7d6ce8 — booked by the round itself (the book-a-tour test), moved (the reschedule test), then cancelled by the round itself (the cancel test) at 06:31:55Z, as intended.52cc13d1 — booked by the last call of the round, then cancelled during cleanup at 06:58:13Z.825c5084 — already cancelled before tonight's round; left untouched.Clara is not supposed to state a count of homes — "we have three two-bedrooms" is banned wording, and tonight's calls mostly respected that rule for homes (one slip, worded as "a couple of three-bedrooms," was still caught and counted as a violation, so the rule already reaches beyond bare numbers into vague wording too). But she freely states counts of appointment time slots on both lines — "there are eleven more later that day" on the single-building line, "9:30, 10:00, 10:30, and more later" on the bench line. One ruling would settle both lines at once: should the never-state-a-count rule also cover time slots, or is that fine as-is?
Fede, 2026-09-10: "we're gonna have to probably refactor the touring engine where we might have to look at a list of people, not just one… Do we combine their availability? Just one? Do they switch between each other, reassign?" Plus two requirements from the Western Slope call: a minimum heads-up before a tour (one hour by default, per company) and a visit length that grows with how many homes are shown.
That design is too big to live here. It is a page of its own: The touring engine — one visit, a list of people, more than one home Proposed — pending review
The invariant it proposes: a calendar belongs to a person, not to a building and not to a company; Clara asks for slots and books a visit, and who takes it is decided server-side. Eight decisions for Fede, the first of which reopens the 2026-09-10 one-calendar-per-company ruling.
Fede asked for one comprehensive test harness for the shared phone agent — real tests, evals, multi-turn conversations, the tool calls verified end to end, across both of the agent's scripts: the single-building script the Willows test line runs, and the many-homes script the bench line runs. We already had most of the pieces. What follows is an honest map of what each piece proved, the three holes that map exposed, and two design questions that are Fede's to answer.
The short version. Six separate layers of testing already point at this agent, and between them they cover the single-building script well. The many-homes script is proven for booking a tour and for the way Clara qualifies a caller, and almost nothing else. Three holes matter more than the empty cells: the live layer can prove that something happened but not that something correctly did not happen; neither script has a local multi-turn conversation test, so every rule about the ORDER Clara asks things in is enforced only by the prompt itself; and on the many-homes line, booking a tour is proven while rescheduling and cancelling have no test at any layer.
Two things the map found that read as product findings, not gaps. First, one of our own many-homes tests grades Clara for asking the caller's budget — and asking for a budget is explicitly banned in the shipped rules, and the live bench call was recorded as a pass precisely because she never asked. That test is marked "expected to fail for now", which means the day Clara regressed into asking, our harness would announce it as a fix confirmed. A test that passes under the bug certifies the bug. Second, the check that verifies a call reached the right specialist looks the agent up in the shared production list, so a call on the staging line reads as a failure even when it behaved perfectly. Both are being corrected.
Rows are behaviours; columns are the six layers that can test them. Full = a named test asserts it. Part = something adjacent is asserted, or it was observed once live without a standing test. None = nothing covers it.
| Behaviour | Unit / golden | Multi-turn eval | Simulated call | Real call | Replay | Side effect verified |
|---|---|---|---|---|---|---|
| Single-building script — the Camellia-parity bar | ||||||
| A unit question is answered first, with no town or area qualifying | Part | None | Full | Part | Part | n/a |
| Tour booked | Part | Full | Full | Full | Part | Part — only that it landed |
| Tour rescheduled | Part | Full | None | Part | None | None |
| Tour cancelled | Part | Full | None | Part | None | None |
| Tour notifications reach the property | Full | None | None | Part | None | Part |
| After-hours message desk | Full | None | Full | Known open — inconsistent on one line | None | n/a |
| Transfer to the line's own leasing agent | Full | Full | Full | Part | None | Broken — false failure on staging |
| Many-homes script — the basics bar | ||||||
| Asks the area first, then the move-in timing | Part | None | Wrong — grades a banned question | Full | None | n/a |
| One question at a time | None | None | Part | Part | None | n/a |
| Names homes with their prices by the third or fourth turn | None | None | Part | Part — happened on turn five | None | n/a |
| Never asks the caller's budget | Full | None | Contradicted | Full | None | n/a |
| Never says how many homes there are | None | None | Full | Full | Full | n/a |
| A street address the caller names is acknowledged and offered | None | None | None | Part | None | n/a |
| Two towns narrowed by town, no count given | None | None | Full | Part | None | n/a |
| Tour booked | Part | Part | Full | Full | None | Part — only that it landed |
| Tour rescheduled | None | None | None | Part | None | None |
| Tour cancelled | None | None | None | Part | None | None |
| Tour notifications reach the property | None | None | None | None | None | None |
| Both scripts | ||||||
| The same script is chosen on every run of the same call | Part | None | Part | Part | None | Full |
| The production prompt text is unchanged, to the byte | Full | n/a | n/a | n/a | n/a | n/a |
| Production and staging offer the same tools | Part — identical by construction | n/a | n/a | n/a | n/a | None — no live comparison |
| Clara does not correct the caller who uses the wrong name | Part | None | None | None | None | n/a |
| A tool fired — did our record of it appear? | n/a | n/a | n/a | n/a | n/a | Full |
| A tool did not fire — is our record correctly absent? | n/a | n/a | n/a | n/a | n/a | None, except two checks |
A note on reading this table, carried forward from §11.8: "absent" and "could not read" are different answers, and a checker that has only ever printed one of them cannot tell you which it means. Every new check described below is proved in both directions — shown failing as well as passing — before its output is believed.
One command runs the whole map for both numbers and writes a single report — every behaviour marked passed, failed or known-open, with the list of tools each call fired and whether each one's real-world effect was found. Alongside it: the missing "did this correctly NOT happen" half of the side-effect checks; multi-turn conversation tests for both scripts, advisory at first; the empty cells filled one layer at a time; and the two broken instruments above repaired. Each ships as its own small change, dark, nothing turned on at a customer.
1. Whose calendar does a scattered-homes tour go on? — ASKED AND ANSWERED; this is no longer a question for Fede. An earlier version of this section had this pending Fede's decision. That was stale by the time it was written, and the correction matters more than the original point. Fede ruled on 2026-09-10: one shared leasing calendar per company, not one per leasing person — so the multi-person question that was holding it never needed answering. The read path is already built and merged: one chokepoint, getCalendarToken, tries the building's own calendar first and the company's calendar attachment second, and every caller that matters goes through it — checking availability, booking, moving and releasing a tour. The change that proposed a per-officer calendar was closed in favour of it. So the proposal as originally framed is void, and the harness case changes shape: it should assert through that one chokepoint, and it should cover both rungs, because the bench still has a calendar of its own and therefore never reaches the second one. A case that only ever exercises rung one cannot tell you the company rung works. What is genuinely left is not a decision but a data change — taking the calendar off the bench's placeholder building so the company attachment is what answers, which is also what makes the shared-mailbox collision below stop being possible. That is our work, on a test property, not a call for Fede.
The measured cost of leaving a calendar on a placeholder building still stands and is the reason to finish this: the placeholder behind the Western Slope line shares one sandbox mailbox with the Willows stress tests, so on 2026-09-09 a 9:00 slot offered to a caller came back as a conflict and the blocking event was a synthetic Willows move-in. A real caller can be told a time was just taken by traffic from a different property.
2. "Can I see the two homes next to each other?" Right now the tour tool takes one home, so two homes means calling it twice — and we have a real call where that failed in the worst way: the second booking was refused as a time conflict because the first booking's own calendar event was in the way, and Clara read one success plus one refusal as two confirmations and told the caller both were booked. The proposal is that one visit is one calendar event at one time, and the tour tool's home input accepts a list of up to three homes; a single building always passes exactly one, so nothing about Camellia changes. The harness case asserts the many-homes script offers a combined visit only when the caller actually asks to see two homes — offering it unprompted is a failure — and that exactly one confirmation and one calendar event result. It is marked known-open, so it reports red and does not block anything until the product change lands. Pending Fede: whether to make that change.
Both cases are written and will run; neither waits on the decision to be useful, and neither product change has been made.
This page has said, since the overnight report, that rescheduling and cancelling a tour are blocked in code because those two tools "declare a property id in their inputs but do not expose it on the voice schema, so on a call the property is never passed". The first half is an accurate reading of the schema. The conclusion does not follow, and it is wrong. The handler takes its selector from whichever field arrives — the phone tool's field is honoured by the same resolver as the documented one — and the property is filled in on the server from the conversation the call belongs to, so it never needed to come from the caller. One of the two tools does in fact carry a property field on its voice schema as well.
Worth saying how this was caught, because the mechanism matters more than the fact. The wrong conclusion was carried forward into the new harness as a pre-marked known-open gap — which would have suppressed a red for a defect that does not exist, the mirror image of the budget scenario that rewarded a banned question. A code reviewer refused the characterisation and cited the three places that refute it. A claim repeated from a prior report is not evidence; it had been true-sounding for a day and a half without anyone reading the handler. Rescheduling and cancelling on a many-homes line remain untested, which is the honest status — not blocked.
The check that asks "did the team actually get a message about this call?" could be asked in two directions: prove one arrived, or prove none did. In the second direction it had a hole. It reads three places a message could have landed; if two came back empty and the third could not be read at all, it reported "confirmed: nothing was sent" — an absence certified off an incomplete look. It had been doing that since the check was written, and it would have gone on reporting clean negatives forever, because a negative that is wrong looks exactly like a negative that is right. The guard is now unconditional: if a lane could not be read, the answer is "could not check", in both directions. That is the same rule §11.8 was written about, found this time in our own apparatus rather than in a symptom.
One honest limit on all of the above. The ability to catch a write that happened when no tool asked for it is proven against fixtures, not yet against a real call — the live driver does not yet hand the checker the list of tools the call actually fired, so today that check produces no row rather than a false pass. It is one function call away and named as a follow-up. Until it lands, "we can catch a phantom write" is true of the checker and not yet true of a live call, and the report should not be read as though it were.
Four separate pieces of harness work each needed to know what the many-homes bench property is, and because there was no single place that said so, all four authors typed it out by hand — and three of the four got the same field wrong in the same direction. The field is the one that decides whether Clara promises to text you about your visit. The real bench property has texting in shadow mode, which makes the shipped code say "nothing is sent for a visit here"; the hand-typed copies said the opposite, so the tests asserted a confirmation text and two reminders that the property provably never sends. In one of them the check could not even notice, because the expected value and the actual value were both built from the same hand-typed copy — a guard comparing a thing to itself.
One refinement, or the rule above gets over-applied. "Read the real thing" raises the question of which real thing, and the answer is not the same for every fact. The script that sets a test property up is the truth about what it was built with — its type, its shadow-mode switches. It is not the truth about what has been connected to it since. The bench's setup script records no calendar at all; the live row has one, attached out of band after the script last ran. So a fixture assembled purely from the setup script would have been wrong in the opposite direction — confidently asserting no calendar for a property that has one. The working rule: structural facts come from the thing that creates them, operational facts from the live record, and a test that needs both should say which it is reading and why. The harness case for the calendar now does exactly that — it takes the live connection as the first rung's fact, and builds the second rung from the same real row with the calendar stripped off, because no property in hand exercises that rung yet.
Only one of the four was caught by review. The other three were caught because, once the first was known, everyone went and looked. The fix is not three fixes. There is now one module that reads the real provisioned property, and the harness reads that instead of describing the property again — the same thing the homes list already does, which is why the homes were never wrong. The general rule, worth more than the incident: a test fixture that re-describes production is a second source of truth, and it will drift toward whatever the author assumed. Read the real thing, or assert nothing about it.
Bench testing with Fede 2026-09-11 settled four rules for how the staging and demo environments work, and named four more decisions still open for his call.
1. Production behaviour on staging lines. Fede: "I need to be testing what production would be like, not experimental features." Both the Willows and bench phone lines render production-grade behaviour (real prompts, live tools, no test gates affecting their paths). Dark features that are behind isTest conditions move to yale-sandbox only, never to a staging number that fields production calls.
2. The Willows is a demo, not a test dump. Fede: "It's a production-grade demo environment for customers." The property uses demo-grade data (fictional companies and personas, no real financial numbers), confirmations are on (residents and staff see real email and text activity), and no robot calls by default — the single-building voice line's outbound caller (the one that dials residents) moves to yale-sandbox, leaving the Willows voice line for customer-facing tours only.
3. Bench property configuration mirrors Camellia. The portfolio-bench property is now seeded to match Camellia's live settings: SMS shadow mode off (confirmations ship), tour confirmation texts on, office hours set, and nine knowledge-base fields left empty to match Camellia's state. The property's display name is changed from the literal "portfolio-bench" to a fictional company name.
4. Four decisions still open. (a) Cut the "is the date firm" clause from the booking script? (b) Drop or shorten the open-needs question — option A drops it, option B shortens it? (c) Should time-slot counts be in scope for the rules that guard what Clara states? (d) Pending: the past-weekday rule above (§ this page).
The independent inspection said two checks had never been run once, and told us to run them and publish the numbers as one table before any more changes to the phone script. This is that table, plus every other check that now has a real number against it. From here on, a change to the script is measured against these numbers — not against a fresh opinion.
What is new here. The full phone-agent test suite had never been read against these lines. The written conversation eval had never been executed at all — its first run is below. The prototype-versus-staging comparison had never existed. All three now have numbers. Two of them are uncomfortable, and they are printed as they came out.
| Check | What it measures | Ran? (date, sha) | Result | Where to verify |
|---|---|---|---|---|
| Full phone-agent suite, nightly | Every scripted conversation we have — 398 of them across five kinds — played against the live agents as text, with every tool faked so nothing real is written. | 2026-09-11, 09:37–09:56Z, scheduled on main at 9fde4146c |
342 pass / 56 fail of 398. Fifteen of those failures are bugs we already know about and track. That leaves 41 real failures. Five conversations also called a tool when the design says they should have needed none. | Actions run 34585023027, artifact voice-comprehensive-report |
| Same suite, before the script was cut back | The pull-request lane runs only two of the five kinds — 270 conversations — so it is cheap enough to run per change. | 2026-09-11, 17:28–17:41Z, 8cfe2d16e |
227 pass / 43 fail; 33 real failures once the ten known bugs are set aside. | Actions run 34627662447 |
| Same suite, after the script was cut back | Identical 270 conversations, run on the branch that strips the many-homes script back to the prototype plus filters. | 2026-09-11, 17:57–18:11Z, eb18853bf (PR #7822) |
228 pass / 42 fail; 33 real failures — the same number as before the cut. Do not read this as "no change". Each run plays every conversation once against a simulated caller that is not deterministic: 12 conversations failed only in the earlier run and 11 only in the later one. The totals are stable; which ones fail is not. Proving a script change moved behaviour needs repeats, not one run each. | Actions run 34630415449 |
| Leasing conversation set | The 66 leasing conversations, run on their own against the live leasing agent with tools faked. | 2026-09-11, 18:22–18:31Z, current main 30009fa34 |
61 of 66 pass, 5 fail. The five failures, in plain terms: Clara promises to text the application link when nothing is behind that promise; she tries a third time to confirm a garbled name after the caller has moved on; asked point-blank how many one-bedrooms exist she says "a few" but omits the price band the rule requires in its place; she cannot produce a named home's square footage after looking it up; and a switch to a virtual tour with no matching booking asks for an email it should not. All five are single-building behaviour. Two of them also failed in the pull-request run 50 minutes earlier, so they are not one-off noise. | scripts/voice-harness/drivers/l1-simulate.ts (npm run test:voice-leasing); log in this session's scratchpad at baseline/voice-leasing.log |
| Written conversation eval — the one the inspection said had never run | Eight written multi-turn cases, four per script, checking the order Clara asks things in, that she never states a home count, and that she never corrects a caller who uses the wrong name. Graded by fixed code, not by a model. | 2026-09-11, 18:22Z, current main 30009fa34 — first execution ever |
Single-building script: 4 of 4 pass. Many-homes script: 0 of 4 — all four errored before reaching the model. The error is in the test pack, not the agent: one field ({{prospect_context}}) is never filled in on the many-homes side, so the prompt reaches the model with a literal placeholder in it and the run aborts by design. The many-homes half of this eval therefore still has no measured result. Fixing the fixture is the next step, and the eval stays advisory until it has one. |
evals/promptfooconfig-shared-phone-agent-multiturn.yaml, via bash evals/run-all.sh shared-phone-agent-multiturn. Billed to the Claude subscription, never a metered key — the runner removes that key from the environment and proves it is gone before starting. |
| Replay of Camellia's real recorded calls | Real past conversations replayed against the staging agent, to see whether it transfers or stalls where the real one did not. | 2026-09-11, 10:25Z, scheduled on main at a3c390d49 |
Nothing was replayed. The job reports success and a green tick, but its own log says it skipped: four settings it needs (the agent ids and the single-property number) are not set on the repository, so it exited without playing a single call. A green tick on this job currently means "did not run", not "passed". That is a hole in our evidence, not a result, and it is the same failure mode this page has written about twice: an absence that looks identical to a pass. | Actions run 34589166218, job log annotations |
| Prototype versus staging — the other check the inspection said had never run | The prototype's own ten scenarios played against both the staging many-homes line and the 970 prototype line, and compared row by row on eleven points. | 2026-09-11, 16:34Z, against base main c730657510 |
110 rows: 4 differ, 29 match, 77 not applicable. Exit code 1 — hold the cutover. In all four, staging is the worse side, and all four are bugs, not deliberate differences. One: the caller says "go ahead and book it" and staging never books the visit (the scenario expects two bookings). Three: staging never uses the caller's name — Sam, Riley and Casey — where the prototype does. The name defect reproduced in every run. | npm run test:voice-cutover-gate; report in this session's scratchpad at cutover-gate/diff-table-latest.md |
| Clone checklist — the Willows line | The seven rows the inspection defined as the meaning of "a production-ready copy", run as code against the live data. | 2026-09-11, 18:16:20Z, current main 30009fa34 |
NOT READY — 3 pass, 2 fail, 2 need a human. Pass: the agent settings match, the prompt matches to the byte, the greeting matches. Fail: the gate audit (28 of 37 switches answer differently than production) and data parity (2 of 5 — see below). The two needing a human are one daytime call per line and the prototype comparison. 12 homes offerable; office hours and time zone correct; no confirmation text expected on this line, by configuration. | Checklist run output, this session's scratchpad at clone-checklist/willows-2026-09-11T18-16-18Z.md |
| Clone checklist — the bench line | The same seven rows for the many-homes stand-in. | 2026-09-11, 18:16:26Z, current main 30009fa34 |
NOT READY — 3 pass, 2 fail, 2 need a human. Same three passes, same two failures, same two manual rows. 15 homes offerable; office hours and time zone correct; tour texting is on, so a caller who asks for a visit does get a text. | Checklist run output, this session's scratchpad at clone-checklist/bench-2026-09-11T18-16-24Z.md |
1. Where the list of available homes comes from. Both stand-in lines read availability from the rent roll; production reads it from the vacancy list in the property system. Kept on purpose: pointing the Willows back at the production source left the line with zero homes to offer, which is the defect that ruined Thursday morning. The difference is recorded rather than hidden, because it means what a caller is offered can differ for a reason that has nothing to do with the agent.
2. The gate audit's 28 differing switches are not 28 differences a caller could hear. Of the 28, nineteen are the .isTest safety fences — email sinks, overnight sweeps, write guards — that exist precisely so a test property cannot send real mail or write real records. A caller never hears those. The checklist currently counts them as failures anyway, because it has no way to tell a safety fence from a behaviour change; splitting the row into "a caller could hear this" and "safety only" is the follow-up, and until it lands this row will stay red for the wrong reason. The one differing switch that genuinely sits on the path a caller hears is src/lib/integrations/voice/bench-codes-rule.ts:43, which adds a block of text to the prompt — but only when the caller's own number is one of ours, so a customer calling the same line gets nothing.
3. The tester's number is still known to the system. It was cleared earlier in the day; by the time the checklist ran at 18:16Z it resolved as a known prospect again, carried by three person records. The cause on record is the property-system sync re-creating it from a Test Property 45 record that carries Fede's email address. Until that is fixed, a call from that number is never a cold call — it is greeted with history — so the "one daytime call from a number with no stored identity" row cannot honestly be claimed. Fix carded for tomorrow.
4. The prototype comparison measures the script as it stood before the cut. It ran at 16:34Z today; the unapproved qualifying order was deleted an hour later. Its four differing rows describe that older script. The re-run after the cut will produce different rows, and those replace these.
The rule this baseline sets. A change to the phone script merges only against these numbers — the prototype comparison and both clone checklists are re-run after each one, and their new output replaces the rows above. No "ready", and no script-order change, without that re-run attached.
This is the go/no-go page the independent inspection asked for: seven rows per phone line, pass or fail on each, the proof-call transcripts attached in full, and every known difference written down with the reason it is there. Everything below was measured this evening against one build of the product — 71b3e92d93 — and every number points at the file it came from.
| # | What | Line | State | What it would take |
|---|---|---|---|---|
| 1 | Availability comes from a different place than production. Both stand-in lines read what is available from the rent roll; Camellia reads it from the property system's vacancy list. | both | red, on purpose | Nothing tonight. Pointing the Willows back at the production source left it with zero homes to offer — the defect that ruined Thursday morning. Recorded as a known difference rather than hidden. |
| 2 | Fede's own phone number is already known to the system, so a call from it is greeted with history instead of as a stranger. It is the number the checklist is written to test with. The proof calls were not affected — they were placed from a robot number with no stored identity, and both conversations show the returning-caller blocks empty, so the calls were genuinely cold. | both | red | A fix is in progress tonight, and the check is being re-scoped to the line's own company. See the note under the row-5 table — what is actually holding this red is not what we assumed. |
| 3 | The prototype-versus-staging gate says hold. 110 rows: 3 differ, 30 match, 77 do not apply. | bench | DIFF — hold | Two of the three differences are faults in the grader, being fixed. One is real: after hours, our line hands the caller to the message desk without ever asking who is calling, so it never uses their name. |
Also pending, and not blocking on their own: text-message registration for the bench number +17208071724 (Fede's call, below); and PR #7853 and anything after it, which landed after this measurement and are therefore pending on this page.
appfolio-45, standing in for Camellia)Run 2026-09-11 21:02:46–21:02:48Z against product code 71b3e92d9327dfecf1bd1536b5cc234da85f5630, reading the production table. Full output: final/willows-2026-09-11T21-02-39Z.md.
| # | Row | Verdict | What it means | Where it came from |
|---|---|---|---|---|
| 1 | Agent settings diff | PASS | Voice, model, language, greeting template, timing and the tool list are identical on the test agents and the production ones. | Checklist §1 — triage agent_4701m0r9… vs agent_2801krxq…; leasing agent_5901m0r9… vs agent_8601krxq…. No difference on any compared field. |
| 2 | Rendered-prompt diff | PASS | After filling in the property facts, the two leasing prompts are byte-identical. | Checklist §2 — same fixture of property facts on both sides, so only genuine prompt differences survive. |
| 3 | Greeting, printed side by side | PASS | "Hi, it's Clara at The Willows — what can I help you with?" against "Hi, it's Clara at Camellia Apartments — what can I help you with?" With the property name blanked, they match. | Checklist §3, cold unknown English caller with no tour and no open work order on both sides. |
| 4 | Gate audit, generated by grep | PASS | 36 test-property switches found. 9 can change what a caller hears; 6 of those answer exactly as production does, and 3 differ on purpose with a written reason each. The other 27 are safety fences a caller never hears. | Checklist §4 — scanned the voice, leasing, promises and agent code; test files excluded. Each switch carries its file, line, function and its reason for being classified the way it is. |
| 5 | Data parity | FAIL | 3 of 5 checks green: 12 homes offerable; office hours and time zone correct (America/Denver, in hours at run time); tour texting is off on this line, so "no text expected" is the configured answer. 2 red: availability source, and the tester number is already known. | Checklist §5. Property row and its leasing settings read strongly consistent; the homes and identity lookups go through secondary indexes, which cannot be read strongly consistent — the page says so rather than implying a guarantee. |
| 6 | One daytime call, graded against a real Camellia call | ATTACHED | Placed by the orchestrator itself at 21:02:36Z (15:02 MT) from robot number +17205942061, which has no stored identity. Booked a tour. Graded turn by turn below: everything matches, differs on purpose, or is behaviour Camellia shares — no clone difference was found. | proof-calls/row6-willows-artefacts.md. Conversation conv_1801m294eyejfw5rdk16req36b4t, Twilio CA14ff43db…, tour 2d81c465-…. |
| 7 | Prototype parity | n/a | This row is scoped to the many-homes line. A single building has no prototype script to compare against, so it is left unclaimed rather than marked green. | Checklist §7. |
portfolio-bench)Run 2026-09-11 21:02:54–21:02:57Z, same build, same table. Full output: final/bench-2026-09-11T21-02-39Z.md.
| # | Row | Verdict | What it means | Where it came from |
|---|---|---|---|---|
| 1 | Agent settings diff | PASS | Same as the Willows — the bench is answered by the same pair of staging agents, and no compared field differs from production. | Checklist §1. |
| 2 | Rendered-prompt diff | PASS | 119 lines differ and every single one of them is the declared many-homes block — the extra instructions a scattered-homes line needs and a single building does not. Nothing else differs. | Checklist §2, with all 119 lines printed for the record. |
| 3 | Greeting, printed side by side | PASS | Same template, property name substituted. The name itself was wrong at call time — it spoke an internal label — and was fixed the same evening (below). | Checklist §3. |
| 4 | Gate audit, generated by grep | PASS | Identical result to the Willows: 9 caller-audible switches, 3 differing on purpose, 27 safety fences. | Checklist §4. |
| 5 | Data parity | FAIL | 3 of 5 green: 15 homes offerable; office hours and time zone correct; tour texting is on here, so a caller who asks for a visit does get a text. Same 2 red as the Willows. | Checklist §5. |
| 6 | One daytime call, graded against a real Camellia call | ATTACHED | Placed by the orchestrator at 21:05:59Z (15:05 MT) from the same identity-free robot number. Booked a tour on a named home in Clifton. Graded below; seven findings raised, one of them serious. | proof-calls/row6-bench-artefacts.md. Conversation conv_2001m294n83kegkb2b24s5thjqe7, Twilio CAfb0f8de5…, tour 403ae24a-…. |
| 7 | Prototype parity | DIFF — hold | The prototype's own 10 scenarios played against our line and compared on 11 points: 110 rows — 3 differ, 30 match, 77 do not apply. Exit code 1. This morning's baseline was 4 differ / 29 match / 77 not applicable (§11.18). | npm run test:voice-cutover-gate on main (PR #7805), one run, 21:03:02→21:06:52Z. Report: final/cutover-gate/cutover-gate-2026-09-11T21-03-02-939Z.md. Every tool faked, nothing written, no phone call placed. |
We assumed this was the property-system sync re-creating Fede's number from a Test Property 45 record that carries his email. On this run it is not. The three person records carrying the tester's number were last touched on 2026-09-06, none of them carries an email at all, and the record the system actually resolves to is a Western Slope prototype prospect created 2026-09-06 (3b88ef6c-…, property western-slope-proto). So the sync fix that is in progress tonight will not clear this row on its own — the check is being re-scoped to the line's own company instead. Source: final/row5-identity-claim-note.md, re-read independently at 21:05:09–21:05:20Z.
This did not weaken the proof calls. Both were placed from robot number +17205942061, which has no stored identity, and both conversations confirm it: the returning-caller context blocks rendered empty and the line had no tour on file for the caller. The red row is about Fede's own cell phone, which is the number the checklist is written to test with.
Reference: conv_1201m1ykdwj8ejms7ks9e3pt5v1w, a real Camellia production call, Mon 2026-09-07 12:52 PM MT. Both transcripts are attached in full below.
| # | Camellia said | The Willows line said | Verdict | Reason |
|---|---|---|---|---|
| 0 | "Hi, it's Clara at Camellia Apartments — what can I help you with?" | "Hi, it's Clara at The Willows — what can I help you with?" | match | Identical template, property name substituted. No Spanish clause on either side. |
| 1 | "Schedule a tour." | "Hi. What do your one bedrooms start at right now?" | on purpose | The robot caller's script differs by design — this one opens on price. |
| 2 | "Let me check pricing and availability for you." | "Let me check pricing and availability for you." | match | Word for word, same position in the call (11 s vs 10 s). |
| 3 | Hands off to the leasing agent at 12 s | Hands off to the leasing agent at 11 s, same payload, one hop | match | Same handoff, and the conversation id survives it. |
| 5 | First question: "What type of apartment are you looking for — a studio or a one-bedroom? And when are you hoping to move in?" | No first question. Goes straight to the answer: price, availability, the move-in special, "Want to come take a look?" | shared behaviour | Not a clone difference (orchestrator ruling, 2026-09-11): the two lines render byte-identical prompts and the same tools, so Camellia does the same thing on the same opener — the reference call was simply opened differently. The caller had already given the size, so skipping that half is defensible; but when they need to move in is never asked at any point, and the booking was made with no move-in date. Filed as a product observation for the Camellia lane, in the open findings below. |
| 6 | "One bedroom, September 30th." | "Yes, I'd love to tour tomorrow afternoon. Can you get me in?" | on purpose | Caller script. |
| 7 | Price, the move-in special, then two concrete tour days | Price and the move-in special at 13 s; concrete slots with a named day at 38 s | match | Same substance — homes named with a price, the special volunteered unprompted, real slots on a named day. Only the order differs, because the caller opened on price. |
| 8+ | Five turns of slot negotiation; Clara re-offers, and pushes back when a time falls outside office hours | One round. The caller asks twice for an afternoon; Clara offers three morning slots, then books noon | shared behaviour | Not a clone difference (same ruling): identical prompt, identical tools — Camellia would answer the same way if asked the same way; the reference call never made this request. Two parts: an explicit "tomorrow afternoon" was answered with 10:30, 11:00 and 11:30; and the caller's direct "what do you have afternoon?" went unanswered before noon was chosen for them. Filed as a product observation for the Camellia lane. The office-hours pushback was never exercised here, so that half is untested rather than failed. |
| 13 | "And what's your name?" | "Let me grab your name first — what should I put on the booking?" | match | Same act, same position — after the slots, before the booking. |
| 15 | "Let me get that booked for you!" | "Let me get that booked for you, Riley!" | match | Same filler, name appended. |
| 16 | Books once, carrying a move-in date | Books once, no move-in date | match | Same tool, same shape, one call. The missing move-in date is the shared-behaviour observation at row 5 above. |
| 18 | "You're all set — tour confirmed for Thursday, September 10th at ten in the morning at Camellia Apartments! Anything else I can help you with?" | "You're all set, Riley — tour confirmed for Saturday, September 12th at noon at The Willows! Anything else I can help you with?" | match | Same template. Day, date, time and place all agree with the tour record and with the calendar entry. |
| 18b | Promises nothing | Promises nothing. A confirmation text is nevertheless really sent, and the system's own promise record was closed as kept | match | The spoken content matches; see the pending text-message question below. |
| 20–21 | Signs off and ends the call itself | Never reached — the robot hung up first | not comparable | Caller-driven. One of the three saved Camellia references ends the same way, so a hang-up before the sign-off is inside the normal production range. |
| — | — | Pet policy answered on request after the booking | match in kind | A Camellia reference answers arrival detail the same way after booking. |
| — | — | The bench standing order was not spoken; the many-homes "which home or which area" question was not asked | match, required | Both would have been differences on a single-building line. Neither appeared. |
| # | Camellia said | The bench line said | Verdict | Reason |
|---|---|---|---|---|
| 0 | "Hi, it's Clara at Camellia Apartments — what can I help you with?" | "Hi, it's Clara at Mesa County Homes — scattered homes (TEST) — what can I help you with?" | match / content | Same template exactly. The stored display name leaked an internal label into what the caller hears — fixed the same evening, one field changed through the app's own rename route, everything else on the property row verified unchanged. |
| 1 | "Schedule a tour." | "Hi, I'm looking for a place in Fruita. What do you have available there?" | match in shape | A cold prospect with leasing intent; this one names an area, as a scattered-homes scenario must. |
| 2 | "Let me check pricing and availability for you." | "Let me check pricing and availability for you!" | match | Same handoff line before the transfer. |
| 3 | Hands off to leasing | Hands off to leasing, identical payload | match | — |
| 5 | First question: size and move-in, in one sentence | Answers the area first — "We don't have anything listed in Fruita right now, but we do have some great options in nearby Grand Junction and Clifton" — then asks, word for word, "What size are you looking for, and when are you hoping to move in?" | on purpose | This is the many-homes front door doing its job: the caller named an area, so the area gets answered before anything else, and if we have nothing there we say so plainly and offer the nearest place we do have. The follow-up is Camellia's combined question verbatim. |
| 6 | "One bedroom, September 30th." | "Just something quiet. A two-bedroom would be great. And I'm thinking sometime in the next couple months." | match | Caller gives size and move-in. |
| 7 | Price band, the move-in special, then tour days | Per-area price bands and the free-rent concession, then two named homes with street, rent and size | match in shape / accuracy | Naming homes as homes — street, bedrooms, rent, at most two per answer — is the bench rule, and it held. Two numbers were loose: the three-bedroom band was quoted as topping out at $2,775 when a $2,825 home is on the list, and "a couple of two-bedrooms" in Grand Junction where exactly one exists. |
| 8+ | Five turns of slot negotiation, including office-hours pushback | One offer — Friday 4:30 or 5 PM, or Saturday from 8 AM — accepted immediately | not exercised | The caller took the first slot, so the negotiation and the office-hours pushback never got a chance to fire. |
| 13 | "And what's your name?" as its own turn | Bundled: "…which of those works for you? And what's your name so I can get the tour booked?" | match | Camellia bundles two asks in exactly this way at its own first leasing turn. The old "one question per turn, always" instruction was deleted this afternoon (PR #7818) precisely because it contradicted the shared script. |
| 15 | "Let me get that booked for you!" | "Let me get that booked for you!" | match | Byte-identical. |
| 16 | Books once, no home named (one building) | Books once, naming the home: "486 32 1/8 Road, 4, Clifton" | on purpose | A scattered-homes line has to say which home; a single building has nothing to name. |
| 18 | "You're all set — tour confirmed for Thursday, September 10th at ten in the morning at Camellia Apartments!" | "You're all set, Morgan — tour confirmed for today, Friday September 11th at four-thirty PM at 486 32 1/8 Road in Clifton. You'll get a confirmation text shortly with the address." | match / on purpose | Same sentence, same order. The "place" is the home rather than the company, because on a scattered-homes line the company name names nothing a caller can drive to. |
| 18b | Promises nothing | Promises the confirmation text — and it arrived, delivered, 22 seconds later | on purpose | The bench keeps the confirmations-and-reminders block, so it answers with the actual send instead of deferring to a person. Camellia has no such block, so its silence is not a fault on either side. |
| 20–21 | Signs off and ends the call itself | Never reached — the robot hung up | not exercised | Caller-caused. |
| — | — | The bench standing order rendered as empty; no Spanish clause in the greeting | match, required | The robot's number is registered as a tester, not a bench admin, so the bench text correctly did not appear. |
Four of the automated grader's complaints about this call were checked one by one. Three are the grader quoting rules that were deleted from the script this afternoon and no longer exist anywhere — one question per turn, offering both tour formats, naming a team member in the confirmation. The fourth splits: "a couple of options" is the phrasing the script itself prescribes, but "a couple of two-bedrooms around sixteen hundred" is a real violation — counting what matches in one area and one bedroom size is counting the portfolio, which the live instructions ban outright, and it was also factually wrong.
The recordings were pulled and re-transcribed independently, because the failure we were hunting for — an English sentence delivered in a Spanish-accented voice — does not show up in a written transcript.
| Check | The Willows call | The bench call | Reading |
|---|---|---|---|
| Language detected, first agent sentence | English, 0.987 confidence | English, 0.938 | English on the greeting, both lines. |
| Language detected, first leasing turn | English, 0.974 | English, 0.975 | English after the handoff too. |
| Language detected, whole call | English, 0.959 | English, 0.966 | No Spanish anywhere in either recording. |
| How many voices spoke | One — cNYrMw9glwJZXR8RwbuR, 59.04 s of it | One — the same id, 108.24 s | The earlier bug showed up as a second voice on the first sentence. There is no second voice on either call. |
| Which voice | "Belle – Empathetic Customer Service", labelled American accent, English — the same voice every one of our eight agents is configured with, Camellia's included | Same voice as production, not a lookalike. | |
| Did anything switch language mid-call | No. The language-detection tool was available and never fired on either call; no per-call language override was set. | — | |
| How fast the line picks up | First spoken word at 0.599 s | First spoken word at 0.639 s; time-to-first-audio 0.135 s | Camellia's own reference call, measured the same way, starts at 0.620 s. The Willows is 21 milliseconds faster than Camellia. No finding. |
Stated honestly: the transcriber identifies the language spoken, not the accent — an English sentence read with a Spanish accent would still come back as English. The accent evidence is the voice-id lookup and the single-voice fact, not the transcription. The two together are what make this a pass. Separately, and not on this call: a different Willows conversation at 04:03Z the same day did open with the Spanish clause, which the ruling says should never render on this line. Our proof call was clean; that one is a separate lead.
conv_1801m294eyejfw5rdk16req36b4t, 2026-09-11 21:02:36Z, 112 s)The Willows +18442853526 · caller +17205942061 · ended by the caller hanging up. Verbatim from the conversation record.
[0s] CLARA (Triage)
Hi, it's Clara at The Willows — what can I help you with?
[5s] CALLER
Hi. What do your one bedrooms start at right now?
[10s] CLARA (Triage)
Let me check pricing and availability for you.
[11s] CLARA (Triage) — (no speech, tool turn)
TOOL transfer_to_agent → {"agent_number": 0}
[11s] CLARA — (no speech, tool result)
RESULT transfer_to_agent → transfer_to_agent_success, status success,
from_agent agent_4701m0r9axyef53ry3bdtjkndcdp,
to_agent agent_5901m0r9azzdetn8b1w2zyjy3v44,
condition "Caller's intent matches leasing scope (see prompt-side routing rules)."
[13s] CLARA (Leasing)
The one-bedrooms start at fifteen-fifty a month, and we've got several available right now.
We're also running one month free on 12-month leases — you'd pay for the month you move in,
and the next month is the free one. Want to come take a look?
[32s] CALLER
Yes, I'd love to tour tomorrow afternoon. Can you get me in?
[38s] CLARA (Leasing)
Tomorrow is Saturday, September 12th — I've got slots at 10:30 AM, 11:00 AM, 11:30 AM,
and more later that day. What time in the afternoon works best for you?
[54s] CALLER
Any afternoon slot works great for me. What do you have afternoon?
[59s] CLARA (Leasing)
Let me grab your name first — what should I put on the booking?
[65s] CALLER
It's Riley Chen.
[67s] CLARA (Leasing)
Let me get that booked for you, Riley!
[69s] CLARA (Leasing) — (no speech, tool turn)
TOOL schedule_tour → {"preferred_date": "2026-09-12", "preferred_time": "12:00 PM",
"prospect_name": "Riley Chen", "desired_bedrooms": 1,
"system__message_to_speak": "Let me get that set up for you!"}
[73s] CLARA — (no speech, tool result)
RESULT schedule_tour → {"success":true,
"tourId":"2d81c465-b410-4b46-bbff-e8fa028dbeeb","auto_confirmed":true,
"unit_number":null,"booked_time":"12:00 PM",
"message":"Tour confirmed for Riley Chen at The Willows on 2026-09-12 at 12:00 PM.
It's been added to the calendar.",
"spoken_confirmation":"Your tour is confirmed for Saturday, September 12 at 12:00 PM.",
"mismatch":false}
[73s] CALLER
Great, thank you.
[76s] CLARA (Leasing)
You're all set, Riley — tour confirmed for Saturday, September 12th at noon at The Willows!
Anything else I can help you with?
[87s] CALLER
Quick question: What's the pet policy for a small dog?
[92s] CLARA (Leasing)
Up to two pets per unit, no breed or weight restrictions. There's a three-hundred dollar
one-time pet fee, a three-hundred dollar refundable pet deposit, and thirty-five dollars
a month in pet rent. Anything else?
[109s] CALLER
No, that's all. Thanks so much. Clara?
— caller hung up. No end_call tool. Termination: "Call ended by remote party".
Independent re-transcription of the same audio agrees word for word: "Hi, it's Clara at The Willows. What can I help you with? Hi, what do your one-bedrooms start at right now? Let me check pricing and availability for you. The one-bedrooms start at fifteen fifty a month, and we've got several available right now. We're also running one month free on twelve-month leases. You'd pay for the month you move in, and the next month is the free one. Wanna come take a look? Yes, I'd love to tour tomorrow afternoon. Can you get me in? Tomorrow is Saturday, September twelfth. I've got slots at ten thirty AM, eleven AM, eleven thirty AM, and more later that day. What time in the afternoon works best for you? Any afternoon slot works great for me. What do you have afternoon? Let me grab your name first. What should I put on the booking? It's Riley Chen. Let me get that booked for you, Riley. Great. Thank you. You're all set, Riley. Tour confirmed for Saturday, September twelfth at noon at The Willows. Anything else I can help you with? Quick question. What's the pet policy for a small dog? Up to two pets per unit, no breed or weight restrictions. There's a three hundred dollar one-time pet fee, a three hundred dollar refundable pet deposit, and thirty-five dollars a month in pet rent. Anything else? No, that's all. Thanks so much. Clara?"
conv_2001m294n83kegkb2b24s5thjqe7, 2026-09-11 21:05:59Z, 167 s)Bench line +17208071724 · caller +17205942061 · ended by the caller hanging up. Verbatim from the conversation record.
0. CLARA (Triage) [t+0s]
Hi, it's Clara at Mesa County Homes — scattered homes (TEST) — what can I help you with?
1. CALLER [t+8s]
Hi, I'm looking for a place in Fruita. What do you have available there?
2. CLARA (Triage) [t+13s]
Let me check pricing and availability for you!
3. CLARA [t+14s] (no speech — tool turn)
TOOL transfer_to_agent → {"agent_number": 0}
→ transfer_to_agent_success, from agent_4701m0r9axyef53ry3bdtjkndcdp
to agent_5901m0r9azzdetn8b1w2zyjy3v44
4. CLARA [t+14s] (empty turn — post-tool continuation)
5. CLARA (Leasing) [t+17s]
We don't have anything listed in Fruita right now, but we do have some great options in
nearby Grand Junction and Clifton. Would you like to hear about those, or are you pretty
set on Fruita specifically?
6. CALLER [t+31s]
I'm pretty flexible on location, so sure, I'd love to hear what you have.
7. CLARA (Leasing) [t+36s]
We've got a nice mix across Grand Junction and Clifton. In Grand Junction, there are
three-bedroom homes running from about nineteen-fifty to twenty-seven seventy-five a month,
and a couple of two-bedrooms around sixteen hundred. In Clifton, there are two-bedrooms
starting around twelve-fifty a month — and the Krista Street homes in Grand Junction are
offering four weeks free rent on new twelve-month leases right now.
What size are you looking for, and when are you hoping to move in?
8. CALLER [t+69s]
Just something quiet. A two-bedroom would be great. And I'm thinking sometime in the next
couple months. Pretty flexible.
9. CLARA (Leasing) [t+77s]
Great — for two-bedrooms, I've got a couple of options. There's a place on 32 1/8 Road in
Clifton at twelve-fifty a month — about eight-ninety square feet. And there's another on
Elm Avenue in Grand Junction at sixteen hundred a month, a bit more compact at around
six-seventy square feet.
Want to hear about either of those, or would you like to come take a look?
10. CALLER [t+103s]
The Clifton one sounds great. I'd love to see it.
11. CLARA (Leasing) [t+109s]
I'd love to get that set up for you! I have Friday, September 11th at four-thirty or five PM,
or Saturday the 12th starting at eight AM — which of those works for you?
And what's your name so I can get the tour booked?
12. CALLER [t+126s]
Friday the 11th at four-thirty works. And my name is Morgan Ellis.
13. CLARA (Leasing) [t+133s]
Let me get that booked for you!
14. CLARA [t+134s] (no speech — tool turn)
TOOL schedule_tour → {"preferred_date":"2026-09-11","preferred_time":"4:30 PM",
"prospect_name":"Morgan Ellis",
"unit_number":"486 32 1/8 Road, 4, Clifton, CO 81520","desired_bedrooms":2,
"notes":"Caller is flexible on move-in, looking within the next couple of months.
Interested in a quiet two-bedroom.",
"system__message_to_speak":"Let me get that booked for you!"}
→ {"success":true,"tourId":"403ae24a-1bea-41b9-aae6-f1f5ac1eb913","auto_confirmed":true,
"unit_number":"486 32 1/8 Road, 4, Clifton, CO 81520","booked_time":"4:30 PM",
"spoken_confirmation":"Your tour is confirmed for Friday, September 11 at 4:30 PM.",
"mismatch":false}
15. CLARA [t+139s] (empty turn — post-tool continuation)
16. CALLER [t+143s]
I'll wait for Clara to finish.
(audio-only, dropped by the live transcript: the robot begins to wrap up and is cut off —
"Perfect. Thank you so much for taking the time to—")
17. CLARA (Leasing) [t+146s]
You're all set, Morgan — tour confirmed for today, Friday September 11th at four-thirty PM
at 486 32 1/8 Road in Clifton. You'll get a confirmation text shortly with the address.
Anything else I can help you with?
18. CALLER [t+165s]
No, that's all. Thanks.
— caller hung up. No sign-off turn, no end_call tool.
The independent re-transcription of the audio matches; its only divergences are transcriber artefacts ("Christa" for "Krista", the street number read out as words, and the robot's clipped wrap-up line that the live transcript dropped).
conv_1201m1ykdwj8ejms7ks9e3pt5v1w, Mon 2026-09-07 12:52 PM MT, 125 s)Camellia Apartments +18445101007 · a real prospect, scrubbed: names replaced with [CALLER], phone numbers and emails replaced. The residual-check ran clean, and the caller's name was fed in as an authoritative variable first because that check has a known blind spot for names given as bare answers.
[t+0s] CLARA Hi, it's Clara at Camellia Apartments — what can I help you with?
[t+8s] [CALLER] Schedule a tour.
[t+11s] CLARA Let me check pricing and availability for you.
[t+12s] CLARA (no speech — tool turn)
TOOL transfer_to_agent → {"agent_number":0}
[t+12s] CLARA (empty turn — post-tool continuation)
[t+14s] CLARA Happy to get that set up! What type of apartment are you looking for —
a studio or a one-bedroom? And when are you hoping to move in?
[t+26s] [CALLER] One bedroom, September 30th.
[t+31s] CLARA Great! We have one-bedrooms starting at twelve hundred a month, and there's
a move-in special right now — one month free on new 12-month leases.
I've got tour times available Tuesday, September 8th or Wednesday,
September 9th — do either of those work for you, and what time would you prefer?
[t+53s] [CALLER] No.
[t+56s] CLARA No problem! What day works better for you?
[t+61s] [CALLER] Thursday, September 10th or Friday, September 11th.
[t+66s] CLARA What time works best for you on either of those days?
[t+74s] [CALLER] Thursday, September 10th at 10:00 a.m. or 12:00 p.m.
[t+83s] CLARA And what's your name?
[t+86s] [CALLER] [CALLER].
[t+92s] CLARA Let me get that booked for you!
[t+95s] CLARA (no speech — tool turn)
TOOL schedule_tour → {"preferred_date":"2026-09-10","preferred_time":"10:00 AM",
"prospect_name":"[CALLER]","desired_bedrooms":1,
"desired_move_in":"2026-09-30",
"system__message_to_speak":"Let me get that booked for you!"}
[t+100s] CLARA (empty turn — post-tool continuation)
[t+105s] CLARA You're all set, [CALLER] — tour confirmed for Thursday, September 10th at ten
in the morning at Camellia Apartments! Anything else I can help you with?
[t+118s] [CALLER] No.
[t+121s] CLARA Great — have a wonderful day, [CALLER]!
[t+122s] CLARA (no speech — tool turn)
TOOL end_call → {"reason":"[CALLER] confirmed no further questions after tour
was booked.","system__message_to_speak":"Great — have a wonderful day, [CALLER]!"}
[t+124s] CLARA (empty turn — post-tool continuation)
| Thing | The Willows | The bench |
|---|---|---|
| Tour record | Written, confirmed, agrees with what Clara said out loud. | Written, confirmed, naming the exact home from the booking. |
| Calendar entry | Exists on the Willows mailbox at the right time, "In-Person Tour: Riley Chen — The Willows", address correct. | Exists on the bench mailbox at the right time — but the address on it is the company's placeholder office, not the home being toured. See open findings. |
| Email to the property team | Composed and attempted, then stopped one step before sending because the test property's address is a deliberately undeliverable one. An error was logged saying exactly that, 0.6 s after the booking. Nothing reached anyone. | Really sent and delivered, 12 seconds after the booking, to our own sandbox mailbox. The subject reads correctly. |
| Text to the prospect | One text, delivered, to the robot number only — no human inbox exists on it. | Two texts, both delivered — the second is a fragment. See open findings. |
The Willows sent a booking text. Would Camellia? Yes — this is a match, not a difference. A confirmed booking bypasses the "tour request texting" opt-in entirely, so that setting is irrelevant here. Reading both property records strongly-consistent: neither carries any text-message setting of any kind, and neither company has any company-level defaults to inherit from — those records do not exist. Camellia reaches the identical code with identical answers at every gate, and its prospect would get the same message with Camellia's name and address filled in. This corrects the earlier handoff note that said Camellia does not text.
What the same investigation did turn up is a real bug, and it is in the open findings below: the booking text left through Twilio even though the Willows is configured to send through AppFolio. The cause is in the source, not the configuration — the tour text senders never tell the message dispatcher which property the message belongs to, so the dispatcher has nothing to look the door up with and falls back to the direct one. The same mistake was found and fixed on the reply lane on 2026-09-03; the tour lane was missed.
Both bookings were cancelled through the app's own cancel endpoint at 21:24:58Z and 21:25:54Z. Both tour records read cancelled on a strongly-consistent re-read, and both calendar entries were verified gone (the calendar returns "not found" for each). The bench's cancellation email was delivered to our sandbox mailbox; the Willows' was suppressed at the sink address as designed. The bench property's display name was also corrected in the data to "Mesa County Homes", through the app's own rename route — one field changed, everything else on the row verified unchanged, and the cancellation email subject already shows the corrected name.
Neither line sent a cancellation text, and the plan expected the bench to. That is logged below as an open question, not a pass.
The verdict asked for this list with a reason per line. Nine differences, all deliberate, none of them hidden.
| # | Difference | Which line | Why it is there |
|---|---|---|---|
| 1 | The list of available homes comes from the rent roll, where production reads it from the property system's vacancy list. | both | Pointing the Willows back at the production source left it with zero homes to offer — the defect that ruined Thursday morning. Kept and recorded, because it means what a caller is offered can differ for a reason that has nothing to do with the agent. |
| 2 | The bench keeps the confirmations-and-reminders instructions, which Camellia's script does not have. | bench | A scattered-homes line has to tell the caller the address of the specific home, so it answers with the actual text rather than deferring to a person. Camellia has one building the caller already knows; its silence is not a fault on either side. |
| 3 | The bench-codes standing order renders into the prompt on a test line. | both (test lines) | By design: a registered teammate calling a test line hears the bench standing order; a real caller at a real property must see none of that text, which is exactly what the switch enforces. On both proof calls it correctly rendered as empty, because the robot's number is registered as a tester rather than a bench admin. |
| 4 | Inbound lead emails skip the per-property rollout allowlist. | test lines | This is rollout state, not behaviour. The allowlist is the switch that arms automatic email replies to real renters; a property that has been armed behaves identically to the bench. Putting the bench on that same list would erase the one control that tells a bench apart from a customer in the switch that reaches real renters. |
| 5 | Follow-up texts and emails for a promise may go to the caller themselves on a test line only. | test lines | The per-property setting for this was never wired into the function that decides it, so without the test-line leg the caller-facing send is unreachable everywhere and setting the property switch would change nothing. Wiring it properly is its own owner-gated change, because doing it here would turn caller-outbound on at every property carrying that field. Until then: dark everywhere, live on the bench. |
| 6 | A bench's homes are fenced out of a real company's inventory answer. | test lines | A safety fence that protects production from the bench, not the other way round. No caller is on the other side of it. |
| 7 | The many-homes "which home, or which area?" question, and naming homes as homes — street, bedrooms, rent, at most two per answer. | bench only | The whole point of the many-homes line. On a single building it would be a difference; on the Willows proof call it correctly never appeared. |
| 8 | The Spanish-language offer is currently off on both lines. | both | Ruled off for now. A pull request that keys it to company-wide lines is pending — until it lands, this is a deliberate off, not a capability we lack. Note that a separate Willows call at 04:03Z today did render the clause, against that ruling; that is a live lead, not part of these proof calls. |
| 9 | The email to the property team is suppressed on the Willows, where Camellia really sends one. | willows | The Willows' contact address is a deliberately undeliverable test address, so the mail is composed, attempted, and stopped one step before sending. This is the designed test-property behaviour. The bench, which has a real sandbox mailbox, does deliver. |
These are things the calls exposed that would be wrong at a real customer too. None of them is a reason the lines are not a faithful copy; all of them are work.
| # | Finding | Severity | Why it matters |
|---|---|---|---|
| 1 | The calendar entry sends the leasing agent to the wrong address. The tour is for a home in Clifton; the entry's location is the company's placeholder office address in Denver. The tour record carries the full street address and the calendar entry does not use it. | high | On a scattered-homes portfolio this is the difference between arriving at the home and arriving 250 miles away. No grader flagged it. Deserves its own small change. |
| 2 | Tour texts can never take the AppFolio door, at any property. The tour text senders do not pass the property to the message dispatcher, so the carrier lookup is asked about nothing and falls back to Twilio every time. | high | The Willows is configured to send through AppFolio and silently does not. The identical mistake on the reply lane was found and fixed on 2026-09-03; the tour lane — confirmations, cancellations, reminders, household fan-out — was missed. Proven from the source and from two behavioural checks, not from a log line. |
| 3 | One booking produced two texts on the bench, the second reading like a debug string: "Hi, this is Clara — following up like I said I would: 486 32 1/8 Road, Clifton — tour address confirmation". | medium | A real prospect gets two messages for one booking and the second looks broken. |
| 4 | Two aggregate numbers were spoken loosely on the bench. The three-bedroom price band was given as topping out at $2,775 when a $2,825 home is on the list; and "a couple of two-bedrooms" in Grand Junction where exactly one exists. | medium | Both figures were readable straight off the list the call was given. The second is also a banned phrasing — counting what matches in one area and one bedroom size is counting the portfolio. Every individual home, rent and size Clara quoted was exactly right; the defects are in the summaries. |
| 5 | Move-in timing is never asked on the Willows when the caller opens with a price question, so the booking is made without it. Camellia asks it in both shapes we have on record. | shared behaviour | Not a clone difference — the Willows and Camellia render byte-identical prompts and the same tools, so this is the Camellia lane's own behaviour, seen on a test line. It does not hold the Willows row 6. Ruling: orchestrator, 2026-09-11. |
| 6 | An explicit afternoon request was answered with morning slots, and the caller's direct follow-up — "what do you have afternoon?" — went unanswered before noon was chosen for them. | shared behaviour | Not a clone difference, same ruling and same reason — identical prompt, identical tools. A product observation for the Camellia lane, not something the stand-in does differently. |
| 7 | No leasing tool can leave a realness breadcrumb. The booking tool's definition never declares the field the breadcrumb is keyed on, so the stamp runs with nothing and silently writes nothing. | harness | The booking itself was real and verified in the tour record — this is an observability hole, not a product gap. Three live test scenarios assert a breadcrumb that cannot appear, and the unit test that "proves" it works hand-feeds the field the live tool does not send. |
| 8 | The cleanup helper does not scope a cancellation to a property, so when a tester number has tours at two properties the cancel resolves to the wrong one and reports success while cancelling nothing. | medium | Hit live during this cleanup — the first Willows cancel returned "tour not found" with an HTTP 200. Every stress harness that cleans up through this helper leaks calendar slots the same way. One-line fix. |
| 9 | No cancellation text was sent by either line, though the code reads as if cancellations should text at every property. | open question | The bench sends directly through Twilio and produced nothing, so this is not explained by the AppFolio-door bug above. Needs a look. |
| 10 | The scrubber that strips names out of saved real conversations misses a name given as a bare answer — it only catches one arriving inside a self-introduction. | medium | The reference transcript on this page was hand-corrected for exactly that reason before it was used. Anything else built on that corpus carries the same hole. |
| 11 | A bench home in Fruita is vacant, its own listing says "NOW", and it is flagged not-available-for-leasing — so it is silently withheld. Same for one home in Grand Junction. | data | Clara's "nothing in Fruita right now" was correct under the rules, but the seed data contradicts itself, and the scenario written to exercise the named-area path therefore exercises the empty-area path instead. |
| Check | When (UTC) | Result | Where |
|---|---|---|---|
| Clone checklist, the Willows line | 21:02:46 → 21:02:48 | NOT READY — 4 pass, 1 fail, 1 attached, 1 not applicable | final/willows-2026-09-11T21-02-39Z.md |
| Clone checklist, the bench line | 21:02:54 → 21:02:57 | NOT READY — 4 pass, 1 fail, 2 attached | final/bench-2026-09-11T21-02-39Z.md |
| Proof call, the Willows | 21:02:36, 112 s | Booked; no clone difference found; two shared-behaviour observations; audio clean | proof-calls/row6-willows-artefacts.md |
| Proof call, the bench | 21:05:59, 167 s | Booked; findings 1, 3, 4, 11 above; audio clean | proof-calls/row6-bench-artefacts.md |
| Prototype-versus-staging gate | 21:03:02 → 21:06:52 | 3 differ / 30 match / 77 not applicable — hold. Morning baseline was 4 / 29 / 77 (§11.18). Two of the three are faults in the grader itself: one axis reads only the first thing said, and one word-filter misses the phrasing "three-bedroom". One is real — after hours, our line never asks who is calling. | final/cutover-gate/, run on main (PR #7805) |
| Leasing conversation set | 21:06:44 → 21:11:11 | 62 of 66 pass (this afternoon: 61). The four failures are on the production leasing agent — Camellia's own behaviour, not anything changed today: promising to text an application link that is not configured; stating an amenity is absent as established fact instead of "not on file"; refusing an exact count without giving the price band the rule requires in its place; and quoting $1,550 where the scenario expected $1,500. | final/leasing-eval/, run on the subscription with no metered key present |
Everything above was measured against one build of the product: 71b3e92d9327dfecf1bd1536b5cc234da85f5630. The changes in it, in the order they landed today: #7801 (re-gating, 16:27Z), #7807 (guard), #7809 (checklist), #7818 (the dead intake block deleted, 17:35Z), #7823 (tool order), #7831, #7834, #7835, #7842 (replay, test fixture, row-4 split, gate rulings), #7844 (push to staging), #7805 (the prototype gate itself), and #7822 (the area question actually reaches a company-wide line, 20:46Z — the head of this build). PR #7853 and anything after it landed after this measurement and are therefore pending on this page. Every tool was faked in the two harness runs: no webhooks fired, nothing was written, and no phone call was placed by either of them. The two proof calls were the only real calls.
| # | Decision | What it is |
|---|---|---|
| a | Register the bench number +17208071724 for texting. | Texts from this number are unregistered traffic. Some carriers accept them — our own Twilio numbers do, which is why the proof call's text went through — and some refuse them, including Fede's own mobile carrier, which rejected two earlier today. Registering it is a submission to an outside body and therefore Fede's explicit go, never ours. |
| b | Moving Western Slope's 970 line onto the shared company-wide line. | Recommendation: not yet. The three red items at the top of this section are the list. When they are clear, this is Fede's call and nobody else's. |
| c | Close the parked rename pull request #7804. | The name it was going to change has already been corrected in the data, through the app's own rename route, and verified. The pull request has nothing left to do. Closing it needs Fede's word, per the standing rule that we never close a pull request without one. |
Seven independent passes over Fede's own transcripts — six Claude sessions plus the Western Slope kickoff recording — pulled out every word he actually said about what the company-wide phone line asks a caller, in what order, and how it books a visit. This section is the result: his rulings, dated, verbatim, with the ones he reversed shown as both halves. Nothing here is paraphrased. Where a row records something nobody said, it says so.
Times are Denver (MT). "Source" names the session transcript, the kickoff recording, or the document that carries the words. Verbatim means verbatim, typos and dictation garbles included; a fix or note from us is in [brackets]. "Relayed" means another Claude session passed his words across — the quote is his, the wrapper is a teammate's.
| Topic | When (MT) | What Fede said — verbatim | Source | Status |
|---|---|---|---|---|
| Which questions get asked | 2026-09-08 21:04 | "if this phone number is just one property, then we just go straight to, you know, talking about the property and, like, what unit they're interested in and stuff like that. And if if there's multiple listings or properties attached, then go first in… go into the triage more of like, what is your budget and your area and, you know, things like that until we get to a property so then we can book a tour on one property." | Session 002, opening brief | in force as the mode rule. The question list inside it was superseded by 09-09 20:44 and again by 09-11 evening. |
| 2026-09-11 09:16 | "it just adds the portfolio mode that asks about, you know, the area and, like, you know, the new questions. Then everything else is exactly the same. We're not trying to add experiments or trying to do anything crazy here. It's just additional filters, so we can get a home to work booked. That's it." | Session 002; quoted in portfolio-intake-rules.ts on main | in force | |
| 2026-09-11 evening | "we had already done a lot of research and a lot of decisions on the order of questions and what questions." | To the orchestrator | in force | |
| Question order | 2026-09-09 20:29 | "do deep researhch on competitors first and best practices from bigger players. how many quetions at once, order etc" | Session 003-bd | The commission. Delivered; see §3 below. |
| 2026-09-09 20:32 | "ok are you sure u did enough reserach" | Session 003-bd | Rejected pass 1; produced pass 2. | |
| 2026-09-09 20:39 | "move-in date first, then budget, then bedrooms, then area, then pets. i mean my intuition tells me the ordr is right but it might be annyong to answer so many questions one by one before getting to the good stuff" — and in the same turn: "mayb eim wrong" … "well see wha tresearch says" | Session 003-bd | reversed by him 5 minutes later — next row. He pre-authorised the reversal in the same breath. | |
| 2026-09-09 20:44 | "this seems like a first good pass" | Session 003-bd | Accepting the researched order (home/area first → move-in → bedrooms; tour by turn 3–4; pets through the open question; budget never a gate). His own words are "first good pass", not "approved". | |
| 2026-09-09 20:43 | "ship dark" | Session 003-bd, relayed | Shipped dark as PR #7500. | |
| 2026-09-11 08:50 | "so i just called it opened with location first? is tha texpected?" → told yes → "is that what we decided?" | Session 002, after his own live call | challenged — this is what triggered the deletion (#7818). | |
| 2026-09-11 evening | "if you're just looking around to see what we have, then you start trying to narrow it down of when are you looking to move in and what area, budget, bedrooms, etcetera, with the goal of getting them to match to one or maybe two properties." | To the orchestrator | in force — newest. Timing, area, budget and bedrooms are all back in, in that reading order. | |
| Budget | 2026-09-08 21:04 | "what is your budget and your area" | Session 002 | Superseded 09-09 20:44, restored 09-11 evening. |
| 2026-09-09 20:29 (relayed) | "the leasing qualifying order becomes TIMING first ('when are you looking to move in, and are you flexible?'), then BUDGET, then open-ended questions, then homes" | Relayed via 003-bd | Superseded by the next row. | |
| 2026-09-09 20:44 | "this seems like a first good pass" — accepting, among other things, the research's "Never ask on the call: budget as a gate, credit, income, occupants, smoking, reason for moving" | Session 003-bd | reversed by him 2026-09-11 evening — see next row. | |
| 2026-09-11 evening | "…when are you looking to move in and what area, budget, bedrooms, etcetera" | To the orchestrator | in force — newest. Budget is asked again. Both quotes are his; the newer one wins. Note PR #7818 had already landed the same conclusion from a different direction ("Budget stays askable — never forced, never banned") — that sentence is ours, not his. | |
| Move-in timing, and flexibility | 2026-09-09 20:29 (relayed) | "when are you looking to move in, and are you flexible?" | Relayed via 003-bd, off the kickoff | The substance is in force; the wording is the open item below. |
| 2026-09-11 08:51 | "why are we asking if date is firm to move in?" | Session 002, after his own live call | open — a question, never answered by him. The clause was deleted the same day (#7818). The customer asked for the substance (§2). | |
| 2026-09-11 evening | "…when are you looking to move in and what area, budget, bedrooms…" | To the orchestrator | in force — newest. Timing leads the narrowing questions again. | |
| The one open-ended question | 2026-09-09 20:39 | "for example pets could you weave it in more naturally with the example jay said about the back yard and dog" | Session 003-bd | in force — his own mechanism, contributed unprompted. |
| 2026-09-09 20:40 (relayed) | "do not ask pets, yard, etc. as checklist questions. Weave them in through one open-ended question after timing and budget" | Relayed via 003-bd | in force | |
| 2026-09-11 08:55 | "It just said, like, tell me what what you're looking for open ended. Is that expected?" | Session 002 | open — a question, not a ruling. He never answered it. The question was deleted the same day (#7818); the customer asked for it by name (§2). | |
| Bedrooms | 2026-09-09 20:39 | "move-in date first, then budget, then bedrooms, then area, then pets" | Session 003-bd | Superseded as an order, not as a question. |
| 2026-09-11 evening | "…what area, budget, bedrooms, etcetera" | To the orchestrator | in force — newest. Camellia's own "what type are you looking for" already collects it. | |
| Area, and the specific home they saw | 2026-09-05 16:45 | "for leasing we would need to ask what they are looking for, if they are callign about a specific property and be abble to cross sell when for example we dont have a 1bed in one of the small properties but we might have a similar one nearby" | Session 000 — the first statement of the many-homes script | in force |
| 2026-09-11 evening | "So you have to figure out they're calling about a specific property they saw or looking around in general." | To the orchestrator | in force — newest. This is the sort-first step. | |
| Pets | 2026-09-09 20:39 / 20:40 | "for example pets could you weave it in more naturally with the example jay said about the back yard and dog" · "do not ask pets, yard, etc. as checklist questions" | Session 003-bd | in force — pets arrive through the open question, never as a checklist item. |
| Counts of homes | — | No Fede quote exists. The ban on stating how many homes fit comes from our own 2026-09-09 overnight robot run, which reproduced the leak three times ("there are two three-bedrooms on that street", "those are the two that fit"). The nearest thing to a ruling runs the other way, and it is the customer's: Jay, 2026-09-09 — "certainly finding a way to tell Clara, hey, by the way, there's eight of these open." | Session 000 mining (explicitly negative); kickoff recording | open — in force as our rule, unruled by him, and contradicted by the customer's own words. Worth putting to him. |
| One question per turn | 2026-09-09 (kickoff) | "we asked some, like, qualifying questions about, like, oh, what's your budget, you know, neighborhood, is there anything… like, that was kind of, like, a first shot, like, I think we can make it better. I feel like it asked, like, too slow, like, one at a time." | Kickoff recording, raw transcript | in force — combined asks allowed. This is the one place he overrides the research's only unanimous finding, deliberately. |
| 2026-09-09 20:39 | "it might be annyong to answer so many questions one by one before getting to the good stuff" | Session 003-bd | Same rule, said twice, five days apart. | |
| Tours — one home, or two | 2026-09-03 11:35 | "I would imagine you can still offer a tour of a different unit with the same bedroom count just so they can kinda go see and get the value of the building and the general sense of the apartment as one option… So thing our goal should be to still get them in to see something and speak to a person." — scoped down the next day (09-03 22:10): "i dont need the full wailist implementatoin yet. just handling tours for units that are not aville for physical showing yet" | Session 006 (Western Slope lane) | in force, turned on at the Camellia and Yale lines 2026-09-04 ("turn on"). The same instinct governs the portfolio line: get them in front of something. |
| 2026-09-10 22:18 | "I know previously we had some failures where we couldn't book a tour because it requires a property… if we narrow it down to one property for a tour, that might work. But what if… or maybe we don't allow this, but what if… I think there was an example where the customer said, oh, can I see two properties that are, like, right next to each other. So now that's two. So how would we evolve our tools for that kind of stuff? Without breaking camellia or over engineering." | Session 002 | Raising the problem, not ruling. | |
| 2026-09-10 22:24 | "we talked about in the meeting a minimum of an hour heads up. So that's a setting configurable. Uh, an hour default. And then we would need to think about, for example, if I'm seeing two townhomes or if I'm showing more than one, maybe the tour duration is longer… it could be like, oh, if it's one, it's fifteen minutes. If it's two, maybe thirty." | Session 002 (queue records) | Design/investigation only, by his own instruction. One-hour minimum notice is already the per-property default. | |
| 2026-09-11 evening | "In some cases, if they wanna see multiple properties, we should allow for that, but it gets complicated" (same street → one 30-minute tour; different parts of town → harder) · "maybe either we don't allow that or it's separate tours, but we really have to think about deeply how that's gonna work and get feedback from the customer. What is the simplest thing to start with?" | To the orchestrator | in force as the question. The orchestrator answered "one tour books one home; if they want two, book the first and the team adds the second at the visit", and Fede did not object. [INFERENCE] — silence is not approval; it is the working default until he says otherwise. | |
| The Spanish offer, and language switching | 2026-09-11 08:42 | "i do see some side effects leaking here (just note them) like asking for spanish say espanol" | Session 002 | reversed by him 23 minutes later — next rows. |
| 2026-09-11 09:05 / 09:27 | "this is something requested for the portfoloio greeing though and i think its good" … "you are not understanding the problem. that greeting is fine" | Session 002 — the last message of that transcript | in force. Keep the Spanish offer in the company-wide greeting. Both quotes are his; the later one wins. | |
| 2026-09-11 09:14 | "No. You don't understand. I never said Spanish. at all. And then I call back, and it's Clara. She's speaking English, but it's obviously the Spanish agent because it has an accent. So the language switch just needs a lot more testing and inspection. It should be… someone says Spanish. Language switch. Then we switch. Or if they if they start speaking Spanish, we should detect that and switch. But it shouldn't be, like, accidental." | Session 002 | in force. The bug is the accidental switch, never the offer. He rejected two invented explanations for it — "no thats not right" / "you made it up" (08:43/08:44) and "no thats not it" (09:13). | |
| Camellia and Yale parity | 2026-09-06 14:48 | "And context for that decision is always being asked. We wanna clone one agent per property, so we wanna have, like, an agent for centralized mode versus property mode, and I said, no. I wanna have one agent and just be able to switch it at one time with whatever adjustments seems." | Session 006 (2026-09-08 lane) | in force. One agent, switched by what the number points at — never a clone per property. Restated 2026-09-11 evening as "one agent for production for triage, one agent for leasing". |
| 2026-09-09 20:13 | "i guess one way is to replicate camellia exactly, with the additions of the portolio level leasing agent. then test that both in protfolio mode adn replay8ing all camellias phone calls. so we [prove] that works. then that becomes the new prod agent" | Session 003-bd | softened by him 2026-09-11 evening — "exactly" becomes "core". | |
| 2026-09-11 09:16 | "the goal is production ready clone that works just like Cam[ellia] with no experimental features, no weirdness… Then everything else is exactly the same… and you cannot break the [Yale]… Yale station. Yale." | Session 002 | in force on "no experimental features" and on not breaking Yale. | |
| 2026-09-11 evening | "I didn't say exactly like Camellia. I said, like, Camellia has most of the core of what we need. Then there's additional things we gotta do to filter out for a portfolio because they're not calling one property." · "The goal is one agent for production for triage, one agent for leasing, and that agent needs to work like it does today with Camellia and Yale 25 Station where they call one property, and it needs to work with a scattered portfolio with a centralized team that has one number for could be hundreds of properties." | To the orchestrator | in force — newest. Camellia is the core, not a byte-for-byte ceiling. Additional portfolio filters are expected, not scope creep. | |
| The 970 cutover | 2026-09-09 20:12 | "well we will worry about it later when we are ready. we got lots of testing to do" — then, same minute: "but eventually we merge right" … "wait" | Session 003-bd | in force — deferred. Decisions 5 and 6 deferred with it. |
| 2026-09-11 09:16 | "once we achieve this, we're gonna hook it up to the real phone line that we did for the customer. Right? So we're gonna deprecate or delete that prototype we had, and then we're gonna send it to the customer over the weekend and be like, hey. This is ready for testing for real." | Session 002 | in force — the sequence, still gated on the clone being right. Deadline in his words: "we have a few days to go live" (09-10 10:20) and "we need to test this next week with the client" (09-10 22:13). | |
| The settings freeze | 2026-09-09 09:18 | "what swtihches exists? after this is 100% working i dont want flags or switches per property this is globa[l]" | Session 010 | in force. Same direction from a different angle: the question order is one global behaviour, not a per-property flag. |
| 2026-09-09 20:29 | "if what hey want is different we coudl make it a setting" | Session 003-bd | reversed by him 16 minutes later — next row. | |
| 2026-09-09 20:45 | "no dont do seetings yet" … "onl yif a client asks for a didferent order later" | Session 003-bd | in force. No new setting, registry key or per-property field for the question order. Context, same evening: "we want to do a full pass at refactoring settings as part of this onboarding" (20:26) and, on the inventory of ~200 settings, "holy shit. we are going ot need a separate document to come up with a strategy to cleanup" (20:40). |
Verbatim from the raw per-speaker transcript in #transcripts (thread 1788995366.021449), not from the recap. Speaker label is the recording's: "JasonFish" is Jay.
| # | Jay, verbatim | What it asks for |
|---|---|---|
| 1 | "I definitely want to understand early in the process when they're looking to move in." | Move-in timing, early. |
| 2 | "and if they're flexible, because if somebody's like, I need to move in by September 15th and I'm not flexible, then, you know, as far as Ember Estates is concerned… there's, like, one or two options that we can put them into. At other places, there's a lot more" | Flexibility on the date, in the same breath as the date — and his reason for it. |
| 3 | "Yeah, it's like timing. is almost a leading question before price." · "It's almost just, like, timing and then price." · "yeah, I think timing is… is definitely, uh… you know, might… yeah, might be your front-running question." | Timing first, then price. |
| 4 | "questions that I know our leasing manager is asking early on is, what's your budget? What's your timing? Um, you know, tell me about what you're looking for. I encourage her to ask as many open-ended questions as possible, and maybe that works here, too." | Budget is asked by his own leasing manager; and one open-ended question. |
| 5 | "So if somebody has a dog and needs a yard and a 6-foot fence so they don't jump out, like, they'll tell you" | Pets and yard arrive on their own, through the open question. Never a checklist. |
| 6 | "if it digests, like, you know, somebody spewing for 5 minutes about their whole story, like, first of all, it saves me having to do it, and B, like, there's a lot of useful information in there." | A rich guest card is the point of listening. |
| 7 | "we're not gonna put all 8 on there… An end unit, a middle unit, and like a four bedroom instead of a three bedroom. So maybe we have three on there and we're trying to funnel all the leads through and Certainly finding a way to tell Clara, hey, by the way, there's eight of these open. But I mean, if the goal is to get to a showing and provide some basic unit or basic property information." | He wants Clara to know the depth of inventory. He never asks her to hide the count — and never asks her to say it either. |
| 8 | "it's gonna say, hey, you're gonna meet Kat, our leasing agent, over at the property at this time. Like, little things like that" | The confirmation names who meets them. |
| 9 | "I scheduled two showings at Em… one at 10 and one at 11, and I… I assumed that it's assuming drive time, but those are… those are next door to each other, you know what I mean? So there's obviously some logic that we can put in there, where, like, yeah, I need, like, 15-20 minutes in one, and then we'll walk" | Two homes next door to each other are one trip, not two hours. Sean, same call: "we don't want to accept tours less than… without less than an hour's notice." |
| 10 | "How would that flow? It books it, we're gonna get an Outlook notification, but for that kind of timing, is it gonna send anything, like, slightly more urgent than a calendar notification?" | A booked tour needs a louder alert than a calendar invite. Still open. |
And Fede, on the same call, on what the whole script is for: "The goal is get a showing with a person, right? Like, don't give objections, just give the information, just, hey, can I book a tour? You know, keep pushing."
Verbatim from qualifying-research-2.md §(d), the pass Fede accepted with "this seems like a first good pass". Pass 1 is explicitly superseded by pass 2's own words: "Pass 1's 'budget second' is contradicted by all primary evidence."
| Do | Confidence | Why — the research's own words |
|---|---|---|
| "Ask exactly three before offering a tour: which home/area → move-in date → bedrooms" | High on move-in date + bedrooms; Medium on home/area first | "High on move-in date + bedrooms (6 and 4 sources; EliseAI's shipped required-set is these two). Medium on home/area first — no scattered-site source exists; it is the disambiguation single-site vendors never need" · "Fewest questions that can actually match a home" |
| "Offer the tour by turn 3–4" | Medium | "Shipped-vendor consensus is early (EliseAI, Thoughtly, PERQ); both rubrics say late… But the one operator with 7 months of data says early-tour over-books unqualified prospects" |
| "Offer in-person and self-guided/virtual in the same breath" | High | "Strongest causal datapoint found: LeaseHawk's 150M-call analysis traced a client's low tour-booking to a missing virtual option, not to weak qualification" |
| "One question per turn" | High | "5/5 verified transcripts" · "One question per turn: 5 of 5 verified transcripts, unanimous. No shipped agent bundled." |
| "Capture name + phone early; don't gate the tour on email" | Medium | "4 sources early vs 2 late; Thoughtly gating on spelling out an email is the clunkiest moment in any transcript" |
| "Ask pets — it is genuinely per-home in SFR" | High | "4 sources; only per-property gate the rubrics and the AI agents agree on" |
| "Never ask on the call: budget as a gate, credit, income, occupants, smoking, reason for moving" | High | "EliseAI omits budget entirely; NAA says price only if asked; the Excel demo puts credit and income on the form, never in conversation" |
| "State it's an AI, and always offer a human" | High | "The only renter complaint pattern in the evidence — never question count" |
Its consensus-order source counts, verbatim: "Move-in date — 6 sources"; "Bedrooms / layout — 4"; "Name, then phone — 4 early… 2 sources put it last"; "Specific needs / preferences — 5"; "Pets — 4"; "Lead source — 3… Rubric-only artifact"; "Price / budget — 4, but always LATE, and NAA says quote price only if the caller asks; EliseAI does not require it at all"; "Occupants — 2… Weakest"; "Reason for moving — 3… Rubrics grade it; leasing AI never asks it."
Its central finding, verbatim: "The central conflict: rubrics close for the tour at item 13 of 20 (ACE) and 13 of 15 (EPMS) — after full qualification and amenity-selling. Every verified AI agent inverts this and offers the tour within 2–3 turns on ≤2 qualifiers."
Sources it actually read. Five full call transcripts: EliseAI (a 7-month operator review, 1,600 leases, plus support.meetelise.com docs read through a search index — direct fetch 403s); Thoughtly (live demo call); Excel/Accelerate AI (demo call); a realtor agent's three live calls. Three published mystery-shop rubrics: LeaseHawk/Fenix ACE Agent Performance Survey (20 items / 100 pts), EPMS telephone shop (15 items / 100 pts), NAA NALP "Telephone Presentations". Plus LeaseHawk's conference talk on 150M calls / 100k scored agents, and renter-side accounts (r/Apartmentliving, a Yahoo Finance piece). Absences it proved: AppFolio Lisa (18 support articles read, no question set documented anywhere), Knock, Entrata ELI — nothing found. Struck from the competitive set: Colleen AI is defunct (folded into Entrata); Hyro is a healthcare vendor, not multifamily. Never called: the two public demo lines, EliseAI (888) 315-2945 and LeaseHawk/Fenix 844-389-4060.
| # | Rule, in his words | The incident that produced it |
|---|---|---|
| 1 | Plain English. "Can you tell me in plain English? I'm confused about how you're explaining" (2026-09-10 08:20) · "plain english how do i ask him about this part of the architecture" (09-10 15:35) · "plain anglish" / "i dnt understand anything" (08-30 16:17) | A status update that used the internal phrase "bench row" without ever saying what it was — his next message was "And what is the bench roll? What does that mean?" Earlier, an incident write-up delivered in engineering register. |
| 2 | Don't block on me for something you can do. "why are you wainting on me" (09-09 21:56) · "i dont merge click" (09-04 13:22) · "everything should be autonomous. There's no buttons. No no one's gonna go click merge. Like, that doesn't exist in our system." (09-08 21:10) · "why jus tdo it" / "can you modify the rule that prevented that?" (09-10 08:21) · "No. I don't understand why to create a test [row] I need to run a terminal command[.] [I] think something's wrong in the permissions or the setup. you should be able to do that." (09-10 08:22) | A session posted "Blocked on you: creating the bench property's row in production — reply with PROPFLOW_CONFIRM_PROD_WRITE=portfolio-bench" and stopped the whole night's work waiting for him to paste one environment variable, to create a test row. |
| 3 | "Ready" means proven before I touch it. "im not a manual qa person" (09-08 21:49) · "you own testing and quality" (09-03 13:05) · "did you write evals and multi turn pro[mptfoo] tests?" (09-10 14:56) · "i also saw no test results, evals, if we regressed, if we replayed past conversatiojns, how we dtermined that its 'prod ready' before i manually made test calls and found a bunch of obvious issues" (09-11 09:22) · "seeems like we ran no quality ch[e]cks" (09-11 09:22) | An overnight run reported a "production ready clone". His own first test calls found dark test-only features on, empty inventory, wrong office hours, his number stored as a resident, and a Clara that did not know today's date. |
| 4 | Never invent — not a mechanism, not a scenario. "no thats not right" / "you made it up" (09-11 08:43/08:44) · "no thats not it" (09-11 09:13) · "I dont want you to test and inveent scenarios we didnt ask for." (09-10 10:17) | The session invented an ElevenLabs language-preset mechanism to explain the Spanish greeting rather than checking, then invented an accent-bleed theory when that was rejected. Separately, it had reported a made-up same-phone/different-name case as a design defect. |
| 5 | Go check how it works today; never answer from a note. "go c[h]eck how it works today" · "how does it work today" (09-10 10:19/10:20) · "rule is go check for claims like that for latest" (09-09 18:52) · "i hate that you always weave live notes from transcripts and not check if that was updated" | A session flagged "Western Slope is on AppFolio's Core plan, API access requires Plus" from a stale transcript note. They had upgraded that same day. His reply: "STOP FLAGGINT SHIT LIEK THIS I KNOW" · "THEY UPGRADED TODAY" · "UGH". |
| 6 | One thing at a time. No dumps. "you are dumping too much info" / "lets go area by area" (09-09 20:19) · "lets go one by one" (09-10 11:59) · "decision one by oone plain english" (09-09 20:07) | All seven Western Slope kickoff items were delivered in one block — including the one-hour tour minimum, the "you'll meet Kat" line and the urgent-alert ask. He cut the dump off and never ruled on those three items, which is why they are still open. |
| 7 | One doc per initiative. "what diagrams should i lookat theres a lot" (09-10 15:22) · "why do we have 4 docs?" / "i want a single doc" (08-30) · "after all this research, i want a single doc for everything." (09-05) | Four separate voicemail documents for one feature; then a proliferation of architecture diagrams he could not navigate. |
| 8 | Don't add what I didn't ask for, and don't over-engineer. "why are you adding stuff we never asked for or thigns production doestn have" (09-05 21:24) · "do another full pas remove all the fluff and shit you addded without my authoriation" (09-05 21:25) · "Without breaking camellia or over engineering" (09-10 22:18) · "were not tr[y]ing to add scope here" (09-10 10:20) | The prototype prompt had grown read-backs, "penciled in", a team-confirmation dance and caller-ID questions nobody asked for. His one carve-out, same night: "you added goos stuff to triage like budget, location, specific property, cross sell protofilio thats all good." |
| 9 | An independent agent grades the work, never the session that did it. "here's my owner's read, though. I need you to spin up a completely separate fable, unbiased, to inspect you with a fleet and some judges. And then I want the ver[dict]… from them. … you don't… not gonna test yourself." (09-11 09:14) · "you are paused for now" (09-11 09:14) · "What went wrong in your session? And you do retrospective on yourself with a separate fable agent?" (09-11 09:13) | The same overnight session that declared the clone production-ready was the one reporting on it. |
| 10 | Read the raw transcript, and don't re-research what is already researched. "read the raw transcript you missedthings" (09-09 20:23) · "we had already researched how ai receptionist compeittors do missed calls and wrote a doc" (08-30 15:47) · "We had done research and documented how the industry does it. Did you ignore that?" (09-11 evening) | A kickoff summary that dropped decisions; a session about to redo research that already existed; and, tonight, the qualifying-question research being re-litigated instead of read. |
Two more that govern everything above, and are not negotiable: the three-step rollout — "shipping to prod off, testing at willows, and only then turning on to customers is a separate step with my approval only" (2026-08-30 19:39, after missed-call changes reached a live customer) — and the settings freeze, "no dont do seetings yet" (2026-09-09 20:45).
It is one shape, said in different words for twelve days. Something arrived that he had not asked for and could not see the reason for — "why are you adding stuff we never asked for or thigns production doestn have", "i ddint ask for anyo fthis", "theres a lot of things here i didnt ask for", "this is half baked", "this seems half baked?", "no this is over engineering and not focused on the problem i want solved" — or it arrived at a customer without him — "we cant be making chagnes in production without my approval or testing", "but why did you change production without asking me" — or it was explained in a register he could not read — "plain anglish", "i dnt understand anything", "Can you tell me in plain English?", "what diagrams should i lookat theres a lot" — or it was called finished when it was not — "i also saw no test results, evals, if we regressed", "seeems like we ran no quality ch[e]cks", "why is it idle its not done", "tell it do fully finish dont be lazy" — or it stopped and waited on him for something he had already delegated — "why are you wainting on me", "why jus tdo it", "So now I'm… I don't understand why you're kinda, like, pausing and keep asking for permissions and all this not done stuff. I don't know what… like, what is going on". And when several of those land in one morning, it becomes the sentence that ended 2026-09-11: "Okay. I think we need to pump the brakes here. there's too many issues and confused. Can you just take a step back and figure out why there's so many bugs, and it's so confusing, and it's… like, stuff is off here, on here. It's just a mess. What is going on?" — followed by "because the overnight goal was a production ready clone. Right? And I would just do the final testing. And then I will share with the client. But instead, I'm finding issue after issue after issue."
Provenance. Every quote above is verbatim from one of: the six mined session transcripts (agents/000, agents/002, agents/003-bd, agents/006 ×2, agents/010), the raw per-speaker Western Slope kickoff recording in #transcripts, qualifying-research-2.md, or Fede's own words to the orchestrator on the evening of 2026-09-11. Two rows carry an explicit [INFERENCE] or "no Fede quote exists" label; nothing else on this page is inferred. Recorded so nobody mines them again: three of the seven passes — the two agents/006 transcripts and agents/010 — are grep-verified negative for the qualifying-question set, the question order, "is that date firm", the open-needs question, counts, one-question-per-turn, the tour-booking rules, the Spanish clause and the 970 cutover. In those three files the 970 line, the September 17 date and Kat by name appear only inside cross-session relays, never in a word Fede typed. What they do carry is parity, tours-of-unavailable-units, the per-property-switch ban, and most of the process rules above. The build specification these rulings resolve into is at scratchpad/fede-context/BUILD-SPEC.md in the session that published this section.
Western Slope’s phone number stopped ringing its own private agent today and started ringing the same Clara every other customer gets. The first fifteen live calls said she describes the homes well and cannot book one properly. Sixteen more, after the fixes, say she is ready to dial. Alongside it, a test company of our own now exists, so rehearsals stop happening on a customer’s account.
One number, one shared brain. The old one-off agent stays parked as the way back.
Proves: The cutover ledger, the vendor's read-back of the line, and today's memory measurement.
Ready to dial. One thing still reaches the team unnamed.
Proves: Sixteen placed calls on the live line, each answer read against the home’s own published record; the booking checked on the record, the calendar and the text.
Built for every call, sent for every call, thrown away on every call.
Proves: A read of the live production script, plus a new check that the script declares the slot.
The old shape loses four homes before a caller ever gets an answer.
Proves: The customer's real page parsed by the production parser against their real building rows. Both pieces are dark.
Nothing was bought. The bench phone line has not moved yet — that needs Fede's word.
Proves: A build report of ids, links and 51 headless screenshots, plus a cold read straight off production.
Nothing here is blocked on engineering. Two writes need his own session.
Proves: The dry-run output of both writes, and the safety hook's own refusal message.
Not a regression — the original design, with one hole at the end of it.
Proves: The tour code's commit history read back to March, plus three open defect lists already carrying it.
91 in total: 72 merged, 10 still open, 9 closed without merging. Newest first.
| PR | Title | State |
|---|---|---|
| 8031 | Route review-verdict-watchdog pages to #agent-smith by default | open |
| 8029 | Grade the visit rules that exist, not the ones the block deleted | merged |
| 8027 | Type-check job runs on the 32 GB pool (tsgo outgrew the 8 GB runner) | open |
| 8026 | A test line hears the company's homes when production does | merged |
| 8025 | Triage's leasing handoff line is the stall the many-homes block bans | merged |
| 8024 | The deposit comes off the home's own line, never off a formula | open |
| 8023 | The team's calendar entry points at the home, not the office | merged |
| 8022 | Classify the company-homes cache gate, which main is red on | closed |
| 8021 | Classify the company-listings cache refresh as safety-only (main is red) | merged |
| 8020 | The many-homes rules never reached the production Leasing agent | open |
| 8019 | Dial the Western Slope line, not the agent it used to ring | merged |
| 8018 | Read a rent said as "twenty-three fifty" as $2,350 | merged |
| 8016 | Grade the deposit against the home the caller asked about | merged |
| 8015 | A deliberate act for retiring the old many-homes bench | open |
| 8014 | The many-homes bench line answers for the test company now | open |
| 8013 | The new many-homes bench can accept its own robot callers | merged |
| 8012 | Everything that names the many-homes bench names Fairhaven | open |
| 8011 | The many-homes voice bench runs on our own homes, not a customer's | open |
| 8009 | A scattered line names a home or books nothing | open |
| 8007 | A company connect never silently makes a person's inbox the leasing mailbox | merged |
| 8000 | A tour can be booked on a company-wide line | merged |
| 7990 | The company's homes invalidate the call-start cache when they change | merged |
| 7987 | The mid-call tools agree with the call-start block | open |
| 7982 | Host our own test listings page, so no bench reads a customer's | merged |
| 7980 | A centralized test company can have a scattered-homes property | merged |
| 7979 | A named writer for a company's listings page | merged |
| 7978 | feat(onboarding-import): stage 4 — renewals & maintenance | merged |
| 7977 | feat(onboarding-import): stage 4 — renewals & maintenance | closed |
| 7976 | A company-wide line's homes block is the company's own list | merged |
| 7975 | Onboarding harness: send the operating model the route now requires | merged |
| 7974 | Read a company's listings page into the company's own rows (no building) | merged |
| 7973 | An org-scoped listings feed fills no building, and refuses by name | merged |
| 7972 | A company's public listings, stored at the company (store only, dark) | merged |
| 7971 | pm-action-reminder: skip sandbox (not-live) organizations | merged |
| 7970 | claude-code-review: cool down the 'GitHub reviewer DOWN' page | merged |
| 7969 | alerts channel hygiene: stale-mute report moves to #agent-smith, posts only on change | merged |
| 7968 | alerts channel hygiene: fix goldmine-live-scorecard manifest drift (#alerts is not a scorecard copy) | merged |
| 7967 | feat(onboarding-import): stage 3 — leasing pipeline | merged |
| 7966 | appfolio-sync: declare cadence classes (dark, PR 1 of 6) | merged |
| 7965 | AppFolio sync schedules: declared per-job state survives deploys (all ENABLED, no change) | merged |
| 7963 | A guarded writer for a property's dial-out desk phones | merged |
| 7962 | Add the Western Slope interim front-door provisioning script (writes nothing yet) | merged |
| 7960 | Sync handler tests: stop the onboarding-import sweep escaping to DynamoDB | merged |
| 7958 | Balances job: wire the portfolio-wide fetch into the handler (dark, per-account arm) | merged |
| 7957 | PMS onboarding import: stage 2 (People & leases) writers (B of 3) | merged |
| 7954 | fix(cotenant-phantom-mint-drift): raise co_tenant ceiling 4 -> 6 — false-paged on a real 6-person lease | merged |
| 7953 | Balances job: portfolio-wide fetch helper (4 reports, one call per account) | merged |
| 7952 | PMS onboarding import: sweep + stale-stage reclaim (A2 of 3) | merged |
| 7951 | Close the three gaps the cutover gate found on the shipped block | merged |
| 7949 | Lease states job: wire the portfolio-wide fetch into the handler (dark, per-account arm) | merged |
| 7948 | PMS onboarding import: stage runner core + arm + stage 1 (A1 of 3) | merged |
| 7947 | Lease states portfolio-wide fetch: helper + arm config (dark, unwired) | merged |
| 7945 | Rental applications job: one portfolio-wide call per account instead of per property (dark, per-account arm) | merged |
| 7944 | fix(alarm): PM-action no-recipient alert no longer mints one Sentry issue per household | merged |
| 7943 | PMS onboarding import: stage 2 (People & leases), stacked on stage 1 | closed |
| 7940 | PMS onboarding import: stage-chain skeleton + stage 1 (units) (dark) | closed |
| 7935 | Guest cards job: one portfolio-wide call per account instead of per property (dark, per-account arm) | merged |
| 7934 | Lease states job: one portfolio-wide call per account instead of per property (dark, per-account arm) | closed |
| 7933 | Renewals job: one portfolio-wide call per account instead of per property (dark, per-account arm) | merged |
| 7932 | PMS onboarding import: stage runner with stages 1-2 on portfolio-wide calls (dark) | closed |
| 7929 | Listing precision (3/3): the runner | merged |
| 7928 | Stop restating the inbound-SMS rule in prose; point at the verdict union | merged |
| 7925 | Listing precision (1/3): the fact sheet | closed |
| 7924 | AppFolio client: every request draws from the per-account budget (armed off by default) | merged |
| 7923 | Occupancies job: one portfolio-wide call per account instead of per property (dark, per-account arm) | merged |
| 7922 | Portfolio leasing rules key on a company characteristic, not a bench-id list | merged |
| 7921 | Never hand a caller a phone number that rings Clara | merged |
| 7920 | Catch a phone line whose inbound texts reach nothing | merged |
| 7919 | Cut the Western Slope 970 line over to the production Triage — agent binding only | merged |
| 7918 | Listings feed: configurable at the COMPANY, resolved nearest-first, dark | merged |
| 7917 | Clara's call-start homes block carries the whole listing card | merged |
| 7916 | Listing precision (3/3): the runner | closed |
| 7915 | PMS onboarding import: everyday-sync gate on import completion (no callers) | merged |
| 7914 | Listings sync follows each card's link and stores the full card | merged |
| 7913 | Listing precision (2/3): the grader | merged |
| 7912 | Read a public listing's detail page: full description, deposit, amenities (parser only) | merged |
| 7911 | AppFolio: distributed per-account request budget in DynamoDB (no callers) | merged |
| 7910 | The company-wide line asks the questions the team actually decided on | merged |
| 7909 | PMS sync pause: sweeps and health alarms respect the pause (dark) | merged |
| 7908 | AppFolio client: portfolio-wide report walker and per-account request budget (no callers) | merged |
| 7907 | PMS onboarding import: durable import record with stages, checkpoints and progress (data model only) | merged |
| 7904 | The Spanish offer stays on company-wide greetings only | merged |
| 7901 | The live bench proof grades the rules that exist, not the deleted ones | closed |
| 7899 | Pause one customer's AppFolio sync with a durable per-organization switch (dark) | merged |
| 7894 | Property type: store PropFlow's vocabulary, never the PMS's raw words | merged |
| 7887 | Properties: the Vacant column was the fourth unit-derived number | merged |
| 7886 | Building picker: judge on the PMS's own unit count, not a name-join | merged |
| 7884 | Rents card: gate on the unit count, so a measured $0 survives | merged |
| 7882 | Dashboard: nothing in scope reads as nothing measured, not as a vacant book | merged |
| 7881 | Let staff remove one building from a customer, for good | merged |
| 7880 | Onboarding picker: start non-buildings unchecked, under their own heading | merged |
One goal on this line: match the caller to a home we have, and book the tour. Nothing else matters.
Sort the caller, fork on timing, then hand the call to the core.
Proves: The raw per-speaker recordings with the client (Sep 4 and Sep 9), the 2026-09-09 industry research, and Fede’s own words of 2026-09-12. Full reasoning: scratchpad/many-homes-mode/REASONING.md.
Five calls to place on the Western Slope line, in order. Each one shows a different thing Clara now does. Every home, rent, deposit and fee below is live in that account today.
He named it, so nothing gets asked twice.
Point at: she never asks what he already answered, and the $32 fee she used to not know is now in her head.
Proves: 367 Ember Lane is live in the account today with that rent, deposit, fee and special.
A firm date and one area question leave exactly two homes.
Point at: two homes and two rents came back in one breath, and the firm date quietly kept the $2,800 house out of the conversation.
Proves: both Clifton homes are open in the account today at those rents and deposits.
Not urgent buys him the unlisted home, and a tour anyway.
Point at: the caller in no hurry hears a home nobody else can show him, and still lands on the calendar.
Proves: the account holds 15 cards today — 14 open, one pre-leasing (374 Ember Lane), which the block excludes.
One extra question, chosen to split the list, picks the home.
Point at: the dog and the fence are asked once, and her added question chose the home.
Proves: Placer’s yard, garage and pet policy are on its live card; its deposit genuinely is not.
A local answer; asking for a human doesn’t end the call.
Point at: she answers the town like a local, and “let me talk to a human” still ends in a booked tour.
Proves: the Redcliff card carries that rent, deposit and fee; the neighborhood lines come from sampled notes.
One text to the caller, one email to the team, both written from the reader’s side.
Proves: the text is the shipped template. The email rewrite is in flight, not shipped — open a real one before promising its exact shape on a call.
If one of these comes up on the call, say it is coming and move on.
Proves: each is a stated 2026-09-12 ruling or an explicit “we’re not building it now”.
What the demo-er says, and what Clara should do with it. Homes, rents and fees are today’s live cards.
| Call | The demo-er says | Clara |
|---|---|---|
| 1 | (dials) | “Thanks for calling Western Slope Property Management, this is Clara. Are you calling about leasing, maintenance, or something else?” |
| 1 | “Leasing. I drove past a new place on Ember Lane with your sign out front — is that one still available?” | “367 Ember Lane — yes, that one’s open. Three bedrooms, two baths, $2,750 a month. Brand new, attached garage, private fenced yard out back — and right now there’s a grand-opening special: six weeks free rent, with water, sewer, trash and gas included your first year. Anything in particular you’d like to know?” |
| 1 | “What do I need up front?” | “Deposit is $2,750, and there’s a $32 background check fee with the application.” |
| 1 | “I’d like to see it.” | “Easy. Do mornings or afternoons suit you better?” → “I’ve got Thursday at 10, or Friday at 9 — either work?” → books, reads it back, takes his name. |
| 2 | “Leasing. I’m just seeing what you have open right now.” | “Happy to help. When are you looking to move?” |
| 2 | “End of the month — and I can’t really move that.” | “Got it, so let’s stick to what’s open today. Anywhere in particular, or are you open to a few areas?” |
| 2 | “Out toward Clifton if you’ve got anything.” | “Two in Clifton. 486 32 1/8 Road is a two-bed, one-bath at $1,250 a month, and 460 32 1/8 Road is a two-bed, one-and-a-half with a fenced backyard at $1,350. Both open now.” |
| 2 | “Tell me about the cheaper one.” | “Upstairs condo, about 890 square feet. New flooring, remodeled bath, new appliances, and a great view of Mt. Garfield and the Bookcliffs. Water, sewer and trash are included, and it has washer-dryer hookups. Anything in particular you’d like to know?” |
| 2 | “What’s the deposit?” → “Can I come see it?” | “$1,200.” → “Of course. Mornings or afternoons?” → “Tuesday at 4:30, or Wednesday at 4?” → books, reads it back, takes her name. |
| 3 | “Leasing. I’m starting to look — probably end of October.” | “Nice, you’ve got some runway. Any part of town in mind?” |
| 3 | “Grand Junction, near town ideally.” | “Then the one to know about is 374 Ember Lane — brand-new duplex, three bed, two bath, about 1,360 square feet, $2,775. It’s pre-leasing right now, which is almost exactly your timing. Want me to put you down for it?” |
| 3 | “Yeah. Can I see it?” | “That one’s not ready to walk yet. Its twin two doors down, 367 Ember Lane, is open now — same community, same floor plan — so you can walk that one this week and see exactly what you’d be getting. Mornings or afternoons?” → “Thursday at 2, or Saturday at 1?” → books. |
| 4 | “Leasing. Looking for a three-bedroom in Grand Junction, around two thousand.” | “Got it. When are you hoping to move?” → “And any particular part of town, or open to a few areas?” |
| 4 | “Next month, I’m flexible.” → “Anywhere in Grand Junction, really.” | “A few could work then. Do you have pets?” |
| 4 | “A dog.” | “Are you after a yard for him, or is a garage more the thing?” |
| 4 | “A yard — fenced. He jumps.” | “Then 566 Placer Street is your best fit: three bed, two and a half bath, about 1,342 square feet in Fruitvale, $2,100 a month. Recently updated, two-car garage, and the backyard is fully fenced. The other three-bedroom in your range is a townhome on Eisenhauer at $2,495 — garage and a balcony, but no yard, so I’d put Placer first. Anything in particular you’d like to know?” |
| 4 | “What’s the deposit on Placer?” | “That one’s not published on the listing, so I don’t want to guess — I’ll get it to the team and they’ll confirm. Want to go see it? Mornings or afternoons?” → “Wednesday at 9:30, or Thursday at 11?” → books. |
| 5 | “Leasing. I’m looking at the Ridgway one, the two-bedroom.” | “575 Redcliff Circle, unit 204 — two bed, two bath, about 950 square feet, $2,300 a month. Freshly renovated top to bottom, balcony, carport, and water, sewer and trash are included. Anything in particular you’d like to know?” |
| 5 | “Honestly — what’s around there? I don’t know the town.” | “It’s a small mountain town and you’d be right in it. Downtown Ridgway is walking distance — shops, restaurants. Ridgway Elementary is in town. Ridgway State Park is right there with roughly fifteen miles of trails, and the town trail system adds a lot more. Grocery’s downtown too. And you’re at the 550/62 junction, so Montrose, Ouray and Telluride are an easy drive.” |
| 5 | “Huh. Is there someone I could talk to?” | “Sure — tell me what you’d like to ask and I’ll write it down for the team. What’s your name?” |
| 5 | “Marcus. I want to know about the parking in winter.” | “Marcus, winter parking — noted, that’s going to the team. In the meantime, want me to get you in to see it? Mornings or afternoons?” → “Saturday at 11, or Sunday at 10?” → books, reads it back. |
Proves: every home, rent, deposit, fee, amenity and special above was read from the Western Slope account on 2026-09-12; the flow is Fede’s rulings of the same evening (§11.22); the neighborhood lines come from the sampled notes for that exact address.
Proposed — pending Fede’s review. The page to hand the Western Slope team so they can test the leasing line themselves. Eight short calls, one screen each: what to play, what to say, what you should hear, and what should arrive afterwards. No preparation needed.
One number, your own phone, and your first call is the fresh one.
Proves: the number is live and answering today, on the same homes as your public listings.
You named the house, so nothing gets asked twice.
Tell us if she asks you something you already told her — your timing, your budget, or which area.
Proves: that home, its rent, deposit, fee and opening special are live on your account today.
A firm date and one area question leave exactly two homes.
Tell us if she offers a home you could not actually move into by your date.
Proves: a firm date silently keeps the homes you cannot have out of the conversation.
The caller with time to spare hears what is not on the market yet.
Tell us if she only offers what is open today and never mentions what is coming.
Proves: a relaxed date is worth a pre-leasing home, and still ends on the calendar this week.
Each question is chosen because it actually splits the list.
Tell us if the dog or the fence gets asked about twice, or the questions start to feel like a survey.
Proves: what you volunteer is written down once and used, never handed back as a question.
Every number she says should match the card on your website.
Tell us if any number differs from your own listing, or she produces one that is not published anywhere.
Proves: everything printed on the public card is in her head, and everything else is handed over, not invented.
A local answer, with the one thing she is not allowed to say.
Tell us if anything she says about the area is out of date, wrong, or not how a local would put it.
Proves: neighborhood answers are written per home in advance, so the call stays fast.
Asking for a human becomes a note, not a dead end.
Tell us if she puts you on hold, promises someone will ring you, or lets the visit drop.
Proves: the message reaches the team through one inbox, and the call still ends with a booking.
The word is the switch. Nothing else flips it.
Tell us if she switches to Spanish when you never asked, or stays in English after you did.
Proves: the language changes on the caller’s word alone, never on a guess.
Two things arrive. If a third one does, that is a bug.
Tell us if the address or time is wrong, if something arrives twice, or if anything else goes out at all.
Proves: the text is the template in use today; the team email is being rewritten this week, so its exact wording may change.
Four lines per call, in one place, as soon after the call as you can.
Proves: every call is recorded and transcribed on our side, so the time and the phone number are enough to find it.
Known gaps. Worth reporting anything else you hit.
Proves: each is a stated decision or a named blocker, not an unknown — the Microsoft approval is the one thing on your side.
What the tester says, and what should come back. Homes, rents and fees are today’s live listings.
| Call | You say | What should come back |
|---|---|---|
| all | (dials +1 970-822-0641) | “Thanks for calling Western Slope Property Management, this is Clara. Are you calling about leasing, maintenance, or something else?” → answer “leasing”. |
| 1 | “I drove past a new place on Ember Lane with your sign out front — is that one still available?” | “367 Ember Lane — yes, that one’s open. Three bedrooms, two baths, $2,750 a month. Brand new, attached garage, private fenced yard out back — and right now there’s a grand-opening special: six weeks free rent, with water, sewer, trash and gas included your first year. Anything in particular you’d like to know?” |
| 1 | “What do I need up front?” → “I’d like to see it.” | “Deposit is $2,750, and there’s a $32 background check fee with the application.” → “Do mornings or afternoons suit you better?” → two concrete times → booked, read back, name taken. |
| 2 | “I’m just seeing what you have open right now.” | “Happy to help. When are you looking to move?” |
| 2 | “End of the month — and I can’t really move that.” | “Got it, so let’s stick to what’s open today. Anywhere in particular, or are you open to a few areas?” |
| 2 | “Out toward Clifton if you’ve got anything.” | “Two in Clifton. 486 32 1/8 Road is a two-bed, one-bath at $1,250 a month, and 460 32 1/8 Road is a two-bed, one-and-a-half with a fenced backyard at $1,350. Both open now.” |
| 2 | “Tell me about the cheaper one.” → “What’s the deposit?” → “Can I come see it?” | Upstairs condo, about 890 square feet, new flooring, remodelled bath, new appliances, views of Mt. Garfield and the Bookcliffs, water/sewer/trash included, washer-dryer hookups → “$1,200.” → mornings or afternoons, two times, booked. |
| 3 | “I’m starting to look — probably end of October.” → “Grand Junction, near town ideally.” | “Then the one to know about is 374 Ember Lane — brand-new duplex, three bed, two bath, about 1,360 square feet, $2,775. It’s pre-leasing right now, which is almost exactly your timing. Want me to put you down for it?” |
| 3 | “Yeah. Can I see it?” | “That one’s not ready to walk yet. Its twin two doors down, 367 Ember Lane, is open now — same community, same floor plan — so you can walk that one this week.” → mornings or afternoons, two times, booked. |
| 4 | “Looking for a three-bedroom in Grand Junction, around two thousand.” → “Next month, I’m flexible.” → “Anywhere in Grand Junction, really.” | “When are you hoping to move?” → “Any particular part of town, or open to a few areas?” → “A few could work then. Do you have pets?” |
| 4 | “A dog.” → “A yard — fenced. He jumps.” | “Are you after a yard for him, or is a garage more the thing?” → “Then 566 Placer Street is your best fit: three bed, two and a half bath, about 1,342 square feet in Fruitvale, $2,100 a month. Recently updated, two-car garage, and the backyard is fully fenced. The other three-bedroom in your range is a townhome on Eisenhauer at $2,495 — garage and a balcony, but no yard, so I’d put Placer first.” |
| 4 | “What’s the deposit on Placer?” | “That one’s not published on the listing, so I don’t want to guess — I’ll get it to the team and they’ll confirm. Want to go see it? Mornings or afternoons?” |
| 5 | “I’m looking at the Ridgway one, the two-bedroom.” | “575 Redcliff Circle, unit 204 — two bed, two bath, about 950 square feet, $2,300 a month. Freshly renovated top to bottom, balcony, carport, and water, sewer and trash are included. Anything in particular you’d like to know?” |
| 5 | “What do I need up front?” → “Any other fees?” | “The deposit is $2,500, and there’s a $32 background check fee with the application.” |
| 6 | “Honestly — what’s around there? I don’t know that part of town.” | Groceries by name, the elementary and high school by name, the Colorado River paved trail system and the nearby canyon trails, and roughly how far downtown Grand Junction is. Positive, factual, no rating of the neighbourhood. |
| 7 | “Is there someone I could talk to?” | “Sure — tell me what you’d like to ask and I’ll write it down for the team. What’s your name?” |
| 7 | “Marcus. I want to know about the parking in winter.” | “Marcus, winter parking — noted, that’s going to the team. In the meantime, want me to get you in to see it? Mornings or afternoons?” → two times → booked, read back. |
| 8 | “Español, por favor.” → then keep going in Spanish. | The same call from that point on, in Spanish — the same questions, the same homes with the same rents, the same two time slots, and the same confirmation text. |
| 8 | (a different call — never say the word) | English, start to finish. She should not switch languages on her own at any point. |
Proves: every home, rent, deposit, fee and special above was read from the Western Slope account on 2026-09-12; the neighbourhood lines come from the notes written for those exact addresses; the flow is the decided one (§11.22).
The Western Slope phone line stopped ringing our stand-in and started ringing Western Slope’s own company record. Fede called it four times that evening and said what was wrong; the night turned each of those into a change, verified the line against the customer’s own record, replayed every earlier test call and scored it, and recorded a demo he can play to the client. What is left is a short list only he can do.
One line, moved onto the customer’s own record. Four calls, four lessons.
Proves: each call is a recording with a transcript and the records it wrote beside it; the line’s new record was read back from production.
What a caller hears today, and what is still held back.
Eight more are built, green and landing this morning, one at a time; two more wait until that queue clears. The appendix names them.
Proves: each live line was heard on a call placed to the number after the change reached production. The queue state is 06:00 Mountain, production 7710a274; the tracker rows move as merges land.
Four proofs, in the order they were run.
Proves: the judge ran on the subscription, never a metered key. The replays were real calls, the caller cleared between dials and every visit cancelled.
Proves: every value was read live from production, and every “does the phone script read it?” answer comes from tracing the code that builds a call.
Nothing here waits on engineering.
Proves: each is a write to a customer’s records, a spend, or a third-party setting — the three a session never does on its own.
Proves: each lesson traces to something that broke tonight: a cancelled merge, three exhausted budgets, five pull requests held an hour, a stray text, a machine out of memory.
Work on the phone line and the Western Slope company only; the rest of the repository’s night is not listed here. Live is on the main line and serving callers. In the queue is built and green, landing this morning one at a time. Parked is deliberately set down. The queue state is as of 06:00 Mountain on 13 September, with production serving 7710a274; the rows on the Western Slope go-live page move as each one lands. Two more changes are written and held back until the queue clears: the two-home pitch, and routing after-hours callers to leasing before any message is taken, on company-wide lines only.
| # | State | What it changes |
|---|---|---|
| 8060 | live | The line rings Western Slope’s own company record instead of our stand-in |
| 8082 | live | The stand-in’s setup script can no longer capture the live number |
| 8115 | live | A mis-heard word cannot put the whole call into Spanish |
| 8116 | live | The language switch needs the word itself — enforced in the tool, not only asked for in the script |
| 8100 | live | The Spanish greeting asks about a renta, the caller’s own word |
| 8052 | live | She takes the message instead of saying she is passing you over |
| 8061 | live | One narrowing question, asked once, in any wording |
| 8069 | live | A morning and an afternoon on one day, not two mornings |
| 8072 | live | The in-person visit is the offer; the video walkthrough only if asked |
| 8071 | live | The count of available homes leaks in the first person too |
| 8080 | live | Everything the listing states reaches the caller, and a fence keeps it that way |
| 8159 | live | A question she cannot answer goes to the visit, not to a message |
| 8146 | live | She stops telling callers somebody will get back to them |
| 8170 | live | Clara can say how long a visit takes on a company-wide line; dark everywhere else |
| 8158 | live | One visit to two homes, sized by how long the trip actually takes |
| 8171 | live | A company’s listed homes get their coordinates and the drive time between them |
| 8084 | live | A home’s card gets a place for what is around it |
| 8091 | live | The lines about what is around a home are written once and fenced |
| 8133 | live | “What’s around the home?” is answered off the home’s own row |
| 8186 | live | A neighbourhood line never speaks in the first person |
| 8188 | live | The operator command that fills in what is around a home |
| 8131 | live | The team hears about a phone booking once the call ends, with what was said on it |
| 8076 | live | The team’s email describes the home being visited, not an old preference |
| 8086 | live | The bench grades many-homes calls against what Fede actually asked for |
| 8109 | live | Re-lands the decided-flow pin and the register, which had merged into their own base branch |
| 8081 | live | A test property can turn the follow-up text off |
| 8105 | live | A bench property can turn its own team alerts off |
| 8148 | live | The bench robot’s booking is read off the right leg, and the caller is released properly |
| 8168 | live | The demo caller is allowed in the agent roster check |
| 8173 | live | The same, for the roster gate |
| 8179 | live | Removes the duplicate demo-caller row the two above created |
| 8147 | live | The self-review check: a pull request body must carry its plan, its proof and its channel answer |
| 8145 | in the queue | Mornings or afternoons asked first, then the times named |
| 8157 | in the queue | The caller’s name taken early, not on the way to the booking |
| 8068 | in the queue | Every call on the company-wide line asks when they need to move |
| 8150 | in the queue | Urgency picks the shortlist: open now for a firm date, coming-soon for runway |
| 8176 | in the queue | One visit, two homes — the visit row, the booking tool and the script |
| 8199 | in the queue | The confirmation text and the team email name both homes |
| 8184 | in the queue | The booking reads the drive the listings job already measured |
| 8177 | in the queue | A bench call that asks to see two homes in one trip |
| 8137 | in the queue | The many-homes tour email leads with the home, not with the company |
| 8125 | in the queue | One tour-cancelled email to the team, not two |
| 8224 | in the queue | The move-in answer keeps the caller’s own words; a date only when they name one |
| 8198 | in the queue | A company fee, labelled as the company’s, is a right answer — not an invented one |
| 8172 | in the queue | The tour confirmation never says who meets the caller |
| 8093 | parked | A new home learns what is around it on its own — set down as a draft after six review rounds; the operator command covers it meanwhile |
| 8204 | parked | The replay-and-judge harness itself — set down as a draft after four review rounds; the run it produced stands |
Proves: every state above was read from the repository’s own history and from each request directly, not from a polling watcher — those had gone blind on their hourly budget three times that day. Everything live shipped switched off or invisible to a real caller; turning any of it on at Western Slope is a separate step and Fede’s call.
Fede called the Western Slope company line and asked how long a tour is. Clara did not know. The cause is three different things wearing one coat: the company has no leasing-settings record at all, its knowledge record holds office hours and nothing else, and one answer — how long a visit takes — has never reached the phone script at any property, Camellia included.
Proves: every value in 13.4 was read live from production with consistent reads on 2026-09-12; every “does the script read it?” answer comes from tracing the code that builds a call.
Office hours, fees and policies make the whole trip. The visit length turns off early and never reaches the words.
Proves: the apartment fixture's before/after shows one new empty key and no changed value, so no Camellia, Yale or Willows caller hears anything different.
Ordered by how much a caller notices. “Reads it?” means the value reaches the words Clara speaks on a company-wide call.
| # | Setting | What the caller hears | Camellia | Western Slope today | Reads it? | Recommended |
|---|---|---|---|---|---|---|
| 1 | How long a visit takes | “How long is the tour?” | 15 minutes | not set → 30 | No — code gap | 15 minutes |
| 2 | Notice a booking needs | Whether “in an hour?” is offered | not set → 60 | not set → 60 | Yes | 60 minutes, written down |
| 3 | Office hours | Opening times, and every time offered | Mon–Fri 9–5, Sat 1–4:30 | Mon–Thu 9–4, Fri 9–3 | Yes | keep — already correct |
| 4 | Which times get offered | The two times put to a caller | derived | derived | Yes | no setting — row 1 fixes it |
| 5 | A calendar to book onto | Whether an offered time is really free | connected | nothing connected | Yes | the client connects theirs at go-live |
| 6 | Contact details | “What's your number / address?” | on file | missing | Yes | their published line, email and office address |
| 7 | Screening rules | “Do I qualify?” | on file | missing | Yes | their published income and history rules |
| 8 | Fees | “What does it cost to apply?” | on file | missing | Yes | $27 per adult, waived with a portable report |
| 9 | Pet policy | “Can I bring my dog?” | on file | missing | Yes | pets allowed; the team confirms the money side |
| 10 | Who Clara transfers to | “Can I talk to a person?” | a direct line | blank | Yes | leave blank — their main line is the one forwarding to Clara |
| 11 | Where the apply link comes from | “Can you send me the application?” | their listings page | not set | Yes | their listings page, at go-live |
| 12 | What answers price and availability | Every rent, every “is it open?” | the PMS | the public listings page | Yes | keep the listings page |
| 13 | Same-day rules per weekday | Whether Saturday walk-ups are offered | Saturday: no same-day | not set | Yes | nothing — they are closed weekends |
| 14 | Time zone | Every time she speaks | Mountain | Mountain | Yes | keep |
| 15 | Language | Whether Spanish is offered | on request | on request | Yes | keep — it comes from the caller, not the company |
| 16 | Email and text holding | Whether anything actually sends | live | both held | Changes what she may promise | keep held until Fede's go-live |
| 17 | The mailbox she answers from | The address on a confirmation | their inbox | a sandbox address | No | their leasing inbox, at go-live |
| 18 | How many homes one visit covers | “Can I see both?” | n/a | n/a | Yes — in the script | no setting; leave it in the script |
Proves: 18 rows — 4 already right, 11 unset or empty at Western Slope, 1 a code gap, 2 go-live steps rather than settings. The two test companies were updated so the bench matches; nothing was written to the real company or to Camellia.
Fede asked for his own test calls, and the client’s, to be placed again against the line after the night’s fixes — and scored by a judge trained on how he gives feedback. Sixteen recorded calls were replayed by a robot that plays the caller, and every call was graded twice: once before, once after, by the same judge.
Proves: the judge ran on Fable 5.1 on the Claude subscription; no metered key was read. Each verdict carries the line it was read off, and a verdict with no quote is discarded rather than believed.
So 4.8 understates where the line is now. Three more changes Fede asked for were still waiting to merge when the batch finished: asking mornings or afternoons before naming times, taking the name early, and asking when they need to move. Those three are the top three rows of what is still wrong below, which is what you would expect if the measurement is honest.
Proves: the deployed commit was read from the health endpoint before each individual call and stored with that call's result, not once for the batch — production redeployed six times while these sixteen calls were being placed.
Fede asked for the paperwork to be graded too, not just the conversation. Ten of the sixteen replays booked a visit; eleven wrote a prospect card.
Proves: read back from production with consistent reads after each call, from the visit record and the prospect card the call itself wrote. An earlier version of this section said the cards were empty; it was reading two field names the card does not have, and the corrected read is above. Texts and the team email are held on this company, so nothing reached a person.
| Call | Date | Who | Before | After | What got better | What is still wrong | |
|---|---|---|---|---|---|---|---|
| Shopping the whole list, narrowing to one town, one home in depth, all the fees, then booking a visit. | 2026-09-12 | Fede | 5 | 7 | +2 | Answers any fee or deposit on the home card straight away, and says so plainly when a field is not published; Opens with the company name and asks leasing, maintenance, or something else; Offers the in-person visit; brings up a video walkthrough only if the caller asks | Asks when they need to move — and how firm that is — before naming any home; Never says how many homes there are; Pitches a home in two or three sentences, then invites a question; Asks which part of town once, open-endedly — and "anywhere" is a complete answer; Asks a second, conversational narrowing question only while more than two homes still fit; Asks mornings or afternoons, then offers a couple of concrete times; Names at most two homes in a turn, each with its bedrooms and its rent |
| The greeting and the language: he spoke English and the line switched itself to Spanish. | 2026-09-12 | Fede | 1 | 8 | +7 | Stays in English unless the caller actually asks for Spanish; Gets the caller's name, once, and uses it | The call ends with a tour on the calendar; Asks when they need to move — and how firm that is — before naming any home; Asks mornings or afternoons, then offers a couple of concrete times; Urgent callers hear homes open today; unhurried callers hear what is coming |
| A neighbourhood question, asking for a person, two homes in one trip, and how long a visit runs. | 2026-09-12 | Fede | 3 | 4 | +1 | The call ends with a tour on the calendar; Opens with the company name and asks leasing, maintenance, or something else; Never says the team will reach out, or that she will find out and get back to them; When asked for a person: takes the question down for the team and goes back to the tour | Asks mornings or afternoons, then offers a couple of concrete times; Never says how many homes there are |
| A resident greeting her in Spanish, asking about his renewal, and correcting her on the office hours. | 2026-09-10 | Jay Taylor (Western Slope) | 2 | 2 | same | Opens with the company name and asks leasing, maintenance, or something else; Stays in English unless the caller actually asks for Spanish | Never says the team will reach out, or that she will find out and get back to them |
| Asking about a home that is not listed, then square feet, the garage and the utilities on one that is. | 2026-09-10 | Jay Taylor (Western Slope) | 7 | 2 | -5 | Opens with the company name and asks leasing, maintenance, or something else | Answers any fee or deposit on the home card straight away, and says so plainly when a field is not published; Asks when they need to move — and how firm that is — before naming any home; Never says the team will reach out, or that she will find out and get back to them; The call ends with a tour on the calendar; Gets the caller's name, once, and uses it |
| Shopping by price alone — anything under eighteen hundred — and then by town. | 2026-09-11 | Jay Taylor (Western Slope) | 5 | 6 | +1 | The call ends with a tour on the calendar; Opens with the company name and asks leasing, maintenance, or something else; Asks when they need to move — and how firm that is — before naming any home; Asks which part of town once, open-endedly — and "anywhere" is a complete answer | Never says how many homes there are; Asks mornings or afternoons, then offers a couple of concrete times |
| Is the one-bedroom still open, and what is the next time you have. | 2026-09-12 | Fede | 1 | 6 | +5 | The call ends with a tour on the calendar; Opens with the company name and asks leasing, maintenance, or something else; When asked for a person: takes the question down for the team and goes back to the tour; Pitches a home in two or three sentences, then invites a question; Urgent callers hear homes open today; unhurried callers hear what is coming | Asks when they need to move — and how firm that is — before naming any home; Never says the team will reach out, or that she will find out and get back to them; Asks which part of town once, open-endedly — and "anywhere" is a complete answer; Asks mornings or afternoons, then offers a couple of concrete times; Sorts picker from explorer — from what they said, or by asking outright when nothing did |
| Shopping three and four bedrooms with a budget, asking for all of them. | 2026-09-04 | Fede | 3 | 8 | +5 | Names at most two homes in a turn, each with its bedrooms and its rent; The call ends with a tour on the calendar; Opens with the company name and asks leasing, maintenance, or something else; Asks when they need to move — and how firm that is — before naming any home; Gets the caller's name, once, and uses it; Never says the team will reach out, or that she will find out and get back to them; Asks a second, conversational narrowing question only while more than two homes still fit | Asks which part of town once, open-endedly — and "anywhere" is a complete answer; Asks mornings or afternoons, then offers a couple of concrete times |
| A caller who cannot remember the address, only that it was a duplex in Ridgway. | 2026-09-04 | Fede | 5 | 3 | -2 | Names at most two homes in a turn, each with its bedrooms and its rent; The call ends with a tour on the calendar; Never says the team will reach out, or that she will find out and get back to them; Pitches a home in two or three sentences, then invites a question; Asks which part of town once, open-endedly — and "anywhere" is a complete answer | Asks when they need to move — and how firm that is — before naming any home; Never says how many homes there are; Asks mornings or afternoons, then offers a couple of concrete times |
| Pet policy questions against one named home. | 2026-08-29 | Fede | 3 | 6 | +3 | Answers any fee or deposit on the home card straight away, and says so plainly when a field is not published; Opens with the company name and asks leasing, maintenance, or something else; Never says the team will reach out, or that she will find out and get back to them; Pitches a home in two or three sentences, then invites a question | Asks when they need to move — and how firm that is — before naming any home; Asks mornings or afternoons, then offers a couple of concrete times |
| The greeting said his name with a Spanish accent, and he pushed on the language. | 2026-09-11 | Fede | 1 | 5 | +4 | Opens with the company name and asks leasing, maintenance, or something else; Stays in English unless the caller actually asks for Spanish; Asks which part of town once, open-endedly — and "anywhere" is a complete answer; Asks mornings or afternoons, then offers a couple of concrete times; Sorts picker from explorer — from what they said, or by asking outright when nothing did | Asks when they need to move — and how firm that is — before naming any home; Names at most two homes in a turn, each with its bedrooms and its rent; Pitches a home in two or three sentences, then invites a question; Urgent callers hear homes open today; unhurried callers hear what is coming |
| Asking outright to be transferred to someone in the office. | 2026-08-31 | Fede | 2 | 2 | same | Opens with the company name and asks leasing, maintenance, or something else; Gets the caller's name, once, and uses it | The call ends with a tour on the calendar; When asked for a person: takes the question down for the team and goes back to the tour; Never says the team will reach out, or that she will find out and get back to them |
| How much are utilities usually — a figure nobody publishes. | 2026-08-29 | Fede | 2 | 2 | same | Opens with the company name and asks leasing, maintenance, or something else; Asks when they need to move — and how firm that is — before naming any home | The call ends with a tour on the calendar; Gets the caller's name, once, and uses it; Never says the team will reach out, or that she will find out and get back to them; Sorts picker from explorer — from what they said, or by asking outright when nothing did; When asked for a person: takes the question down for the team and goes back to the tour |
| Naming his own time — put me in for a tour at four today. | 2026-08-25 | Fede | 0 | 4 | +4 | The call ends with a tour on the calendar; Opens with the company name and asks leasing, maintenance, or something else; Gets the caller's name, once, and uses it; Never says the team will reach out, or that she will find out and get back to them; Sorts picker from explorer — from what they said, or by asking outright when nothing did | Asks when they need to move — and how firm that is — before naming any home; Asks mornings or afternoons, then offers a couple of concrete times |
| Someone who is not a renter, leaving a message for the team. | 2026-08-31 | Fede | 2 | 9 | +7 | The call ends with a tour on the calendar; Asks when they need to move — and how firm that is — before naming any home; Gets the caller's name, once, and uses it; Never says how many homes there are; Never says the team will reach out, or that she will find out and get back to them; Asks which part of town once, open-endedly — and "anywhere" is a complete answer | nothing |
| A leaking toilet — the one thing this leasing line must hand straight to a person. | 2026-09-04 | Fede | 2 | 2 | same | Opens with the company name and asks leasing, maintenance, or something else | Never says the team will reach out, or that she will find out and get back to them |
| Call | After | The verdict |
|---|---|---|
| Shopping the whole list, narrowing to one town, one home in depth, all the fees, then booking a visit. | 7 | It got the tour booked, it offered morning and afternoon the way it should, and it actually knew the thirty-two dollar background fee this time instead of guessing — okay. But it never asked when they want to move in, it took the name at the very end, and the home description ran long enough that the guy had to cut in twice, so tighten that up. |
| The greeting and the language: he spoke English and the line switched itself to Spanish. | 8 | That one was clean — I mumbled something that sounded Spanish and she stayed in English, asked move-in first, gave me a morning and an afternoon, and got it on the calendar. Take the name earlier and say the time back once it's booked, but it's fine. |
| A neighbourhood question, asking for a person, two homes in one trip, and how long a visit runs. | 4 | She got the tour booked and she actually knew Ridgway is two hours from Grand Junction and put the two Redcliff homes on one trip, so that part is right. But she punted a simple neighborhood question to the tour, then said she'd leave the team a note with his number — we don't do that, that's off everywhere, and I shouldn't be finding it in a basic flow. |
| A resident greeting her in Spanish, asking about his renewal, and correcting her on the office hours. | 2 | He asked about his renewal and she tried to transfer him twice, said the office is closed word for word two times in a row, and then took a message with a promise the team would get back to him — that's the take-a-message path I told you to turn off everywhere. She stayed in English when he said hola, fine, but she told him there's no renewal on file before she even knew his name, never explained how renewals actually work, and I shouldn't be finding this on a basic resident call. |
| Asking about a home that is not listed, then square feet, the garage and the utilities on one that is. | 2 | She took three questions on a home that's sitting on our website and punted every single one to a note for the team, never mentioned 374 isn't ours, and never once said the word tour. Square feet and the garage are on the card, the follow-up promise is supposed to be off everywhere, and this whole thing should have been 'I don't have utilities on file, let's get you in to see it Saturday' — that was a miss. |
| Shopping by price alone — anything under eighteen hundred — and then by town. | 6 | It got the tour booked and I liked that she didn't guess the square footage, but why did she book it before asking what time worked and then have to reschedule on the same call? That's sloppy on a basic flow — fix that, and take the name earlier, but it's fine. |
| Is the one-bedroom still open, and what is the next time you have. | 6 | It got the tour booked and the listing pitch was short, morning or afternoon on the same day, that's how it should go. But she literally started to say she'd leave the team a note before catching herself, and that whole take-a-message thing is supposed to be gone everywhere, plus she fired the booking twice, so I'm finding stuff on a basic flow. |
| Shopping three and four bedrooms with a budget, asking for all of them. | 8 | That one worked — she asked the move-in date first, knew Fruitvale is inside Grand Junction, knew the Morningside place cold, offered a morning and an afternoon, and got it on the calendar. You should take the name earlier and stop asking the questions one at a time like a form, but it's fine. |
| A caller who cannot remember the address, only that it was a duplex in Ridgway. | 3 | She quoted the one-bedroom at twenty-three hundred and only fixed it to seventeen-fifty when the guy called her on it — that price is on the listing, she should just know it, and a transfer fired for no reason on a basic leasing call. The tour got booked and the 9:30 was handled fine, but she never asked move-in date, took the name at the very end, and had that dead '...' turn where the caller's sitting there wondering if she's still on the line. |
| Pet policy questions against one named home. | 6 | She got the tour on the calendar, offered morning and afternoon like she should, and never made anything up, so that part I'm fine with. But she said 'the leasing team can confirm' about ten times in a row and it starts sounding like a form — the keys question and the deposit are stuff she should just know, she never asked for a move-in date, and she took the name at the very end, but it's fine. |
| The greeting said his name with a Spanish accent, and he pushed on the language. | 5 | She got Casey on the calendar fast and didn't make anything up, so the basic thing worked. But booking two separate tours an hour apart is wrong, it should be one combined visit with the same agent, and she asked five questions one at a time and then started reading listings until he had to cut her off. |
| Asking outright to be transferred to someone in the office. | 2 | She heard 'transfer me' and went straight to taking a message and promising a callback, which is the one thing I said to turn off everywhere. Nobody ever tried to book him a tour — she should have said the office is closed, the fastest way to see units and pricing is a visit, and offered a couple of slots. |
| How much are utilities usually — a figure nobody publishes. | 2 | She took a message on a question she could have just answered honestly and parked to the tour, and then she promised the team would follow up, which we don't do. Nobody got named, nobody got asked about move-in, and nobody got offered a visit — that was a miss. |
| Naming his own time — put me in for a tour at four today. | 4 | He asked for a tour and the first thing she did was try to transfer him and promise to leave the team a note with his number — that's supposed to be off everywhere, and I shouldn't be seeing it on a basic booking. She got him on the calendar for Monday in the end, but he had to say 'just book me' twice, she fired the booking before she even had a home picked, and the two listings should have been one combined visit instead of making him choose. |
| Someone who is not a renter, leaving a message for the team. | 9 | Vendor calling in, she took the message, used the caller ID when he didn't have his number, and got off the phone in under a minute with no fluff. Only thing is she never asked who at Miracle Method to ask for, but it's fine. |
| A leaking toilet — the one thing this leasing line must hand straight to a person. | 2 | A leaking toilet is the one call that goes straight to a person, and the transfer fired but she just kept talking and played maintenance dispatcher instead. Then she told him a work order was in and she'd text him when the handyman was confirmed, and nothing behind her actually did either of those things, so that's a promise we can't keep. |
Proves: 16 calls were placed as real phone calls to the company line and graded from their recorded transcripts; the caller identity was cleared between every dial, and every visit the run booked it cancelled. Production was serving b234e96a56 when the batch started.
Turning any of this on at a real property remains a separate step and Fede's explicit call, under the standard rollout playbook. Nothing on this page changes that.