The onboarding we ran at Situs Group on the night of Sep 1, turned into a repeatable 11-stage playbook, a data inventory with load/don't-load verdicts, and a review UX — plus nine decisions that need Fede.
Proposed — pending Fede's review · 2026-09-02
Contents
You add Clara to your PMS. We deploy a fleet of agents overnight on your operations and come back the next day — sometimes in a few hours — with all the context. You spend a short session confirming, denying, and cleaning up what we're not confident in or found conflicts on. That's the onboarding.
This is the vision's "judgment with boundaries" loop run once, up front: outside policy Clara asks, the answer becomes policy, and she never asks again. Onboarding is that loop compressed into one night of collection and one short review session — instead of six months of the operator catching mistakes one at a time.
Commercially: pre-seed, seed raise in 6–9 months, pre-PMF, Situs is the first paid design partner. The overnight fleet is the wedge (nobody else can stand up a property's rulebook from the client's own records in one night) and it is the seed story (every property onboarded makes the next one cheaper).
situsgroup.appfolio.com| Wall clock | Agent-hours | Effort split | Re-pull once the toolkit existed | Outputs |
|---|---|---|---|---|
| 5 h 39 min 18:45 → 00:24 MT |
24.4 ~32 agent launches |
45% load-bearing 38% supporting 17% noise |
1 h 35 min 5,179 files / 142 MB, 1 orchestrator, 4 processes — call it a 1.5 h job |
Property facts · 23 stable rules · 27 conflict rows (12 topics) · missing-facts list (14 clusters / 690 blocked rows) · human baseline to beat · 2,118-turn replay graded · go-live blocker list |
Human baseline measured from their own records: median 46 min to an individual human answer in hours, 11 h after hours, 22% never answered. Replay: 56% of Clara's draft replies acceptable cold, before any of this context was loaded.
~4.5 h of stage time; ~2.5 h wall clock with exports and entity pulls parallel and policy mining, replay and the config audit concurrent. PMS-specific stages change mechanics per PMS; PMS-agnostic stages survive a swap to Yardi or RealPage unchanged in shape.
Order changed on Sep 2 (D9). S5 documents and S7 policy mining were the lane we built first; both were then measured and neither moved the replay headline. The new stage S9b makes the question-bank coverage check an explicit gate rather than a report, and the post-run fill order puts live integrations ahead of documents — the calendar, availability, balances and application status first, because 64% of real question volume needs live state that no document can supply.
| Stage | PMS-specific? | Inputs | Outputs | Time | Agents / model | Guard for the failure we saw |
|---|---|---|---|---|---|---|
| S1 Access, single login, write guard | Contract specific, shape agnostic | PMS subdomain; Keychain credentials; a 2FA channel we control; written read-only authorization | Shared cookie jar, session PID file, write-guard log, login-event log | 10 min | 1 session holder (never more), deterministic script | Fix #1. One session holder, N jar consumers. PID file, not pkill patterns — a missed pattern re-authenticated on a credential shared with the production JP runner. Hard cap of 1 login per run. Mandatory write guard with a pre-set allowlist for AppFolio's own report render and the calendar read-query |
| S2 Parallelism calibration + soak | Agnostic | Live jar | Probed tab ceiling every walker reads | 10 min | 1, deterministic | Measured ceiling: 8 tabs / 8 concurrent fetches, 33 pages/min sustained. Never re-request an identical URL (that is what drew the one 429). Treat 429/503 as throttled-but-alive, only a redirect to login as dead — mistaking one for the other triggers the re-login hazard |
| S3 Report / bulk exports | Specific | Jar; report list; the window | ~40 CSVs, named by their filter | 25 min | 1, deterministic + a standing retry pass | Never use a report as a volume denominator — a date-scoped work-order report returned 289 rows where the per-record walk found 3,810. Three reports never export in this account; move them to a known-unavailable list instead of retrying 113 times |
| S4 Entity pull, in dependency order | Specific | S3 CSVs as the index | Occupancies → tenants + full message history → work orders → guest cards → applications → renewals → calendar → inspections | ~60 min at 8 tabs | 1 orchestrator, 4 concurrent processes, deterministic | A post-condition per record type, checked before the file is written — 207 guest cards were saved as empty shells and had to be re-pulled. Assert index count == detail count. Navigate to a same-origin page before any in-page fetch. The window rule: current year complete, half-open range, undated records dropped never imputed |
| S5 Documents, templates, settings | Specific | Jar; occupancy and property lists | Executed leases, templates, letters, fee schedule, screening criteria, house rules + an index mapping each file to its property | New — unmeasured | 1, deterministic | Not done last night — the largest gap. Two traps: the plain document URL redirects to the blank base template (a blank contract was once attached to a customer notice this way); only the download POST returns the executed document — and that POST is a non-GET, so the S1 write guard must allowlist it deliberately |
| S6 Postprocess, manifest, window audit | Agnostic | The raw dump | Manifest with a mandatory gaps[] array; window audit | 20 min | 1 script + 1 auditor (sonnet) | Fix #3. The window audit runs before any analysis — last night ~60 published statistics had to be restated (e.g. "tenants who ever replied by text" 44.6% → 34.8%). Exclusions list at manifest time (a "DO NOT USE" placeholder property carried 17 phantom units). The bot-vs-human actor list is a kickoff-call input, never inferred — one shared VA login looks exactly like a bot |
| S7 Policy mining | Agnostic | Windowed message corpus + the PMS's own per-lease settings | Policy ledger: rule as observed, evidence label, counts, quotes, and the system-configured value beside it | 40 min | 1 on opus — judgment: rule vs one-off | Situs: 49 topics, 23 stable rules, 12 conflict topics / 27 rows, 5 unknown. The killer move is staff-says vs system-configured: staff quote a flat $50 late fee; AppFolio holds five different schemes across the 485 leases that have one. Neither side is authoritative alone — the conflict list is the client conversation |
| S8 Property-facts assembly | Agnostic | S3 reports + S4 records + S7 stable rules | Per-property fact block | 20 min | 1, deterministic | Every inconsistently-quoted value written explicitly as NOT ESTABLISHED. That is load-bearing, not a placeholder: it converts "Clara invented a number" from an invisible pass into a graded failure in S9 |
| S9 Replay dry run | Agnostic | Real inbound turns that got an individual human reply + the S8 fact block | Graded rows, results, scrub map, 100-row calibration sample | 40 min for 2,118 rows at concurrency 8 | Production draft model as subject, judge model as grader — on the subscription; a metered runtime is refused | Two non-negotiables: in-process prompt replay only (the full harness can dispatch a real send, and these are a third party's residents), and deterministic scrubbing across the whole dump. Result: 56% acceptable, missing-fact 33%, wrong-action 5%, wrong-fact 4%, hallucinated-policy 2%, tone 1%. Run 2, with the full mined knowledge base loaded, returned 56% again — a null result inside a 0–3 point noise floor measured by a control re-run |
| S9b Question-bank coverage check | Agnostic | The 397-question bank (P0/P1) + the per-property knowledge blocks from S8 | Per-question answered / missing-fact / wrong, a coverage percentage by tier and property, and a fill list ranked by the inbound volume each missing fact unblocks | 15 min for 1,062 asks at concurrency 8 | Production draft model as subject, pinned judge as grader — on the subscription | The gate a first client cannot otherwise have — no conversation history exists to replay, so the bank stands in. Two rules: no thread, no resident record, no guest card, so a fact the knowledge base lacks is genuinely absent; and re-run it after every fill step, not once. Situs first run: 27% answered (P0 33%, P1 23%), wrong only 2%, flat across communities — a portfolio gap, not a per-property one |
| S10 Audit of our own property config | Agnostic | The PropFlow codebase + the new client's shape | Findings graded A (will break) / B (will misbehave) / C (tests only) / D (already per-property), each with file:line and fix altitude | 35 min | 1 on opus | Measured: A=9, B=32, C=12, D=17. Audit the live repo — the first pass hit a 4.5-month-stale mirror and three findings had to be retracted. Search every module root; a sweep of one lib path misses the files holding the biggest prompt findings |
| S11 Outputs and handoff | Agnostic | Everything above | Fact block + stable rules to Clara · missing-facts list to the ingestion backlog · conflicts to the client · baseline to the scoreboard · blockers to engineering · PII-marked data package to the team | 30 min | 1 on sonnet | Nothing ships to Clara that did not pass through the review session |
Paired with the hub's 58-item config inventory (16 switches / 30 config fields / 12 knowledge sections across 7 dependency stages). Six of the 58 are source-code edits requiring a deploy — so "turn on property #23" is not yet a form. Portfolio defaults are the fix: tour conventions, follow-up timing, escalation style and the rollout ratchets move to the org tier; the building keeps only what is the building — its number, calendar, mailbox, hours, specials, fees, units, PMS pointer.
17% waste on a first-of-kind run, and only ~2.3 h of it is genuine defect rework. The biggest single item was a deliberate scope add, not a mistake.
| Noise | Agent-hours | Cut or keep |
|---|---|---|
| Prior-year pull + year-over-year analysis | 1.80 | Keep for sales, cut for operations. Added at 23:05 "just to have it"; correct for the commercial proposal, zero contribution to Clara's context. See D2 |
| Two external literature memos | 1.43 | Cut. Commissioned before the corpus was in hand and superseded by the memo written from the actual data. Nothing cites them |
| Guest-card empty-shell re-capture | 0.58 | Cut by guard — a post-condition per record type |
| Findings-report rewrite after "insanely hard to read" | 0.25 | Cut by process — presentation rules stated before v1, not after |
| Inspections type-bug re-run | 0.24 | Cut by guard — assert index count == detail count |
| Write-guard / double-login / blank-page fetch failures | ~0.05 | Cut by guard — pre-set allowlist, PID file, same-origin precondition |
| Four parallel scout logins that killed each other | — | Cut by rule — one session at a time, always |
The pattern: every defect that cost real time has the same shape — no post-condition on a captured record. Empty shells, a bad URL builder, a fetch from a blank page, an aborted render call. Playbook v1 turns each into a guard.
59 entities inventoried across the client's PMS. The verdict, not the volume, is the product of this stage.
| Verdict | Count | Meaning |
|---|---|---|
| ONBOARD | 8 | Context Clara loads once at onboarding |
| LIVE-SYNC | 19 | Live data Clara reads at runtime — a feed, not an onboarding artifact |
| ANALYSIS-ONLY | 25 | Findings report and replay corpus. Never loaded into Clara |
| EXCLUDE | 4 | Context rot — explicitly do not load |
| NOT EXPORTABLE | 3 | Confirmed twice in this account; stop retrying |
| Entity | Source | Fields | Volume at Situs | Freshness | Why |
|---|---|---|---|---|---|
| Rent roll | Report export | Unit, resident, rent, status | 10 rows | Nightly | Core property facts. Also a sync feed |
| Rent roll, itemized | Report export | Charge composition per unit | 721 | Nightly | What a resident is actually billed, line by line |
| Unit directory | Report export | Unit inventory, sizes | 771 | One-time + on change | The unit list Clara answers about |
| Property directory | Report export | Property list | 206 | One-time | The properties Clara is configured against |
| Vendors | Vendor page + directory export | Name, trade, contact | 1,281 | On-demand | Trade routing for maintenance |
| Recurring work-order templates | Recurring work-orders page | Schedule, trade, property | 220 (224 rows) | On-demand | "When does pest control / filter service happen here" is a real resident question |
| Stable policy rules | Derived (S7) | Rule, evidence label, counts, quotes, configured value | 23 rules | Confirmed at review, then re-checked on a cadence | The rulebook. Derived from messages, never the raw messages themselves |
| Property fact block | Derived (S8) | Facts + explicit NOT ESTABLISHED list | 1 per property | Per onboarding | What Clara answers from — including what she must refuse to guess |
| Dataset | Volume | Why not |
|---|---|---|
| Owner directory | 561 rows | Owner PII. Clara does not answer owners at go-live |
| Inventory status | 3 rows | Effectively unused in this account |
| Inventory usage | 1 row | Effectively unused in this account |
| Keys detail | 1 row | Effectively unused in this account |
| Entity | Source | Volume 2026 | Freshness | Verdict | Why |
|---|---|---|---|---|---|
| Occupancy index | Internal JSON feed | 3,438 | Nightly | LIVE-SYNC | The universe every other walk is driven from |
| Tenant / occupancy record | Page + sub-blocks | 1,418 | Nightly | LIVE-SYNC | Identity, unit, lease dates, balance |
| Work orders (open) | JSON:API + detail page | 2,588 | Nightly | LIVE-SYNC | Clara must know what's already in flight |
| Guest cards (open prospects) | Index + detail + conversation panel | 883 | Nightly | LIVE-SYNC | Live leasing pipeline |
| Rental applications | Index + applicant detail + audit timeline | 90 | Nightly | LIVE-SYNC | Application status is a top-3 missing fact (84 blocked rows) |
| Renewal rows | Filtered feed | 143 | Nightly | LIVE-SYNC | Phase 2, not MVP |
| Calendar events | GraphQL read query | 825 | Live | LIVE-SYNC | Tour booking cannot work without it. Pulled in 28 seconds |
| Unit vacancy detail | Report export | 212 | Live | LIVE-SYNC | Live availability is the #2 missing fact (102 blocked rows) |
| Historical advertised rent (current) | Report export | 1,163 | Nightly | LIVE-SYNC | Current asking rent. History is analysis-only |
| Tenant directory | Report export | 1,037 | Nightly | LIVE-SYNC | Identity resolution |
| Delinquency / delinquency as-of | Report export | 433 | Live | LIVE-SYNC | The headline $3.73M owed across 385 tenants is unverified — likely mixes commercial and stale balances |
| Tenant unpaid charges summary | Report export | 1,220 | Live | LIVE-SYNC | "Resident ledger line items" is a named missing fact |
| Security deposit funds detail | Report export | 466 | On-demand | LIVE-SYNC | Deposit amount is the single most-repeated individual missing fact |
| Lease expiration detail / summary | Report export | 191 / 39 | Nightly | LIVE-SYNC | Phase 2 |
| Renewal summary | Report export | 55 | Nightly | LIVE-SYNC | Phase 2 |
| Rental applications report | Report export | 34 | Nightly | LIVE-SYNC | Screening timeline |
| Upcoming activities | Report export | 3 | Live | LIVE-SYNC | Today's schedule |
| Tenant event history | Page sub-blocks | 174,619 events | One-time | ANALYSIS-ONLY | The derived policy ledger is loaded; the raw events never are. This is the single most important line in the table |
| Email bodies | Per-email fetch | 93,656 | One-time | ANALYSIS-ONLY | Replay corpus + policy mining |
| SMS threads | Page sub-block | 32,309 | One-time | ANALYSIS-ONLY | Replay corpus + policy mining |
| Tenant notes | Page sub-block | 1,355 | One-time | ANALYSIS-ONLY | 49% are truncated in the page itself |
| Lease / renewal audit log | Lazy-loaded sub-block | 45,960 | One-time | ANALYSIS-ONLY | Who did what, when — for the baseline, not for Clara |
| Work orders (closed history) | Same feed | — | One-time | ANALYSIS-ONLY | Vendor track record, not runtime context |
| Inspections | Feed + detail | 201 | On-demand | ANALYSIS-ONLY | Executed instances only; the template was never reached |
| Renewal detail documents | Lease-document page | 21 of 143 | One-time | ANALYSIS-ONLY | Functionally useless — every captured text is truncated at exactly 8,000 characters |
| Gross potential rent, occupancy summary, unpaid balances by month, lease history | Report exports | 724 / 58 / 481 / 203 | Nightly | ANALYSIS-ONLY | Portfolio analytics for the findings report |
| Work order report | Report export | 2,448 | Nightly | ANALYSIS-ONLY | Row counts unreliable — the date filter keys the wrong field |
| Work-order labor summary | Report export | 28 | — | ANALYSIS-ONLY | 1,442 entries / 1,760 hours, 0 billed — the billback gap |
| Purchase orders | Report export | 3 | — | ANALYSIS-ONLY | PO/invoice sections parsed empty on every work order checked |
| Guest cards / inquiries / inactive guest cards / showings | Report exports | 137 / 128 / 400 / 22 | — | ANALYSIS-ONLY | Leasing corpus |
| Leasing funnel / agent performance / summary / prospect source | Report exports | 33 / 8 / 4 / 29 | — | ANALYSIS-ONLY | Commercial findings only |
| Inspection detail / unit inspection / unit turn detail | Report exports | 1,193 / 212 / 952 | — | ANALYSIS-ONLY | Turn analytics |
| Box score, property performance, tenant transactions summary | Report exports | — | — | NOT EXPORTABLE | Never exported in either year. Platform limitation in this account, confirmed twice — one of them was retried 113 times against a modal intercepting the click |
These cut across entities rather than removing one, and they are separate from the 4 excluded datasets.
| Do not load | Volume | Why |
|---|---|---|
| The "DO NOT USE" placeholder property | 17 units, 0% occupancy, 146 work orders | Not a property. Inflates every vacancy and maintenance number and gives Clara 17 phantom units |
| Commercial properties | Office towers, suites, building-directory threads | We are scoped to multifamily. Their commercial line stays outside our scope — and the top replay failure was a commercial suite-directory thread |
| Pre-window tenant history | Only 72% of texts and 46% of emails fall inside the current year | Survivor-biased: full history survives only for people still living there. Loading it teaches Clara a skewed past |
| Bot-authored messages | Four named system accounts | The PMS's automation talking to itself. The trap: one shared VA login looks exactly like a bot and must be kept as human — get this list in the kickoff call |
| Templated blasts and marketing sends | 15,408 of 20,686 outbound texts (74%) across 267 templates; one blast alone was 554 texts | Broadcast copy is not conversational ground truth — and it swallows real questions: 18% of question-bearing resident texts were "answered" only by the next blast |
| Failed / undelivered messages | 1,661 (8.03%) | Never reached a human. A data-quality finding, not context |
| Duplicate co-tenant messages | Deduped on timestamp + first 120 characters | The same message stored on each co-tenant's record |
| Owner directory and owner correspondence | 561 rows | Owner PII; owners are out of scope at go-live |
| The prior-year dump, in full | 5,179 files / 142 MB | Exists for year-over-year comparison only. Never Clara's context |
| Scout-run outputs | Four directories | Superseded by the full dump. One exception: the property CSVs are still the only copy of some views |
| Renewal document text | 8,000-character truncations | Truncated mid-lease. A partial lease is worse than none |
And we already own the capability. The browser-agent repo carries three live-verified document readers the dump toolkit simply never called: an executed-lease reader (proven byte-identical to what the UI prints), a renewal-letter renderer, and a per-document signature-status reader. What last night captured was the pointer — a filename and a link, as scraped text. The link was never followed.
| Document | Where it lives in AppFolio | Reachable by the toolkit? | Replay gap it unblocks |
|---|---|---|---|
| Fee schedule (application, admin, deposit, pet, parking) | Property settings + lease charge config; the published schedule is usually a document | Partly. The configured values were captured and they contradict what staff say. The published schedule document was not reached | 70 blocked rows — the highest-value document in this table |
| Application / screening criteria | Online-application settings; rental-application questions | Not attempted — no path in the toolkit | 84 rows combined with application status. Fair-housing sensitive: this must be the client's written text, never inferred from staff messages |
| House rules / community policies | Property Documents tab, or a lease addendum | Unknown — no path, zero hits | Noise, conduct, parking and tow, smoking, guests — all currently inference-only topics in the ledger |
| Executed leases + addenda | Occupancy → Lease Documents | Yes, proven live. Index via the occupancy API, binary via the download POST | Lease-terms questions directly; underpins renewals and move-outs |
| Lease templates / e-sign packets | Form templates | Yes — already read by the lease reader's resolver | The template is what makes a portfolio-wide rule loadable instead of 1,418 per-resident reads |
| Notices & letters templates | Letters & templates | Partly — the renewal-letter render path is proven | Tone and required legal language for outbound notices |
| Move-in / move-out checklists | Inspection templates | Instances yes, template no | Move-out and turn charges; deposit-return questions |
| Renters-insurance requirements | Lease addendum / property settings | No path attempted | The practice is well measured (1,297 messages about the monthly liability charge); the requirement is not |
| Vendor contracts & insurance certificates | Vendor record → attachments | No path attempted | Vendor, pest and inspection schedules — 21 blocked rows |
| Welcome packets | Shared documents library | No path attempted | Move-in questions: keys, utilities, laundry, mail |
| Website content & listing text | Outside the PMS — the public listings page | Yes, and partly done | 102 rows — the #2 cluster. And listings sync never runs for any Situs property (blocker above) |
| Work-order photos & invoices | Work-order attachments | Links only — the code comment says "captured, never clicked" | Billback evidence |
Priority order, by blocked replay rows: fee schedule (70) → application and screening criteria (84, fair-housing sensitive) → house rules → executed leases and templates → vendor and pest schedules (21).
A new client has no conversation history with us, so nothing tells us whether the knowledge base we just built is good enough to put Clara in front of their residents. The question bank is the stand-in: ask the questions other properties actually get, against this client's knowledge base, and the gaps come out as a ranked shopping list before anyone is texted.
397 canonical questions — P0 154 / P1 200 / P2 43 — built from 5,465 real inbound turns across two corpora: Situs Group (1,636 apartment turns, 2026) and Camellia Apartments (3,829 turns since April 2026, pulled read-only from production). 1,940 of those turns (35%) are questions; the rest are acknowledgements, greetings and content-free fragments. Every question carries the fact key that would answer it — 112 keys, 89 of them behind at least one P0 question — plus where that fact lives in AppFolio and which document normally carries it.
| Tier | Question | Mostly asked by | Situs | Camellia | Total | Fact key |
|---|---|---|---|---|---|---|
| P0 | When can I schedule a tour? | Prospect | 78 | 154 | 232 | tour.availability_calendar |
| P0 | How do I reach a specific staff member? | Vendor | 3 | 54 | 57 | contact.staff_directory |
| P0 | What units are currently available? | Prospect | 12 | 40 | 52 | availability.vacant_units |
| P0 | How do I request maintenance? | Resident | 26 | 21 | 47 | maintenance.request_process |
| P0 | Can I reschedule my tour? | Prospect | 34 | 9 | 43 | tour.availability_calendar |
| P0 | What is the property address? | Prospect | 22 | 13 | 35 | property.address_directions |
| P0 | What is the status of my application? | Prospect | 26 | 8 | 34 | apply.status |
| P2 | [long tail — 34 one-off questions folded into one row] | Resident | 3 | 31 | 34 | other.long_tail |
| P0 | How do I reach the front office? | Prospect | 6 | 26 | 32 | contact.phone_main |
| P0 | How do I submit a payment? | Resident | 24 | 8 | 32 | ledger.payment_methods |
| P0 | How do I access the online application? | Prospect | 16 | 9 | 25 | apply.link |
| P0 | Can I set up a payment plan for my balance? | Resident | 21 | 0 | 21 | ledger.payment_plan_authority |
| P0 | What payment methods are accepted? | Resident | 15 | 5 | 20 | ledger.payment_methods |
| P0 | Who am I speaking with? | Prospect | 3 | 16 | 19 | contact.staff_directory |
| P0 | When can I move in? | Prospect | 8 | 10 | 18 | availability.move_in_dates |
The top question is 12% of all question volume on its own. Tiers are cumulative share of question volume: P0 is the top 80%, P1 the next 15%, P2 the long tail. Spanish is 5.8% of question volume, carried almost entirely by Situs.
Coverage is flat across the three communities (23–29%), so this is a portfolio-level gap, not a per-property one: the knowledge blocks are built from the same PMS fields everywhere, and the same fields are empty everywhere. Filling one building's gaps fills all thirteen.
| # | Fact | Inbound volume unblocked | Where it lives |
|---|---|---|---|
| 1 | tour.availability_calendar — the live showing calendar | 598 | Not in AppFolio. The showing calendar lives in the leasing tool / staff calendar |
| 2 | apply.status — application status for this applicant | 177 | Applications module + screening settings; guest card; online application |
| 3 | contact.staff_directory — who is who, and how to reach them | 147 | Property → Property Manager + contact fields; vendor directory |
| 4 | tour.confirmation_process — how a tour gets confirmed | 145 | Not in AppFolio. Staff availability + showing policy |
| 5 | ledger.charge_breakdown — what the charges on this account are | 126 | Tenant ledger; delinquency report; late-fee settings |
| 6 | ledger.balance_due — what this resident owes right now | 93 | Tenant ledger; delinquency report |
| 7 | fees.deposit_security — the security deposit | 90 | Charge codes; lease recurring charges; application settings |
| 8 | availability.vacant_units — what is vacant today, with rents | 85 | Reporting API unit-vacancy + unit-directory reports; unit listings |
| 9 | movein.date_process — when they can move in, and what happens | 81 | Lease record move-in date; unit turn status |
| 10 | vendor.work_order_scope — what the vendor was actually sent to do | 81 | Vendor directory; work-order assignment; bills and invoices |
A separate long-tail bucket (177 turns of one-off questions with no shared fact) sits between #1 and #2 and is not fillable as a fact. Six of the ten are live system state. The single biggest gap is not a document at all — the tour calendar alone blocks 598 inbound messages, nearly a third of all question volume, and there is no AppFolio field for it. Full ranking in coverage_fill_list.csv.
Every question in the bank names the fact key(s) that would answer it, drawn from a controlled vocabulary seeded from the replay's missing-fact list. Each key carries its AppFolio source, its document source, and whether a P0 question depends on it — so the bank doubles as the ingestion schema, not just a checklist.
| Family | Keys | Family | Keys | Family | Keys |
|---|---|---|---|---|---|
apply.* | 12 | contact.* | 5 | utilities.* | 4 |
lease.* | 10 | property.* | 5 | moveout.* | 4 |
fees.* | 8 | access.* | 5 | pets.* | 4 |
policy.* | 8 | parking.* | 5 | availability.* | 3 |
ledger.* | 7 | amenities.* | 5 | hours.* | 3 |
maintenance.* | 6 | tour.* | 4 | movein.* | 3 |
pricing.* | 4 | vendor.* | 4 | other / escalation / owner | 3 |
The keys carrying the most real messages behind them:
| Fact key | Questions using it | Messages behind it | Where it lives |
|---|---|---|---|
tour.confirmation_process | 14 | 368 | Not in AppFolio — staff availability + showing policy |
availability.floorplans | 12 | 360 | Unit vacancy + unit directory reports; marketing listing sheet |
tour.availability_calendar | 6 | 313 | Not in AppFolio — staff availability + showing policy |
other.long_tail | 60 | 206 | Varies |
hours.office | 14 | 177 | Property contact info (partial); office-hours + after-hours doc |
availability.vacant_units | 14 | 173 | Unit vacancy + unit directory reports; marketing listing sheet |
contact.email_leasing | 16 | 134 | Property manager + contact fields; staff directory / phone tree |
availability.move_in_dates | 10 | 120 | Unit vacancy + unit directory reports; marketing listing sheet |
Corpora. Both scrubbed locally before anything reached the deliverable. Situs Group: 1,636 inbound turns, 2026-01-01 → 2026-09-01, multifamily only (16 apartment properties / 397 units), from the de-duplicated turn set the replay already used. Camellia Apartments: 3,829 turns, 2026-04-01 → 2026-09-02, pulled read-only from the production table through the same read pattern as the existing prod-corpus harness. Names, phones, emails and dollar balances substituted locally.
From messages to canonical questions. Every inbound turn is tagged — is this a question, what is the underlying ask in generic phrasing, who is asking, which fact key(s) would answer it — on claude-haiku-4-5 at temperature 0, on the subscription. 1,940 of 5,465 turns (35%) are questions. Raw phrasings are then clustered twice within each fact key (1,707 → 756 → 451), capped at 6 questions per key, then counted across both sites and tiered by cumulative share of volume. P2 questions seen exactly once are folded into one long-tail row per fact key so the bank stays a working checklist. Final: 397.
What the checker does. For each question it renders Clara's production drafting prompt, read at run time from the leasing eval config, with the resident persona swapped in on resident questions, the property's knowledge block loaded as a cached system block, and no thread, no resident record and no guest card — so anything the knowledge base does not carry is genuinely absent, which is the point. A pinned judge then labels the reply answered (specific, actionable, supported by the knowledge base), missing-fact (a graceful "let me check" is still a gap), or wrong (asserted a fee, policy, amount or timeline the knowledge base contradicts or never mentions). Subject model resolves from production resolveDraftParams(channel); judge is the pinned EVAL_JUDGE_MODEL. Everything runs on the subscription; the runner refuses a metered runtime. It uses the in-process replay tier — there is no code path in it that can dispatch a send.
What we cannot see.
The questions themselves were collected from public renter FAQ pages, ILS "questions to ask" guides and AI-leasing-vendor material — Apartment List's 36 questions, Greystar tour questions, Zillow and Zumper tour-scheduling pages, Camden and AvalonBay pet FAQs, HUD assistance-animal guidance, EliseAI and Zuma product pages — grouped into 18 topics from availability and tours through screening, fees, pets, parking, utilities, amenities, lease terms, move-in, maintenance, emergencies, payments, renewals, move-out, community rules, accessibility and vendors. Every row cites its source; nothing was invented.
The frequency claims, all carrying the caveat above: roughly 60% of calls to leasing offices are pricing-and-availability inquiries; leasing inquiries are 20–25% of total call volume; ~49% of calls to leasing offices go unanswered; a reply within 5 minutes makes a lead 21× more likely to convert than one at 30 minutes; 60% of prospect communication is phone, 22% email, 18% text; ~87% of callers who reach voicemail leave no message. The best-sourced number in the set is the NMHC / Grace Hill 2024 Renter Preferences Survey (172,703 respondents across 4,220 communities: in-unit washer/dryer and A/C each a must-have for 93%, high-speed internet 90%).
Where it says our bank is blind. Six categories skew to phone and are therefore under-sampled by a corpus built mostly from written channels: compound conversational pricing questions ("month-to-month vs 12, and does that change the deposit?"); emergency maintenance (flood, no heat, lockout); candor questions ("would you live here?"); neighbourhood and safety questions; callers who hang up rather than leave a voicemail, who become no record at all; and fair-housing accommodation requests, which are typically raised verbally first and only documented afterwards. The Camellia half of the corpus is voice-heavy and partly covers the first two; the rest is a known hole in the bank.
The customer's job is disposition, not authoring, and most facts never reach them at all. Consolidation runs first, unattended; the human sees only what the data cannot settle.
A raw extraction produces hundreds of candidate facts per portfolio (Situs: 112 fact keys × 16 properties, plus every message-mined restatement). Reviewing them one by one is the queue we refuse to build. Five rules, applied by the pipeline with no human in the loop, decide almost all of them:
| # | Rule | Effect |
|---|---|---|
| 1 | Documents and system settings auto-confirm. A fact stated in a lease, a letter template, or an AppFolio setting is CONFIRMED with the document as its evidence. No card. | Late fee, NSF fee, month-to-month premium, renewal credit, insurance charge — settled from the documents pull, never asked. |
| 2 | Message-mined facts auto-confirm above an evidence floor. At least 5 messages from at least 2 staff, no contradicting statement, no contradicting document → CONFIRMED. Below the floor → held as UNCONFIRMED. Clara does not act on it and nobody is asked to confirm it; it surfaces only if a live conversation needs it (section 7). | The "Confirm" queue disappears. Low-evidence facts cost zero review minutes. |
| 3 | Dedup across properties. Values are normalized (amount, unit, wording) and compared. Same at every property where evidenced → one portfolio-level fact, zero property rows. A property gets its own row only where its value differs. A fact seen at one property only is a portfolio fact tagged "seen at", not an override. | One card per fact, never one per property. Sixteen identical late-fee rows become one. |
| 4 | Live state is never a fact. Vacancy, balances, unit status, application status are PMS pointers read at runtime. They are excluded from extraction, so they cannot appear in review. | Removes the largest class of junk from the first Situs build (unit snapshots presented as knowledge). |
| 5 | Missing is not a card. A key with no evidence anywhere goes to the fill list (5b) or the integrations lane (D9). It is never presented as a blank for the customer to type into. | Blank-page authoring never happens; the customer is not asked to write a policy book. |
What is left for a human is exactly one thing: conflicts — staff vs staff, staff vs document, document vs system setting — plus the rare outdated item where a document contradicts a current setting. On the Situs data that is 13 conflict topics against 112 keys. The review is a 20-minute session, not a project. When the queue is longer than a coffee, the pipeline is wrong, not the customer.
Evidence floor and dedup are the same rules the "What Clara knows" page is built from, so the review queue and the brain page are one dataset with two views.
| # | Principle | Source |
|---|---|---|
| 1 | Pre-fill, never blank-page | EliseAI's knowledge setup — prefilled from the property's own website before the customer opens it |
| 2 | Every claim carries its evidence inline — quotes, counts, dates — not a bare score | Glean's citation-over-badge model; Notion Q&A inline citations |
| 3 | Conflicts resolve field-by-field against a named base, not message-by-message | Master-data survivorship (field-level) + git three-way merge |
| 4 | Batch by decision type, keyboard-first for the easy stuff; slow down only for conflicts | Prodigy / Label Studio accept-reject-ignore; Linear Triage single-key actions |
| 5 | Decline or skip must ask why, or offer snooze — never silently discard | Linear Triage: decline requires a reason; snooze preserves state |
| 6 | Ownership and a re-check cadence attach to every accepted item | Guru verification cadence; Confluence stale-page automation |
| 7 | Every accept, reject or edit writes an audit entry — who, when, before, after | Confluence Page Review Manager; Palantir ontology proposals (branch → review → merge) |
| 8 | Corrections are training signal, not one-off fixes | Chatbot escalation-log training (directional); Ramp/Brex pattern learning (marketing-sourced, low confidence) |
| 9 | State explicitly when there isn't enough evidence — never guess and present it as fact | Notion AI's "I couldn't find enough information" behavior. This is NOT ESTABLISHED surfacing in the UI |
| 10 | Watch reviewer behavior for decay (approval rate → 100%, latency → 0) and treat it as a queue-design bug, not a customer failing | Approval-fatigue literature |
Topic-first buries the handful of items that actually need a human inside hundreds that don't. Property-first repeats the same fact sixteen times. The tree is portfolio-first; a property is a filter that shows only where it differs from the default.
Portfolio ├─ Needs You (2 types, badge-counted) │ ├─ Conflict (staff vs staff, staff vs document, document vs setting) — scoped to the properties where it occurs │ └─ Outdated (a document contradicts the current system setting) ├─ Policy Book — portfolio defaults (auto-confirmed, browsable by topic) │ └─ Overrides table: fact × property, only where the value differs └─ Property view (e.g. "Carr Street Flats") = defaults + this property's overrides + its open conflicts
"Confirm" and "Missing" are gone from the human tree on purpose: confirm is decided by the evidence floor, missing goes to the fill list. Intercom Fin's four-way taxonomy (missing / unclear / duplicated / contradictory) still applies, but three of the four are handled by the pipeline.
┌─────────────────────────────────────────────────────────────┐ │ CONFLICT · Late fee grace period [Applies to: ▾] │ │ Willows only ▾ │ │ Staff says the grace period is either 3 days or 5 days. │ │ │ │ Variant A — "3 days" Variant B — "5 days" │ │ ──────────────────── ───────────────────── │ │ "we always give a 3 day "our policy is 5 days, │ │ grace window" we've never enforced │ │ — Erika, text to tenant #4021 day 4" │ │ Aug 14, 2026 · 1 of 6 msgs — Darrin, staff Slack │ │ Jul 2, 2026 · 1 of 2 msgs │ │ │ │ System setting (AppFolio): late_fee_grace_days = 5 │ │ │ │ Recommended: Variant B (matches system, more recent, 3 │ │ independent mentions vs 1) │ │ │ │ [ Keep A ] [ Keep B ✓rec ] [ Something else… ] [ Skip ] │ └─────────────────────────────────────────────────────────────┘
Every card carries: the claim in one line; evidence quotes with speaker, source, date and corroboration depth ("1 of 6 msgs"); a confidence label, not a number ("3 independent mentions"); the system-of-record value where one exists; a recommendation with its reasoning stated; and a scope selector — this property / these properties / whole portfolio — defaulted to the narrowest, so a correction never silently propagates.
Conflicts always show two or three named, attributed variants side by side. Base = the system of record when there is one, otherwise the better-corroborated variant leads. Anonymized conflicts cannot be resolved with judgment.
J/K — next/previous within the current type1/2/3 — pick the choice in that screen positionEnter — accept the recommendation (most Confirm items are single-key)S — skip or snooze; always asks "check again in 7 / 30 / 90 days", never silentSpace — expand full evidence; quotes collapse to one line by default so a scan pass stays fastNeeds You: 6 conflicts · 2 outdated — two separate counters, not one blended progress bar, so the customer always knows which kind of attention is left.Upload triggers scoped re-derivation. A property can upload a lease template, house rules or fee schedule at any point. That runs the same agent pipeline restricted to the claims the document touches, and re-opens only the affected cards. Never a full portfolio re-run. Uploaded documents outrank message-mined evidence and carry an "Official document" badge — but if the document conflicts with what staff actually do, that surfaces as a variant, because the gap between the written policy and the practiced one is itself a finding.
Audit trail. Every disposition writes item id, before-value, after-value, decision type, reviewer, timestamp, scope, and whether the customer took the recommendation or overrode it. Browsable per property, filterable to overrides only — the fastest way to spot a review decaying into rubber-stamping.
End state: one policy book for the portfolio, with a per-property override table. Every resolved item organized by topic, each showing its evidence, its resolution, who confirmed it and when, and its next re-check date. That is what Clara reads. Nothing enters it without a disposition; skipped items stay in Needs You until they are resolved or their snooze expires.
Same four decision types, same evidence-and-recommendation structure. Different order and phrasing, because a call can't show a side-by-side card.
D1 · Where the playbook lives
clara-onboarding repo with per-PMS adapters. Cleanest concept, but it duplicates the shared-infrastructure package that ADR-0013 already commissioned, strands the onboarding wizard in a third repo, and there is no second PMS adapter in code to justify it — Yardi and RealPage are knowledge bases, not adapters.packages/browser-core boundary; the playbook (window rule, policy mining, fact assembly, replay, config audit, outputs, review UI) becomes an onboarding module in the product.Why: the consumer of every playbook output is Clara's property config, which lives in the product — where the onboarding wizard, credential delegation and property import already ship. And auditing our own Camellia assumptions is inherently a product-repo job, because it reads that repo's own prompts. No new repo; revisit only when a real Yardi or RealPage adapter exists in code.
D2 · Scope of the standard pull
D3 · What gets loaded into Clara by default
D4 · Review UX, first version
D5 · Documents
D6 · Data use — resident messages as eval material
What we did last night: internal replay only, deterministically scrubbed across the whole dump through a hand-verified substitution table, in-process prompt replay with no send path. Nothing was sent to anyone and nothing left our machines. The data package is PII-marked and owner-only.
D7 · The turnaround promise in the pitch
Say "overnight" and deliver in a few hours.
D8 · One PMS login per client, or the shared PropFlow login
Sessions are per database, and a clean test on Sep 2 (fresh profile, second login, both sessions probed at 10, 60 and 180 s) showed a second login does not invalidate the first; the Sep 1 collision came from cloned browser profiles sharing one cookie jar. So this decision is about lockout, audit and revocation, not session collisions. Becomes stage 0 of the playbook: the client creates the user before the overnight run.
D9 · Onboarding order: integrations before documents
Two measurements landed on Sep 2 and they point the same way. Replaying 1,625 real Situs turns with a full mined knowledge base loaded moved the acceptable rate from 56% to 56% — a null result inside a measured 0–3 point noise floor. And the question bank, counted from 5,465 real inbound turns, finds 64% of question volume needs live PMS data, not a static fact sheet. The single largest gap is the live tour calendar, which alone blocks 598 inbound messages and has no AppFolio field at all.
The null result is not an argument against the knowledge base — missing-fact still fell 33% → 30% and 105 rows were fixed by it, and it caught Clara inventing unit availability that run 1 could not see. It is an argument about order: the documents pay off, but only once the live feeds are underneath them.
Proposed — pending Fede's review · 2026-09-15
Western Slope finished the in-product review this morning: 26 of 26 items answered by Jay — 6 corrected, 4 open/conflict questions answered, 16 confirmed.
Correction 2026-09-15: the apply step merged with a write target Fede had not confirmed (anchor property). Nothing was written anywhere. Re-targeting to company + building is the next PR chain; the walker PR was stopped before opening.
load-knowledge-base.ts (creates or reseeds a review from a fact sweep or a knowledge draft); export-knowledge-base.ts (a read-only dump of answers to JSON); PR #8650 merged 2026-09-15 (apply step, dry-run default); PR #8656 open, checks being fixed.Fede, 2026-09-15: onboarding should scale to people self-signing up, with no engineer running steps by hand. Five moments:
npm run knowledge:verify-live -- --gold <gold-set.json> --property <front-door-property-id> against production; it must print 0 missing, and that output is the tracker row's evidence. Then place one bench call per property exception on phone, text and email (scenarios knowledge-exception-* in scripts/voice-harness/scenarios/) and confirm Clara answers from the exception, not the company rule.1 · Kick-off, on AppFolio access (build first alongside the apply step — Western Slope needs the apply half by Thu Sep 17; kick-off targets the next new company)
When a company's AppFolio credentials are stored (wizard finish) and the Clara user exists, a job runs the discovery phases (units, listings, guest cards, maintenance, lease templates) by deterministic API script with the browser agent as fallback, builds the operations map and the Camellia-shaped knowledge draft, and creates the review in "draft" status with a link and password. First: draft held until Fede has read it and sends the email. Once two companies have gone through cleanly: sent automatically, Fede on copy.
2 · Apply step (this week — needed for Western Slope go-live Thu Sep 17)
A script/job apply-knowledge-review reads a finished review and writes knowledge sections: Correct → the proposed text becomes the section; Wrong + correction → the correction, rewritten in Clara's voice, replaces it; Answered → the answer fills the open question. Company-wide policy goes to org-level knowledge, a fact about one home/building goes to that building (Gera's portfolio model). Each section records who answered and when. Idempotent, re-runnable, prints a before/after diff.
Items that need judgment are held out and listed, never written silently:
The first run is reviewed by Fede as a before/after page; later runs go straight in and post the diff to #updates-fede.
3 · Close the loop
When every item has a verdict the review flips to done (today status stays "open" forever), the apply step runs, and a one-line summary with the held-out items goes to #updates-fede. Held-out items that need the client's answer become one short follow-up back to the client (sent by Fede).
D1 · Where the apply step writes DECIDED 2026-09-15
D2 · First-run gate DECIDED 2026-09-15
D3 · Kick-off gate for new companies
D4 · Per-client Clara AppFolio login
Proposed — pending Fede's review (2026-09-15)
| Date | Fede's words (shortened) | What it means for the playbook |
|---|---|---|
| Sep 5 | Western Slope up fast on infra we have; Situs can take longer | New clients don't all move at the same speed — the playbook has to work for a fast, simple client and a slower, harder one |
| Sep 7 | "we should just call APIs" — no Playwright unless there's no API | Raw pull is a deterministic script; browser agent only where no API exists (stage 3) |
| Sep 7 | Situs and Western Slope "are separate companies" | No cross-client knowledge sharing, ever — separate orgs, separate knowledge bases |
| Sep 9 | "wtf i never asked for a suggested link feature" — autonomous, no merge buttons ever | The knowledge build never asks a human to click merge/confirm on routine matches — it decides, and a wrong decision gets fixed in the engine |
| Sep 8 | "this is an insane slop ball" — split policy / listing fact / internal-only / settings | Every knowledge draft uses Camellia's shape and that four-way split, not a flat question list |
| Sep 8 | "we just collect free-form, and when we inject it into the system, we decide how we structure" | Clients answer in plain language; PropFlow's build step does the structuring |
| Sep 7 | "since we're adding customers in the back office, we should be setting the PMS" not asking the client | PMS choice is a PropFlow staff step, never a client-facing wizard question |
| Sep 12 | Bulk first-time import is a different job from day-to-day polling | Raw pull is a one-time onboarding job, not the same code path that keeps a live client's data fresh |
| Sep 16 | Import window (Fede, 2026-09-16 evening — in progress) | The import keeps only guest cards and applications received in the last 30 days by AppFolio's own date, open or closed; rows without a date are dropped. The every-minute sync creates no new record older than 30 days either, but keeps updating records it already has. Before this, open records were kept at any age: Western Slope's first pull brought 1,065 guest cards (762 older than 90 days) and 145 applications (110 older than 90 days); those are being cleaned up. |
| Sep 11 | "I told you to do a full end to end QA" — a partial pass isn't QA | Every stage needs a real end-to-end proof (a live review answered, a live email sent) before it counts as done |
| Sep 11 | Clara never quotes income limits, decides eligibility, or states a blanket "no assistance animals" rule | Held-out rule: anything that touches fair housing goes to a human, never auto-written into knowledge |
| Sep 15 | Questionnaire goes to hello@propflowai.co first, for us to check, before the client sees it | Today's priority #1 — see section 13 |
| Sep 15 | Client self-signs up, connects AppFolio, and adds Clara as a user in their own AppFolio | One Clara login per client (decided 2026-09-08) — the wizard, not a PropFlow engineer, collects it |
scripts/knowledge/load-knowledge-gold-set.ts places exceptions on the property row and refuses to finish with any unplaced; knowledge:verify-live diffs what Clara reads against the approved file; the onboarding harness (scripts/onboarding-harness/knowledge.ts) runs create → load → verify red-before/green-after on a scratch org. "Reviewed" is never "loaded"; only the verify output counts.Every row below happened between Sep 7 and Sep 17 while standing up one new client. The rule column is what the next onboarding does instead; the last column is where the rule is enforced, so it survives this page.
| Symptom — what the client or Fede saw | Root cause | Prevention rule | Where it lives now |
|---|---|---|---|
| On a live call Clara said a lockbox-only home shows in person. | Only the company-level facts loaded; the seven per-home exceptions had nowhere to go, and nothing compared what Clara reads to the approved file. | Load every approved fact or refuse to finish, then diff what Clara reads against the approved file before "knowledge reviewed" counts. | Checklist step "knowledge live"; loader refuses unplaced facts; knowledge:verify-live |
| A test call got the old draft office hours after the client's answers were approved. | The knowledge write had not propagated, and hours were also answered from a stale structured field nobody read back. | One structured field is the source of truth for hours; the spoken sentence renders from it, and it is read back live after every knowledge write. | Checklist step "read back hours"; officeHours on org and property; platform default 9–5 |
| Evening and weekend tour requests were refused as if the office were closed. | Tour slots read reception hours instead of the hours the client will actually show homes. | Touring hours are their own setting, separate from office hours, falling back to office hours only when unset. | Setting tour.hours; checklist reads both back |
| Staff tour emails silently stopped mid-morning on the client's live test day. | A change put staff notices behind the company-live switch; the company row had no live value, so every non-PropFlow recipient was suppressed. | The company-live switch holds prospect outbound only. Staff and team notices are never held by it. | One outbound-email seam, one audience decision; checklist proves a booking end to end |
| Sessions reported "staff email is on" for days while the client received nothing. | The evidence used was a config resolver's answer and a delivery to a test property, not a delivery to the client. | "Email on" means a delivered event to the client's real address with a timestamp and a message id — never a resolver dry-run or a row field. | Checklist step "one robot booking → delivered event" |
| The client's whole delivery log was empty for the entire onboarding. | A shadow/redirect switch left over from setup had been rerouting every staff notice away from the client since day one. | No shadow, redirect or test-mode switch may exist on outbound staff email; delete them rather than gate them. | Shadow-mode reads deleted; checklist reads the delivery log filtered to the client's domain |
| Team tour notices landed in a PropFlow engineer's smoke mailbox. | The front-door property's notice address was still the setup test mailbox, and a wrong "never mail the company's own inbox" rule blocked the correction. | Every notification and escalation address points at the client before the first live call; the client's own connected mailbox is a valid recipient. | Checklist step "notice addresses read back"; property-level staff-notice recipient setter |
| Tour notices for 69 of 70 homes reached nobody. | Recipients resolved only from a per-property email address that scattered single-family homes never have. | Notice recipients fall back to the company's escalation contact; no home may end go-live without a resolved recipient. | Org fallback shipped; checklist asserts a recipient for every property |
| 100 "new application to review" emails hit the client's inbox in one hour. | The first import brought years-old guest cards and applications, and an hourly reminder job had no age cutoff. | Import and sync only create records dated within the last 30 days, and any reminder backstop is opt-in and off by default. | 30-day import window and sync cap; reminders opt-in |
| Repair callers were handled by Clara instead of the client's own call center. | The forward rule lost to the "we know this caller" rule as soon as caller recognition filled in a name. | The client's own intake route outranks caller recognition, and a forward is a plain transfer — never an automated key-press through someone else's phone tree. | Setting maintenance.intake_route; prompt fence plus a live replay before merge |
| Every call on every customer's line failed for nearly two hours. | A dynamic field in a phone tool resolved to an empty string, and the voice vendor rejects the entire tool set when one required field is empty. | No dynamic voice field may ever resolve to an empty value; the dark path is asserted by a test, and a voice regression is reverted first and re-landed second. | Test: the dark state is a real call, no tool field may hold an empty variable |
| A call to the client's own number reached Clara with no property context at all. | A company-scoped routing record short-circuited the property lookup instead of falling through to the property map. | Only a property-scoped routing record may short-circuit a lookup; anything company-scoped falls through. | Checklist step "bench call on the real number reads back org, property and homes" |
| Clara quoted an invented "deposit is one month's rent" rule and was wrong on most homes. | A portfolio-wide sentence written into knowledge outranked each home's own published figure. | Per-home figures come from the listing; knowledge holds only the rule and never a figure that varies by home. | Knowledge-writer rules; graded bench calls per home |
| The client's shared leasing mailbox could not be connected — three failed attempts over days. | The Clara account had been added to the mailbox without Full Access and Send As. | Mailbox permissions are granted and proven by one read and one send before the connect step, not discovered during it. | Checklist step "mailbox connect proof" |
| Not one lead email ever produced a guest card. | All of the client's lead mail was listing-site relay mail; the PMS's own guest-card notification had never been switched on at the client. | Clara replies only to guest cards that come from the PMS, and switching on the PMS's guest-card notification is a named setup step at the client. | Checklist step "PMS guest-card notification on" |
| Nothing about the client — calls, tours, digests — ever reached the team's Slack channel. | No activity channel was set, and once it was, the app addressed the channel by name and the channel got renamed. | Register the activity channel by its permanent id, and prove it with one call that posts. | Checklist step "one call posts to the channel" |
| A tour Clara confirmed on the phone never appeared on the leasing agent's calendar. | A stale pinned calendar connection returned an empty token and nothing retried, refused or alerted. | A calendar write that cannot resolve a live connection fails loudly, and every calendar-affecting write is verified through the calendar's own API, never from our row alone. | Dead-connection fallback with an alert; checklist verifies the booked event |
| Back-to-back test calls greeted the caller with the previous persona's name, and a repair note leaked into a leasing email. | Every bench persona dialed from one number, so caller recognition merged them; a shared free-text note field is written by unrelated parts of the product. | One caller id per test persona, never a real person's number or mailbox as a test identity, and no shared free-text field carried between unrelated flows. | Per-persona bench numbers; stale-note guard, note field being retired |
| Twelve empty tour records appeared in production. | An update path wrote a placeholder row when the tour it was updating did not exist. | An update never creates: a write against a record that is not there fails instead of minting a stub. | Upsert guard on the tour writer |
| Nothing PropFlow decides could be written back into the client's PMS. | The browser robot's account list for the new company was drafted and never applied, and the front-door property has no PMS property behind it to write to. | Before any write-back is armed, the robot account is applied and proven with one login, and every front-door property has a PMS mapping or an explicit no-write rule. | Checklist steps "robot login proof" and "front-door mapping" |
Pull requests behind these rules: 7919, 7962, 8007, 8043, 8060, 8314, 8375, 8378, 8458, 8553, 8606, 8638, 8704, 8726, 8750, 8844, 8861, 8882, 8893, 8900, 8912, 8913, 8923, 8927, 8934, 8958, 8962, 8964, 8968, 8969, 8970, 8973, 8977, 8978, 8979, 8980, 8986, 9013, 9031, 9036, 9046, 9047, 9098, 9103, 9105, 9113, 9135, 9137, 9139, 9140, 9144, 9148, 9155.
Proposed — pending Fede's review (2026-09-15)
| Stage | What happens | Owner today | Once automated | Proof it's done |
|---|---|---|---|---|
| Proposal | Client agrees to onboard; a rollout-timeline page is sent | Fede | Fede (sales stays human) | Proposal page sent, client replies |
| AppFolio access + Clara login | Client connects AppFolio and adds Clara as a user in their own AppFolio (one login per client) | Client, with Fede walking them through it | A wizard step | Credentials stored; Clara's user confirmed in the client's AppFolio |
| Raw data pull | Units, listings, guest cards, maintenance, lease templates, documents pulled | Ad hoc script, by hand | Deterministic API script, browser-agent fallback only where there's no API | Pull manifest with a gaps[] list, no writes |
| Mailbox + calendar connect, mailbox audit | Leasing mailbox and calendar connected, with a scripted audit of the inbox. Owned by the shared-mailbox lane — see the go-live tracker row ws-shared-mailbox-access-gap-2026-09-15 (docs.propflowai.co/a/western-slope-go-live) and the design section docs.propflowai.co/a/email-architecture-2026-08-14#inbound-lead-flow-2026-09-15. | By hand, per client | Same wizard step as AppFolio; audit runs automatically | The audit section on the client's go-live page — see Western Slope's leasing inbox audit |
| Operations map + knowledge draft | Camellia shape, 12–20 leasing-only items, split into company policy / building fact / internal-only / settings | Agents, hand-assembled and hand-reviewed | The same job that ran the raw pull | Draft matches the Camellia-shape item count and split |
| Questionnaire review created + sent | Review goes to hello@propflowai.co first for us to check, then to the client | Not built yet — today's #1 priority | Automatic after two clean company runs | Email at hello@, then at the client's inbox |
| Client answers + "I'm done" | Client works through the review, then submits | Client | Client (this stays human) | "Done" status + submitted event fires |
| Automated knowledge build | Answers become Clara's knowledge, org-level or per-property per the client's setup | A person hand-edits the record from an export | The event-driven consumer job (section 13c) | Before/after diff, held-out items listed, never silent |
| Verify Clara reads the approved knowledge | Run the verify command against the approved file (renders exactly what Clara reads, diffs it against the gold set). It must be green: every approved fact present, every home exception tied to its home. Then one bench call per exception, on phone, text and email, answered from the exception, not the company rule. "Knowledge reviewed" isn't done until this step is green. | Not built yet — loader shipped 2026-09-16, verify command in flight | Runs automatically after every knowledge build | Verify output pasted on the go-live tracker row as prod evidence |
| Hooked-up tests | Basic checks that the build actually wired in and Clara can answer from it | Not built yet | A smoke run after every knowledge build | Pass/fail posted to #updates-fede |
| Dark rollout → Willows-style dry runs → go-live | Feature ships off; dry runs at a test property; Fede turns it on for the real client | Fede, per the standard rollout playbook | Same — this step stays Fede's call | Go-live tracker row moves from untested to proven |
Every step below exists because Western Slope's go-live hit the matching failure in section 11. A step is done when its proof artifact is pasted on the client's go-live tracker row — not when the code merged.
| # | Checklist step | Proof |
|---|---|---|
| 1 | The client's PMS access and the browser robot account for their database are applied, not drafted. | One successful robot login against the client's database, timestamped |
| 2 | Every front-door property has a PMS property behind it, or an explicit rule saying nothing is written back. | The mapping read back per property, or the no-write rule on the tracker row |
| 3 | The client switches on their PMS's own guest-card email notification. | One guest card arriving in the connected mailbox |
| 4 | The leasing mailbox is connected with Full Access and Send As already granted. | One read and one send through the connection |
| 5 | The calendar is connected and a booked event is verified through the calendar's own API. | Bench booking, then event found; cancel, then event gone |
| 6 | Approved knowledge is loaded, company facts and per-home exceptions both. | knowledge:verify-live against the approved file — zero missing, zero unplaced |
| 7 | One bench contact per home exception, on phone, text and email, answered from the exception. | Three transcripts per exception |
| 8 | Office hours and touring hours are both read back from the live record. | officeHours and tour.hours printed for org and property |
| 9 | Every property resolves a staff-notice recipient, and every notice and escalation address is the client's, never a PropFlow test mailbox. | Resolved recipient listed per property, all on the client's domain |
| 10 | One robot booking produces a real staff email at the client. | A delivered event to the client's staff mailbox — timestamp and message id |
| 11 | No shadow, redirect or test-mode switch exists on the client's outbound email. | The delivery log for the client's domain, non-empty, with no redirected recipients |
| 12 | The company-live switch holds prospect outbound only; staff notices go out regardless. | One held prospect message and one delivered staff notice, same day |
| 13 | A bench call on the client's real number reads back the company, the property and the homes. | Call context printed, no empty fields |
| 14 | Repair calls leave on the client's own intake route, including for a caller Clara recognises. | One recognised-caller repair call forwarded, no work order created |
| 15 | No voice tool field resolves to an empty value on the dark path. | The empty-variable test green on the client's agent |
| 16 | The import brought only records dated in the last 30 days, and reminder backstops are off. | Import count by age; reminder setting read back as off |
| 17 | The team's activity channel is registered by permanent id. | One call posting into the channel |
| 18 | Every bench persona dials from its own number, and no test identity is a real person's number or mailbox. | The persona-to-number list used for the go-live round |
Start here: this page. The go-live tracker (docs.propflowai.co/a/western-slope-go-live) is the status board — every stage above should have a row there once it's wired for a client. The scripts to know by name: the appfolio-onboard tool/skill for the raw pull, load-knowledge-base.ts and export-knowledge-base.ts for the review record, and apply-knowledge-review (PR #8650) for the knowledge build. No new doc page — updates to this playbook go here.
Proposed — pending Fede's review (2026-09-15)
The audit's output feeds knowledge, shared parsing code, and a client ask. Owned by the shared-mailbox lane — see the go-live tracker row ws-shared-mailbox-access-gap-2026-09-15 (docs.propflowai.co/a/western-slope-go-live) and the design section docs.propflowai.co/a/email-architecture-2026-08-14#inbound-lead-flow-2026-09-15.
Proposed — pending Fede's review (2026-09-15)
Situs Group is next: 645-ish units, no AppFolio API access (browser-only), full 2025/2026 raw dumps already sitting in ~/situs-data, no org/property in prod yet, shared AppFolio login with JP&Co today. Ranked in Fede's stated priority order, each a small dark PR (≤300 lines):
(a) Questionnaire auto-generated from the raw pull, emailed to hello@ first — highest priority
Reuses: the Camellia-shape draft format, load-knowledge-base.ts, the review page/gate. Missing: the job that turns a finished raw pull into an operations map and knowledge draft on its own, and the "create review + email hello@" step. Proof: a review auto-created in draft status for Situs, and the email lands at hello@propflowai.co.
PR chain (scoped 2026-09-15, building):
Engineering choices taken on the lane's recommendation (internal, no customer effect): the draft is a file fed to the existing loader, not a new table; script first, Lambda only if the automatic trigger needs it; the customer-facing "we're setting you up, you'll hear from us in a few hours" message ships with PR 4, not before.
(b) "I'm done" submit + event — in flight (second PR being built)
Reuses: the review page's answers API. Missing: the PR itself — done status, submitted event, internal email to hello@. Proof: a test review flips to done, the event fires, hello@ gets the email.
(c) Consumer job that builds the knowledge base from the event
Reuses: PR #8650 (apply a finished review to the knowledge base, dry-run default, held-out rules). Missing: wiring it to fire on the submitted event instead of being run by hand. Apply runs directly from the submit click, writing company + building per Gera's model; depends on the company-level read path going live first. Proof: the dry-run diff appears automatically on a test review's "done" event and matches a hand-run apply.
(d) Hooked-up smoke tests
Reuses: the existing smoke-test pattern; the question-bank coverage check from section 5b as the shape. Missing: a per-org check that a knowledge build landed on the right org/property and Clara can answer a couple of P0 questions from it. Proof: a pass/fail line posted to #updates-fede right after a build.
(e) Per-client Clara AppFolio login + wizard step
Reuses: D4 in section 8 (already recommended), Western Slope's own dedicated login as the template. Missing: the wizard step, and a Clara login for Situs separate from the JP&Co one it shares today. Proof: Situs has its own Clara user, added by the client, confirmed on their AppFolio account page.
From the mailbox audit (Western Slope, also needed for Situs) — owned by the shared-mailbox lane. See the go-live tracker row ws-shared-mailbox-access-gap-2026-09-15 (docs.propflowai.co/a/western-slope-go-live) and the design section docs.propflowai.co/a/email-architecture-2026-08-14#inbound-lead-flow-2026-09-15.
Proposed — pending Fede's review (2026-09-15)
Not now: Clara interviewing the team (long-term only, not this build); sending the questionnaire to the client fully automatically — that stays gated on Fede's check until two company runs have gone cleanly through hello@ first.
Proposed — pending Fede's review · 2026-09-15
Goal: a new client's answers about how they operate become the facts Clara speaks from, at the company level and per building, without anyone at PropFlow retyping them.
Today: a customer-facing document upload already exists — the property page's Knowledge Upload modal (KnowledgeUploadModal.tsx) posts a PDF/text file to /api/properties/[id]/knowledge/ingest, which extracts discrete knowledge sections with an LLM and saves them to the property's DynamoDB knowledge row — so the upload step is existing, not to be designed, and the draft generator reads documents from that knowledge base store, not from a mailbox.
Proposed — pending Fede's review (2026-09-15)
Fede's ruling, in his words: asked whether the system should check coverage after the draft, he said "no i think we should know what we need before hand" and "obvious things like that… should just be part of the core checklist."
Meaning: a leasing knowledge base has a fixed list of must-haves, defined before any client is looked at. The pull fills what it can and marks each item found / conflicting / missing. The questionnaire is derived from the gaps and cannot skip a must-have. Done means every must-have is filled.
Why: Western Slope's 26-item review never asked for the application link, because no list existed. The review was built from what the data offered, not from what a prospect needs.
Proposed — pending Fede's review (2026-09-15)
| # | Must-have | Scope | Status |
|---|---|---|---|
| 1 | Office hours | company | Conflict (record Mon–Thu 9–4; client Tue–Thu 1–3 walk-ins; Friday contradicts itself) |
| 2 | Phone, email, address | company | Filled |
| 3 | Apply link | listing | Empty |
| 4 | Application fee | company + exceptions | Conflict (record $27, past charges $32, client $40; Courtyard/Lincoln $0 vs $40) |
| 5 | Decision time | company | Filled (1–2 business days) |
| 6 | Who applies, ID + income documents | company | Filled |
| 7 | Income requirement | company | Filled (~2x rent) |
| 8 | Portable screening report | company | Filled |
| 9 | Co-signers | company | Filled |
| 10 | Vouchers + affordable units | company + 2 buildings | Partly (restriction item answered with a fee) |
| 11 | Security deposit | listing | Filled (per listing, refundable, 60 days) |
| 12 | Pet rules + deposit | company + exceptions | Filled; 632 Hill exception held out (fair housing) |
| 13 | Assistance animals | company | Filled, overrides client wording |
| 14 | Lease terms | company + Ember exception | Filled (Ember 14–18 month promo) |
| 15 | Month-to-month notice | company | Filled (30 days, +$150) |
| 16 | Utilities | listing | Per unit, not policy |
| 17 | Renter's insurance | company | Filled ($100k; $10.50 fallback) |
| 18 | Smoking | company + 2 exceptions | Filled |
| 19 | Tour method | company + 2 exceptions | Filled |
| 20 | Tour booking link/hours | company | Empty |
| 21 | Move-in payment method | company | Empty |
| 22 | Key handoff | company + 3 exceptions | Filled |
| 23 | After-approval steps | company | Filled |
| 24 | Specials/concessions | listing | Empty as policy |
| 25 | Parking/laundry/amenities | listing | Listing fields |
| 26 | What Clara hands to a person | company | Empty as a fact |
Totals: 15 filled, 4 conflicting, 5 empty, 2 listing-only.
Pull + audit → candidate facts with source labels → 12–20 item draft in the Camellia shape (company policy vs building fact; internal-only and settings excluded) → hello@propflowai.co check → link + password to the client → answers → "I'm done" → apply: company policy to the company tier, building facts to that building, provenance (who/when), three hold-outs (fair-housing conflict, answer not matching its question, "depends per unit") → before/after summary with held-out list to hello@ → Clara's four readers (text, email, voice policy variable, mid-call lookup) pick it up on the next contact.
| Decision | Option | Notes |
|---|---|---|
| D5 · Corrections DECIDED 2026-09-15 | Never verbatim. Every client answer is read through one lens — Fede: 'should we say this to a prospect directly, or is this language a business-to-business explanation of how the policy works behind the scenes' — and split into up to three parts: (1) Say to prospects: the public fact in Clara's voice; (2) Clara knows, never says: operating context that shapes behaviour (what to steer toward, when to hand off, what not to promise); (3) Not knowledge: a question back to the client, or a setting. Prospect lens also applies to our own proposed text (Fede: "that should include Clara's own answers"). | |
| D6 · Re-apply vs staff edits | A) RECOMMENDED — staff edits always win, the review is a seed, not a re-provisioning step. | Staff own the live knowledge base. Re-apply is safe. |
| B) The review overwrites on every re-apply. | Staff corrections get clobbered; re-apply becomes rare and defensive. | |
| D7 · Company tier at a real client | A) RECOMMENDED — first write only after Fede reads the before/after summary on hello@. | Prevents one wrong company answer from reaching every home on day one. Scales: second run goes straight in, diff posted to #updates-fede. |
| B) The hello@ check is enough, write immediately. | Faster, but no backstop for an error that touches every property. |
Jay's answer: "Our office is closed Monday and Friday. Tuesday–Friday 1–3pm our doors are open to the public, but we encourage prospects to call us instead to receive assistance. We also accept physical appointments."
Proposed — pending Fede's review (2026-09-15)
Not decided here: the generator prompt, the harness, the automatic trigger. No code until Fede settles the above.
Proposed — pending Fede's review (2026-09-15)
Fede reviewed the first machine-drafted knowledge base for Western Slope entry by entry. Every rule below is a mistake the draft made. The script, the draft generator and the review page follow them from now on. Result: Western Slope knowledge base.
Mined 2026-09-15 from every session since August: five of the rules above had already been said before today (tiers 4 sessions, automation 4, short 3, trust the client's answer 3, listing data 2). Full hit list in the session scratchpad kb-corrections-history.md.
The test that keeps these rules alive: western-slope-knowledge-gold-set.json holds Jay's 26 raw answers and the 24 entries Fede approved. The first draft-generator PR must reproduce those entries from those answers (topic, tier, and every figure) before it merges; a rule without that check is just prose.
Verbosity budget still applies on top (Aug 29 knowledge-layer): voice one figure and one date per turn; text answer plus figure plus link; email full.
Documented — not built (2026-09-15)
Ran the flow once, headless, at Willows (property 45, jpco.appfolio.com), to document it — not to automate it. Guest card action row: Rental Application ▾ → Send Rental Application (AppFolio's own help docs still call this "Email Rental Application" — the label changed). Opens a "Send Rental Application by Email" modal: Unit or Campaign (searchable, blank by default, not pre-filled from the guest card's existing interest), Recipients (pre-filled with the guest card's email), optional Bcc and SMS checkbox, and a pre-filled Message. Submits to POST /guest_cards/{id}/email_rental_application, confirmed by a 204 response and an "Email Rental Application Sent" line in the guest card's own audit log.
interest_prefixed_id, a unit-level id (u_<id>) — more granular than the property. It comes from a live text search inside the modal, not from the guest card's own interest.u_<id> — the search matches free text (address, unit number, rent), not a stable key.Matches rule 12 above: no generic per-home apply link is exposed here either — AppFolio builds and sends it, and it never appears in the admin UI or any captured request.
Built and merged 2026-09-16 — dry run pending
The rule Fede set today. A new client connects their mailbox, calendar and AppFolio and everything reads and records from that moment, with no flags. Two front doors bring people to Clara: the phone line and the leasing mailbox. Nobody gets Clara's text number any other way, so texting needs no gate. Voice goes live when the client forwards their number. Email goes live when we flip the company's live switch, which holds every email Clara would send for that company until then. That is the whole go-live: one flag on our side, one call-forward on theirs.
| Front door | Today | Matches the rule? |
|---|---|---|
| Email reading (mailbox connected) | Push mailboxes read immediately at connect. Shared mailboxes that Microsoft refuses push on fall to the pull reader, which is behind two global switches: a disabled schedule and an environment flag. With those off the mailbox is never even classified as pull. | Yes Western Slope's leasing mailbox is on the push (webhook) lane and is read today: 82 ingestions in the 3 days before 2026-09-16. |
| Email replying | The company live switch exists (field on the company, set only through an admin API call, no button, on purpose). It holds two things: the first reply to a guest card from AppFolio or a listing site, and the follow-up cadence for those leads. A human who emails the mailbox directly, and a website lead arriving by email as a human message, are still answered while the company is not live. This is the narrow version kept after the 2026-09-11 revert of the "hold the whole inbox" PR. | Partly The switch is the right one. Its reach is too narrow for today's rule. |
| Voice | No live gate in our code. The line answers, books, texts confirmations. But a company's number is not something an operator connects: it must be added to a checked-in registry file in the repo and bound to a property by staff through the admin API. Neither Fairhaven nor Western Slope has a line attached through the company attachment model; Western Slope's front-door number is mapped by that registry file. | Yes Wiring a number is an engineering onboarding step; not on the go-live path. |
| Text | No live gate. Cadence texts for guest-card leads are held only because the cadence itself is held by the live switch. | Yes |
| Calendar | Company calendar connects with the mailbox in one consent and tours land on it. Booking on individual agents' calendars is a per-company setting, off by default. | Yes for the company calendar. |
The company's own settings blob at Western Slope also carries copies of the global email and SMS switches set to off. Nothing reads those copies; the real switches are global and on. Harmless, but confusing, and worth deleting in the same chain.
Fede, 2026-09-16 evening — in progress
Reminder emails to the property manager (e.g. "New application to review") are a per-company setting, off for everyone by default. Turning them on means naming the email that gets them. No new client gets reminders unless they opt in. Camellia (JP&Co) is opted in with its current recipients so nothing changes there. Origin: on 2026-09-16 the hourly reminder sent 100 emails to Western Slope's leasing inbox, one per open application imported at onboarding. The company live switch does not gate these (Fede rejected that).
Each PR ships dark, is proven on Fairhaven (sandbox tenant) and Willows, and flipping Western Slope live stays Fede's press. All merged 2026-09-16 (page switch #8889, Zillow suppress #8896, gate #8893, reader #8888); the email gate covers every customer-facing door: the two send seams, the inbound Lambda, the prospect-touch door used by tours and follow-ups, and vendor dispatch. Team mail is never held. Reader proven in prod 22:32Z (schedule on from config, poller ticking, Western Slope stamped push). Gate dry run at Fairhaven pending a neutral-domain prospect inbox.
| Question | Options | Recommendation |
|---|---|---|
| D0. What the switch holds DECIDED 2026-09-16 | (A) Everything customer-facing: no email leaves Clara for a dark company. (B) Portfolio-arrived leads only (as built). (C) B plus website form leads. | A, chosen by Fede. The client's staff still answer their own inbox as before, so no prospect goes unanswered; Clara reads, records and sends nothing. To try the email door before go-live, flip the switch for the test or use the phone door. |
| D1. Does the master email gate also hold Clara's emails to the client's own team (escalation forwards, PM reminders, owner reports)? | (a) Hold everything Clara emails for that company. (b) Hold only prospect, applicant and tenant email; team notices still go. | (b). The client testing their own line should see every notice, per the 2026-09-13 rule. The risk being guarded is a stranger getting a reply, not staff. |
| D2. Auto-replies from the mailbox itself (out-of-office style acknowledgements), if any: held too? | (a) Held. (b) Allowed. | (a). "No outbound comms from Clara until turned on" reads as all of it. |
| D3 · PM reminders opt-in DECIDED 2026-09-16 | (a) Off by default per company, opt-in names the recipient email. (b) On by default for every company. | (a). Camellia (JP&Co) opted in with its current recipients; no new client gets reminders unless they opt in. See "PM reminders are opt-in" above. |
Evidence: production reads of both companies' profiles and attachments (2026-09-16); code paths company-mailbox-poll-flag, org-mailbox-inbound, inquiry-outbound-hold, outreach-trigger, the inbound-processor send gate, clara-live admin route, phone-lookup and phone-registry. Full file list in the session notes.
Diagnosis + design rule — follow-up to the 2026-09-17 Western Slope weekend-tour incident (go-live audit, section 11b)
Hours ended up in five places, three values, because nothing writes hours in one spot. tourHours and officeHours (both structured) are set by staff scripts. Separately, the 09-15 knowledge pass hand-typed a sentence off the client's website into a knowledge entry ("Closed weekends and holidays.") and got it approved into the gold set — nothing compares that sentence to the setting that already existed. Two independent writers, same fact, never reconciled; only a live call surfaced the disagreement.
tourHours (property overrides org). (B) business/office hours, separate: officeHours. Never merge.Fail the knowledge-base build/apply step when an entry's text contains hour-like patterns (day names, time ranges, "closed", "by appointment") for a field with a structured setting on the same org/property — same shape as the provisioning-script guard already added for officeHours (#9188/#9214), generalized to knowledge prose and to tourHours. Not built; named as the open item on the go-live audit.
~/situs-data/onboarding/PLAYBOOK-AUDIT.md (forensic audit, timeline, playbook v1, entity inventory, missing documents, repo options) ·
~/situs-data/onboarding/UX-RESEARCH.md (patterns, principles, UX spec, wireframes, voice mapping) ·
~/situs-data/analysis/CAMELLIA-ASSUMPTIONS-AUDIT.md (go-live blockers) ·
~/situs-data/analysis/POLICY-LEDGER.md · ~/situs-data/analysis/replay/RESULTS.md, RESULTS-KB.md (run 2) ·
~/situs-data/onboarding/question-bank/ (README.md, question_bank.csv, fact_keys.csv, coverage.csv, coverage_fill_list.csv, RESEARCH.md) ·
~/situs-data/analysis/memo-corpus-for-clara.md · ~/situs-data/analysis/WINDOW-AUDIT.md ·
~/situs-data/STATUS.md, FLEET-BRIEF-2026.md, situs-dump-2025/RUN-LOG.md, the run manifests ·
~/.claude/propflow-vision-2026-08.txt ·
Situs Group initiative hub ·
Situs Group — What the Records Show (2026)