If you read one thing
You own it now. Fede handed you the whole area on today's call: "I think you can take it over."
Live: Western Slope's text door has been on since 7:46 AM Denver today. Its inbox copy has been on since Oct 16.
No lead has gone through the door yet: zero door sends at 19:28Z.
Budget: 1,896 of 4,000 AppFolio requests used at Western Slope by 19:27Z.
Three decisions wait on you: what Clara does after staff text a lead, the rollback shape, and whether a per-module gate sits on top of today's split (section 9).
Where to look: rollback in section 10, every link in section 13.
How this page was built, and what "unverified" means here
| Source | What it gave |
|---|---|
| Read-only production reads, 19:28Z | Settings on each company, watch state, door outbox |
| App code at origin/main 30c49abc1c | Every file:line on this page (will drift) |
| Architecture hub, tracker rows, sibling pages | History, measured numbers, prior decisions |
| Raw per-speaker transcript of today's call | Quotes and decisions; it wins over the recap |
| Read-only GitHub reads | PR and issue states |
| UNVERIFIED | Not confirmed from a primary source; treat as a lead, not a fact |
How it works today
| Step | Where (file:line) | Model | Reads / writes |
|---|---|---|---|
| Clock | lambda/appfolio-sync/schedules.json:443-448; handler.ts:1616-1658 | rules | Fires every minute; each database decides if a tick is due (schedule.ts:78-88) |
| Robot read | src/lib/domain/pms-mirror/watch.ts:262; adapters/appfolio-source.ts:57-59 | rules | Robot route /api/texting/inbox/read; budget check watch.ts:274-291; takes a 5-min lease |
| Copy stored | watch.ts:357-401; file-message.ts:264-361 | rules | Writes MSG# or HOLD#; places the row on a person; appends to their conversation |
| Wake rules | wake-on-reply.ts:56-82; file-message.ts:460-495 | rules | One wake per thread per batch; publishes to the inbound queue (lambda/appfolio-sync/pms-mirror-reply-wake.ts:22-59) |
| Clara's turn starts | agents/clara/lib/messaging/process-envelope.ts:166; inbound-router.ts:468 | rules | Answers the row the copy already filed; writes no second copy |
| Haiku label | inbound-router.ts:1370-1517; src/lib/domain/leasing/tour/classify-message.ts:148, :173 | claude-haiku-4-5 | Returns tour_request, tour_reply or general; tour labels run the tour pipeline, which passes its actions in |
| Sonnet writes | agents/clara/lib/agent/conversation-manager.ts:6607, :3419, :3480; model-tiers.ts:224 | claude-sonnet-5, high effort | Drafts the text reply |
| Hold check | process-envelope.ts:544-557; classify-reply-topic.ts:150-175; supervised-reply-hold.ts:1-40 | claude-haiku-4-5 | Only when a module is Supervised at the building; decides hold or send |
| Door decision | dispatcher.ts:511-540; pms-doors/text-door-recipient.ts:23-30, :60-64; thread-door.ts; company-text-door.ts:13-30 | rules | Reads pms_text_door; leasing-only; a thread keeps its door; tenancy match keeps Clara's line |
| Send | adapters/appfolio-inbox-text.ts:180-241, :278-363; pms-mirror/adapters/appfolio-text-door.ts:133-172 | rules | Compliance gate (:203-217); robot route /api/texting/inbox/send; writes PMSTEXTOUTBOX# REC# |
| Read-back | file-message.ts:103-113, :304-311 | rules | Next tick files Clara's sent row as hers via the SENT# pointer |
| Retry drain | src/lib/pms/pms-text-door-drain.ts:172-238 | rules | Re-sends held texts with the same record id; re-runs the gates |
The wiring Fede could not remember
"I don't know if it's, like, he just wakes up Sonnet and Sonnet decides. Another layer of Haiku. Can't remember, honestly."
Fede, today's call
- No model decides whether Clara wakes: fixed rules look at who sent the text and who spoke last on the thread.
- Once she wakes, a Haiku call labels the text as a tour request, a tour reply or general, after cheap pattern shortcuts; only a tour label runs the tour pipeline, which hands what it did to Clara's turn.
- Sonnet always writes the reply, whatever the label, so Haiku never routes and never answers.
- A second Haiku call runs after the draft, and only when a module is in Supervised mode, to decide whether the reply is held.
- The label step only runs when the AppFolio line resolves to exactly one property; whether Western Slope's line does is UNVERIFIED, and if it resolves to none, Clara gets no leasing context.
An earlier code read said "no Haiku before Sonnet". It missed the tour label step. The step order is in section 2.
Switches, gates and kill switches
Settings that exist
| Setting | Lives | Values | Western Slope | Willows sandbox | Situs | JP&Co / Camellia | Flipped from | Who / when |
|---|---|---|---|---|---|---|---|---|
| pms_inbox_mirror | ORG#<id> / PROFILE | true, else off | on since Oct 16 03:05Z (tracker) | on | off | off | Admin › Customers › company › Company-wide group (moved there by #20764, Oct 17) | Stamp fields exist; Western Slope's stamp not read, date from the tracker |
| pms_inbox_backfill_days | same item | default 7, max 730 | 14 | not read | — | — | No route writes it UNVERIFIED elsewhere | — |
| pms_text_door | ORG#<id> / PROFILE | clara_line (default), appfolio_inbox | appfolio_inbox 13:46:54Z Oct 21 | appfolio_inbox; start UNVERIFIED | clara_line | clara_line | "Where Clara's texts go out" toggle, platform admins only. Hub says Modules card; #21429 (Oct 20) moved it to Company-wide group | WS: "fede (session 007, Fede: 'turn it on and watch it', 2026-10-21)". Sandbox: no stamp |
| Module modes | per module | off / on / drafts | leasing on; maintenance on, drafts; renewals off; collections off | not read | not read | not read | Modules card | — |
| pmsShowingSyncEnabled | company | true / off | on since Sep 22 | on | off | off | Admin company page | Real AppFolio Showings, not texts |
Raw fallback for the door: aws dynamodb update-item on ORG#…/PROFILE, setting pmsTextDoor. No cache: it applies on the next text.
What has no switch, or an untraced one
| Thing | State today |
|---|---|
| A switch that stops only the door's writes | none No write gate or kill switch found in the copy, doors, drain, job or door adapter |
| Tenant inbox handling | none Fixed in code: tenants are copied, never wake, and are always texted from Clara's line |
| Leasing-only rule | Code rule, no switch (#21569, #21576) |
| Global SMS kill switch | Sits before every door adapter, per a header comment, and re-runs on retry. Whether the first door send passes it is UNVERIFIED, inferred and not traced |
| Robot l4Enabled, per robot account | "Master kill switch" for every robot route the door uses (multiAccountConfig.ts:94-96). Per-account values UNVERIFIED |
| emailsEnabled | Email only; off at Western Slope; does not touch texts |
Gera's two-gate proposal (open)
| Gate | What it stops | Today |
|---|---|---|
| 1. PMS messenger write gate | Every PMS message write, from any module | Nearest thing is the company door setting, which moves texts between lines rather than stopping them |
| 2. Per-module writer gate | One module's writes; both gates must be on | Module modes exist; no per-module door switch |
"I would have two gatekeepers, one at the… PMS messenger write level… And then… a maintenance writer gatekeeper flag. So, both of them have to be on in order for us to be able to properly write."
Gera
"Um, yes, but… Right now, like, [other] is on, so… You can leave it on."
Fede. He agreed gates are right in principle ("I like having gates because it's like a high risk thing"), but never said yes or no to the second gate.
Customers today
| AppFolio database | Company | Inbox copy | Text door | Clara's AppFolio login | What's known |
|---|---|---|---|---|---|
| westernslopepm | Western Slope | on Oct 16 03:05Z, 14-day backfill | on Oct 21 13:46:54Z, leasing only; 0 sends at 19:28Z | User 540 "Clara Propflow". A real text sent from it is unit-tested only | 90 properties. Hit the send reserve Oct 19 night and Oct 20 at 6:56 PM Denver. Both new leads today came by email |
| jpco | Willows sandbox (property 45) | on, watch live since Oct 13 12:18Z | on (test); start date UNVERIFIED, on by Oct 16 per hub | Robot login 284 is one of Clara's logins (from a PR title only) | Where every live proof ran. 768 requests used on this database at 19:27Z |
| jpco | JP&Co / Camellia | off; its rows are dropped by the company wall | off | — | Shares the database with the sandbox |
| situsgroup | Situs Group | off, never run (no state row) | off | None recorded on the connection; AppFolio has Clara users 1254 and 1259 | Oct 13 ruling: waits for Willows proof. Its Sep 30 throttling storm is why the daily cap exists |
No code allowlist turns either setting on anywhere. Both are per company, default off.
The request budget and the clock
| Where the requests go (Western Slope, Oct 16-20 average 3,502/day) | Share |
|---|---|
| Inbox list reads | 65% |
| Guest-card email re-reads (every 2 h then, every 6 h now) | 31% |
| Thread reads | 3% |
| Door sends | 0 |
| Fix | Effect | Live |
|---|---|---|
| #21568 | Keeps peeking every 15 min after the reserve is hit | 04:50Z Oct 21; live proof pending |
| #21590 | A door send starts fast mode; peeks honor it; emails every 6 h | merged 15:51Z |
| #21592 | Charges the robot's real session-check count | merged 16:08Z |
| #21594 | 60 s from 6 AM to midnight, 2 min overnight | merged 19:25:54Z, live in the sync job 19:40:39Z |
| Robot #586 | One session check per read; cuts about 1,300 requests/day | merged Oct 21 |
| Rejected | Raising the cap (about 5,400 needed), a cheaper change signal, a longer fast mode | — |
| Cost of one door text | Requests |
|---|---|
| Reply into an existing thread | ~7 |
| Start a thread (measured Oct 20 at Willows) | ~27 |
| Start from a tenant record (constant) | 20 |
Supervised and hold mode
| Step | How a held draft is made | Where |
|---|---|---|
| 1 | Sonnet drafts the reply | conversation-manager.ts:6607 |
| 2 | If any module at the building is Supervised, Haiku labels the reply's topic | process-envelope.ts:548-551 |
| 3 | A rule decides hold or send | supervised-reply-hold.ts:1-40 |
| 4 | A held card posts to the company's Slack: the property leasing channel, else the company front-door channel. No channel mapped means no post; the hold page is the only place it shows | held-slack-target.ts:4-8, :23-25 |
| 5 | A draft that cannot be held escalates to the alerts channel | renewal-reply-review.ts:347, :385-386 |
| Other holds | What triggers it |
|---|---|
| Staff stepped in | A teammate texts in AppFolio, so Clara holds for that person (today: for good) |
| Company wall | A row with no property, or two companies, is held as HOLD# |
| Door outbox | Busy, stale, door down, wrong thread, refused; the 10th busy hold opens a "door down" episode |
Fede's rules from the call
| Rule | His words |
|---|---|
| Hold mode is the problem, not the noise | "it's not that noise that not bothers me, but really the root cause is like, hey. We're kind of in hold mode." |
| Each held message type ends in "turn it on" or "kill it" | "either say, okay, that message is not necessary, we turn it off"; "I don't think we should leave supervised mode for, like, a month." |
| Most tenant messages get no reply | "for now, most of the time, my assumption is we wouldn't respond." |
| Never touch eviction, can't-pay-rent, or judgment calls | "hey, I'm getting evicted or like, hey, I can't pay the rent"; "If it's something that… Requires human judgment. We don't touch it." |
| Routine asks can go to Clara later | "if it's like a routine or easy ask, we could start picking up some of those." |
| Gera's offers to cut noise | Fede's reply |
|---|---|
| Grey "would have sent" bubbles on the conversation page, admins only | "Okay. Okay. Understood." |
| One grouped Slack message for held items | "Okay." |
What the call decided
From the raw transcript (13:31 CDT). Where the recap disagrees, the raw transcript wins. The transcript cuts off mid-sentence.
| Decision | Who said it | Status |
|---|---|---|
| Gera owns PMS messaging | Fede: "Yeah, I think you can take it over, yeah." | decided |
| Gate the writes | Fede: "I like having gates because it's like a high risk thing" | agreed in principle The exact two-gate design is open |
| Tenant inbox goes on next, in Supervised mode | Fede: "I want to turn it on supervised so that at least we know what would have happened." | agreed in principle Gera did not commit in words |
| Leasing stays on | Fede: "You can leave it on." | decided What "[other]" referred to is unclear |
| The prospect/tenant split can change | Fede: "But feel free to change it." Gera: "I'll look into it." | decided Gera may change it |
| Maintenance first at today's customer meeting | Fede: "I think we talk maintenance first." | decided The recap said leasing first. It is wrong |
| AppFolio's canned "work order created" text | Fede: "should that even exist?" | open A question for Gera, not a decision |
| Exploratory work stays off CI | Fede: "don't put it on CICD, necessarily. Just local" | decided |
| Customer recipes plus PMS ingredients feed one engine | Fede: "It's a recipe." Gera: "It's getting all the ingredients" | agreed |
| Ingest uses exact "where to find it" steps per PMS | Fede: "We got to give extremely specific instructions." | agreed |
Decisions waiting
Recommendation first. "Hub" means the Oct 21 audit's recommendation; "this page" means a suggestion made here, not decided by anyone.
D-A. A staff member texts a lead in AppFolio. What does Clara do?
| (a) Hub pick | The lead's next message decides. If it answers staff, Clara records it and stays quiet. If it writes to Clara, she answers. Her own nudges pause after a staff text. No clock, no button. |
| (b) | Only a staff reply stops her. |
| (c) | Staff hand back with a button. Conflicts with the Sep 8 no-buttons rule. |
| Today | She stops for good, and nobody sees it. The door went on with this behavior. 11 of 45 leads were hit, about 2 a day. |
D-B. Rollback shape
| (a) Hub pick | Accept the number change and send a "this is Clara's direct number" text. Small build. |
| (b) | Roll back with no notice. |
| (c) | Never roll back; fix forward only. |
Second gate: per module, on top of or replacing today's split?
| (a) This page | Add the company write gate plus per-module gates on top. Keep the prospect/tenant split until the gates are proven. Fede said "leave it on". |
| (b) | Replace the split with per-module gates. |
| (c) | No new gate. Keep the door setting and the leasing-only rule. |
When does Supervised mode end? No exit criteria are set.
| (a) This page | Give each held message type a dated verdict, turn on or remove, inside Fede's "not a month". |
| (b) | Use a volume bar: hold N clean drafts per type before turning it on. N not set. |
| (c) | No criteria. Review case by case. This is today's state. |
Upgrade the Haiku label to Sonnet?
| (a) This page | Keep Haiku. First measure its labels on the historical message set in S3 that Fede pointed to. Fede: "I don't know if we need to". |
| (b) | Move the label to Sonnet now. That is Gera's question on the call. |
Rollback and the watch
Rollback is one setting back to Clara's line. It is cheap to flip and not free.
| Rollback cost | Detail |
|---|---|
| Leads move numbers silently | Every AppFolio lead moves to Clara's own number with no notice (D-B) |
| 44 threads go quiet | Their newest text came via AppFolio. Their AppFolio replies stop waking Clara, logged as "the team holds this", and nothing pages |
| Held texts freeze | No expiry while the door is off. Turning it back on expires them at once and fires a false "door down" |
| Roll back if (set this morning) |
|---|
| Any door text goes to a tenant, staff member or vendor |
| A lead's reply sits unanswered more than 10 minutes during the day |
| A lead with no AppFolio card is held more than 30 minutes |
| The door is down, or sends fail |
| The read-only watch reads | Where |
|---|---|
| Watch state and budget | PMSMIRROR#appfolio#westernslopepm / STATE |
| Copied rows and wall holds | PMSMIRROR#… MSG# / HOLD# / SENT# |
| Door texts and alert latches | PMSTEXTOUTBOX#<org> REC# / EPISODE# |
| Wake decisions | /aws/lambda/propflow-appfolio-sync-prod, filter "decision=" |
| Texts that left on Clara's line | Twilio, from +19708220641 |
| Alert today | Goes to | Real or noise |
|---|---|---|
| Door holding texts ("door down") | Sentry only, never #alerts | After a read-back timeout it is noise (#21068) |
| Robot cold 15 minutes | Slack #alerts, mentions Fede only | Real; once per outage, with recovery |
| Send reserve hit, cold session, held for good | Sentry | Expired held text is mostly noise (#20990) |
| "No reply for 15 minutes" on a held door reply | Existing reply clock | Real |
Suggested here: the robot-cold page mentions Fede by user id, so repoint it to Gera.
History
| When | What happened |
|---|---|
| Sep 3 | Older per-property hand-off of Clara's messages to the PMS (#6831, dark); separate from today's copy |
| Oct 11-12 | Gap measured: PropFlow saw nothing of any customer's PMS inbox. Design written on the hub |
| Oct 12, 8 PM | Build started on Fede's go |
| Oct 12-13 overnight | 15 PRs merged, copy dark; watch live at Willows 12:18Z Oct 13 |
| Oct 13 | Door decided by Fede by voice: text only, one company switch, inbox thread, no fallback. First live door send at Willows, 3:45 PM Denver |
| Oct 13-14 | Robot's AI key died (Tue 1:26 PM); Western Slope's robot could not sign in for 21 h; 11 writes landed late, none lost. Outbound door chain merged dark |
| Oct 15 | Gera named the system Reception. "Nothing falls through" decided |
| Oct 16 03:05Z | Inbox copy on at Western Slope, 14-day backfill |
| Oct 16 | Door send proven at Willows (36 s to phone). Fede's "no timer" ruling |
| Oct 19 | 11 PRs for two-way texting. Proposal "D7" let tenants wake Clara. Fede put it on hold: design doc first |
| Oct 20 | New-lead path proven at Willows (99 s). Evening rulings: no turn-on today, leasing only, copied tenants never get a turn, a staff text should not stop Clara for good. Reserve hit 6:56 PM Denver |
| Oct 21 04:21-06:26Z | Fixes for tenants, leasing-only, reserve peeks and tenancy merged (#21563, #21569, #21568, #21576). Willows run green 05:25-05:39Z |
| Oct 21 13:46:54Z | Door on at Western Slope. Fede: "turn it on and watch it", "revert or turn off if you see customer impact" |
| Oct 21 15:51-19:25Z | Evening-delay chain merged: #21590, #21592, #21594, robot #586 |
| Oct 21 | Reasoning-preamble guard issues filed (#21603, #21604). Not door-related |
| Oct 21 | Guest-card notes policy decided by Fede: option (a), every Clara contact and outcome on the card, the same at every customer, no setting. Hub section marked decided; build anchored on #21618, three PRs in flight from session 001 (harness notes dimension, message notes, outcome notes). Still Fede's call: deleting the Showings setting would turn on AppFolio Showings at JP&Co and Situs |
| Oct 21, 13:31 CDT | Handoff call |
Every core PR, by week (108 found by fuzzy search, may be incomplete)
From nine fuzzy GitHub title searches, filtered by keyword. 99 merged at the 19:15Z count, then #21594 merged at 19:25Z, making 100. 8 closed unmerged. Dates are UTC.
2026-W36 (1)
| PR | State | Merged | By | Title |
|---|---|---|---|---|
| #6831 | merged | 2026-09-03 | fede-propflow | messaging: a property can hand Clara's messages to its PMS (dark) |
2026-W38 (4)
| PR | State | Merged | By | Title |
|---|---|---|---|---|
| #9827 | closed | - | gera-propflow | feat(atlas): a work order's dispatch + PMS mirror as independent health facts (ui-atl-r2-completeness-and-health, part 2) |
| #9833 | merged | 2026-09-19 | gera-propflow | feat(atlas): a work order's PMS mirror as independent health facts (ui-atl-r2-completeness-and-health, part 2b) |
| #9911 | merged | 2026-09-20 | fede-propflow | household: grouping can be computed from the PMS mirror alone, same result as stored rows on all three corpora (Option D, PR 5) |
| #10031 | merged | 2026-09-20 | fede-propflow | leasing(prospects): rows built from the PMS mirror when the record owner is AppFolio — one row per person, homes listed (Option D, PR 9b) |
2026-W39 (3)
| PR | State | Merged | By | Title |
|---|---|---|---|---|
| #10916 | merged | 2026-09-22 | gera-propflow | fix(prospects): take the chunk when a PMS-mirror company is in view |
| #11198 | closed | - | gera-propflow | fix(pms-mirror): decide guest-card insert-vs-update from the pre-write read, not a timestamp tie |
| #12628 | merged | 2026-09-26 | gera-propflow | fix(leasing): the PMS-mirror lead timeline reads only messages about buildings the reader can see (p7-relationships) |
2026-W40 (5)
| PR | State | Merged | By | Title |
|---|---|---|---|---|
| #13644 | merged | 2026-09-29 | gera-propflow | feat(sms): the text door requires a module declaration (row 15, part 4) |
| #14017 | merged | 2026-09-30 | gera-propflow | fix(gatekeeper): text door sends on an unreadable module setting — only a stored OFF refuses (gk-drafts-mode 11.3b part 1) |
| #14900 | merged | 2026-10-01 | gera-propflow | feat(gatekeeper): Collections Supervised hold path at the text door, dark (lane D part 1) |
| #15064 | merged | 2026-10-01 | gera-propflow | feat(leasing): the text door refuses an unapproved Leasing text at a Supervised building; staff-started texts pass (dark, supervised-mode lane B) |
| #16218 | merged | 2026-10-03 | gera-propflow | test(guards): text-door-hold reads the text-door import from the syntax tree, not source text (text-search batch 16, part 1) |
2026-W41 (2)
| PR | State | Merged | By | Title |
|---|---|---|---|---|
| #16504 | merged | 2026-10-05 | gera-propflow | feat(gatekeeper): the text door checks vendor and staff texts in Supervised once their sender declares no approval (dark, door part 1) |
| #18719 | merged | 2026-10-11 | gera-propflow | The gas lane reads across companies only through its own LifeSafetyContext door, minted from the line or the mailbox (gauntlet T18, Q3) |
2026-W42 (66)
| PR | State | Merged | By | Title |
|---|---|---|---|---|
| #19167 | merged | 2026-10-13 | fede-propflow | PMS inbox mirror, PR 1: the mirrored-message shape, the message-arrived event, and two dark company settings |
| #19187 | merged | 2026-10-13 | fede-propflow | fix(pms-mirror): a mirrored company's guest-card rows wake nothing and hold nothing (#19161) |
| #19191 | merged | 2026-10-13 | fede-propflow | PMS inbox mirror: AppFolio texting-inbox adapter (keeps every field, dark) |
| #19198 | merged | 2026-10-13 | fede-propflow | fix(pms-mirror): six role-only readers never read a mirrored office text as Clara's |
| #19204 | merged | 2026-10-13 | fede-propflow | feat(pms-mirror): a row copied from the PMS inbox says who and which door |
| #19218 | merged | 2026-10-13 | fede-propflow | feat(pms-mirror): placement by the ladder; a stored PMS message row is written once |
| #19219 | merged | 2026-10-13 | fede-propflow | PMS inbox mirror: organization wall and write-once raw store (dark) |
| #19220 | merged | 2026-10-13 | fede-propflow | fix(pms-mirror): bulk, notice and history rows never read as staff answered; no AppFolio photo on a work order |
| #19222 | merged | 2026-10-13 | fede-propflow | test(pms-mirror): the sync handler's mirrored wiring has controls that go red |
| #19232 | merged | 2026-10-13 | fede-propflow | fix(pms-mirror): bulk sends and notices keep their sender kind on the mirror block |
| #19233 | merged | 2026-10-13 | fede-propflow | feat(pms-mirror): the one mirror writer, behind pms_inbox_mirror; history rows inert |
| #19243 | merged | 2026-10-13 | fede-propflow | PMS inbox mirror: the watch's Denver-time schedule (busy, quiet, late, asleep, 30-minute burst; dark) |
| #19246 | merged | 2026-10-13 | fede-propflow | fix(pms-mirror): one writer per door; on a mirrored company the importer leaves texts to the inbox mirror |
| #19248 | merged | 2026-10-13 | fede-propflow | PMS inbox mirror: one watch tick, keyed on the AppFolio id (dark) |
| #19263 | merged | 2026-10-13 | fede-propflow | PMS inbox mirror: the watch runner — AppFolio source over the robot's read-only inbox route, leased state, tick log (dark) |
| #19264 | merged | 2026-10-13 | fede-propflow | feat(pms-mirror): production placement lookups, company-walled reads |
| #19276 | merged | 2026-10-13 | fede-propflow | PMS inbox mirror: the runner files each new row once through the production placement lookups (dark) |
| #19281 | merged | 2026-10-13 | fede-propflow | PMS inbox mirror: the sync Lambda runs the inbox watch every minute; only databases with a company on tick (dark) |
| #19282 | merged | 2026-10-13 | fede-propflow | fix(pms-mirror): a mirrored bulk send shows its recipient count |
| #19284 | merged | 2026-10-13 | fede-propflow | PMS inbox mirror: read-only performance report: every tick, every row's lag, p50/p95 (dark) |
| #19338 | merged | 2026-10-13 | fede-propflow | PMS mirror: find a property-less row's company before holding it (#19326) |
| #19349 | closed | - | fede-propflow | PMS mirror: a row's texting line places it when its parties cannot (#19161, #19326) |
| #19350 | merged | 2026-10-13 | fede-propflow | fix(conversations): polish the PMS-mirrored thread and list |
| #19374 | merged | 2026-10-13 | fede-propflow | fix(pms-mirror): a property's own texting line places a two-company or property-less row |
| #19375 | merged | 2026-10-13 | fede-propflow | fix(pms-mirror): Clara's seat is a set per database (robot login 284 no longer stored as staff) |
| #19401 | merged | 2026-10-14 | fede-propflow | feat(pms-doors): pms_text_door company setting, default clara_line (#19161) |
| #19410 | merged | 2026-10-14 | fede-propflow | fix(pms-mirror): alert engineering once per cold AppFolio session episode |
| #19411 | merged | 2026-10-14 | fede-propflow | feat(pms-doors): company-page card to choose Clara's text door (#19161) |
| #19412 | merged | 2026-10-14 | fede-propflow | fix(pms-mirror): relabel a staff-filed row as Clara's on re-read once her login id is known (#19404) |
| #19441 | merged | 2026-10-14 | fede-propflow | feat(pms-doors): sendPmsText, the AppFolio text door's L4 primitive (dark, no caller) |
| #19463 | merged | 2026-10-14 | fede-propflow | feat(pms-doors): Clara's texts go through the company's AppFolio inbox thread when its text door says so (dark) |
| #19473 | merged | 2026-10-14 | fede-propflow | feat(pms-doors): drain texts the AppFolio door held busy, after each mirror tick (dark) |
| #19474 | merged | 2026-10-14 | fede-propflow | feat(pms-mirror): link Clara's read-back text to the outbox record that sent it (#19468) |
| #19485 | merged | 2026-10-14 | fede-propflow | fix(pms-mirror): a cold tick re-tries at the busy interval, whatever the band (#19413) |
| #19602 | merged | 2026-10-14 | fede-propflow | fix(pms-mirror): one company on the database, a tenant/prospect row with no property goes to its front door |
| #19605 | merged | 2026-10-14 | fede-propflow | fix(pms-mirror): read the AppFolio row's main party, not only the additional ones (#19596) |
| #19618 | merged | 2026-10-14 | fede-propflow | fix(pms-mirror): AppFolio's automatic texts are notices, never a teammate reply |
| #19619 | merged | 2026-10-14 | fede-propflow | fix(pms-mirror): an AppFolio mass text reads as a mass text, not a reply |
| #19623 | merged | 2026-10-14 | fede-propflow | fix(pms-mirror): office senders show AppFolio's display name, never 'Assistant' |
| #19638 | merged | 2026-10-14 | fede-propflow | fix(pms-mirror): file prospect and applicant threads only; hold tenant threads with a dry-run target (#19617) |
| #19662 | merged | 2026-10-14 | fede-propflow | PMS mirror: place a guest-card prospect's text through the card's own person link |
| #19668 | merged | 2026-10-14 | fede-propflow | PMS mirror: one engineering alert when a text is held for good |
| #19900 | merged | 2026-10-15 | fede-propflow | test(pms-mirror): unknown-number row reaches the front door, adapter -> wall composed (#19610) |
| #19905 | merged | 2026-10-15 | fede-propflow | fix(pms-mirror): future tenant labelled tenant, any home applied to labelled applicant (#19664) |
| #19923 | merged | 2026-10-15 | fede-propflow | feat(pms-mirror): hand-run backfill of up to a month of leasing inbox threads, filed as inert history |
| #20072 | merged | 2026-10-15 | fede-propflow | fix(pms-mirror): a live staff text settles the person's open handoff (#20037) |
| #20073 | merged | 2026-10-15 | fede-propflow | fix(pms-mirror): keep AppFolio delivery status and thread link on mirrored texts |
| #20079 | merged | 2026-10-15 | fede-propflow | fix(pms-mirror): AppFolio robot cold 15 min pages Fede in #alerts, once per outage, with recovery |
| #20118 | merged | 2026-10-15 | fede-propflow | feat(pms-mirror): copy a guest card's AppFolio emails onto the person's thread (#20105) |
| #20132 | merged | 2026-10-15 | fede-propflow | fix(pms-mirror): robot-cold page reads provider and host from the account adapter |
| #20154 | merged | 2026-10-15 | fede-propflow | fix(pms-mirror): one person's failed email read no longer fails the tick (#20135) |
| #20183 | merged | 2026-10-16 | fede-propflow | fix(pms-mirror): a re-read text never marks a person touched, so email reads keep the 2-hour gate |
| #20210 | merged | 2026-10-16 | fede-propflow | fix(pms-mirror): never file a mirrored message on a placeholder thread (#20202) |
| #20216 | merged | 2026-10-16 | fede-propflow | fix(pms-mirror): a deeper backfill floor re-reads the emails the shallower one skipped |
| #20266 | merged | 2026-10-16 | fede-propflow | fix(pms-doors): a prospect's text-door thread needs no property on the guest-card party (#20253) |
| #20270 | merged | 2026-10-16 | fede-propflow | fix(pms-doors): a household text-door thread addresses the one party matched by the recipient's number (#20253) |
| #20284 | merged | 2026-10-16 | fede-propflow | fix(pms-mirror): a backfill pass is done only when no thread was skipped during the whole pass |
| #20318 | merged | 2026-10-16 | fede-propflow | fix(text-door): unplaced guest-card texts name no property, so the robot guards on party identity |
| #20384 | merged | 2026-10-16 | fede-propflow | fix(pms-doors): staff and vendor texts always leave on Clara's line, never the AppFolio door |
| #20410 | merged | 2026-10-16 | fede-propflow | fix(text-door): manager replies (confirmation review, mass-message confirm) leave on Clara's line |
| #20449 | merged | 2026-10-16 | fede-propflow | Text door: open the AppFolio text thread from the guest card when none exists |
| #20451 | merged | 2026-10-16 | fede-propflow | Text door: a front-door property reads the company's PMS database and the thread's own building |
| #20453 | merged | 2026-10-16 | fede-propflow | Text door: the same person on file twice is not ambiguous |
| #20466 | merged | 2026-10-16 | fede-propflow | fix(pms-mirror): Clara's own text read back is placed by the door's sent pointer, never held no_property (#20381) |
| #20721 | merged | 2026-10-17 | gera-propflow | fix(appfolio-sync): the inbox watch reads the property roster once per run, not once per database |
| #20764 | merged | 2026-10-17 | gera-propflow | feat(admin): the inbox mirror and the coworker line become switches in the Company-wide group (gatekeeper features, part 11) |
2026-W43 (27)
| PR | State | Merged | By | Title |
|---|---|---|---|---|
| #20974 | merged | 2026-10-19 | fede-propflow | fix(text-door): a door reply reaches the thread once, through a double text or a redelivery |
| #20975 | merged | 2026-10-19 | fede-propflow | fix(text-door): a text thread keeps the door it is already on |
| #20982 | merged | 2026-10-19 | fede-propflow | fix(pms-doors): a text with media leaves on Clara's line; the renewal letter's intro and PDF come from one number |
| #20985 | merged | 2026-10-19 | fede-propflow | fix(pms-doors): a held door text never goes out stale, superseded or past a gate |
| #20992 | merged | 2026-10-19 | fede-propflow | feat(pms-mirror): a prospect's reply in the AppFolio thread wakes Clara when she is the one talking |
| #20993 | merged | 2026-10-19 | fede-propflow | feat(pms-mirror): a teammate's text in the PMS thread puts Clara on hold for that person |
| #20995 | merged | 2026-10-19 | fede-propflow | fix(pms-mirror): a STOP on the PMS texting line opts the person out on every door |
| #21000 | merged | 2026-10-19 | fede-propflow | fix(pms-doors): the thread-door rule reads past another number's rows, and never counts a text sent to someone else |
| #21031 | merged | 2026-10-19 | fede-propflow | fix(pms-mirror): a STOP or START texted by a number the mirror cannot place is still recorded |
| #21083 | merged | 2026-10-19 | fede-propflow | fix(pms-mirror): two door texts filed in one tick wake Clara once, with the latest text |
| #21140 | merged | 2026-10-19 | fede-propflow | fix(pms-mirror): a PMS automatic email under Clara's seat name is the PMS's notice, not Clara's |
| #21174 | merged | 2026-10-19 | fede-propflow | fix(pms-mirror): Clara's own PMS email is matched by guest card, not only the thread person |
| #21216 | closed | - | fede-propflow | feat(renewals): list shows tenants with no texting thread in the PMS inbox |
| #21221 | closed | - | fede-propflow | feat(pms-mirror): file tenant threads; Clara wakes only where her last text was a renewal |
| #21249 | merged | 2026-10-20 | fede-propflow | feat(pms-doors): a first text to a current tenant opens the PMS thread from the tenant record |
| #21312 | closed | - | fede-propflow | feat(pms-mirror): a door-sent text keeps its kind on the thread, so a tenant's renewal reply wakes Clara |
| #21315 | merged | 2026-10-20 | fede-propflow | fix(pms-mirror): wake Clara on every tenant and prospect reply unless a teammate spoke after her |
| #21318 | closed | - | fede-propflow | fix(pms-mirror): reserve 600 of the daily PMS request budget for Clara's texts |
| #21324 | closed | - | fede-propflow | fix(text-door): the team-note relay and row-less household texts name their conversation, so a Clara-number thread keeps Clara's number at the door flip |
| #21429 | merged | 2026-10-20 | gera-propflow | feat(admin): the text door and the default hand-off person move into the Company-wide group (gatekeeper placement, part 1) |
| #21563 | merged | 2026-10-21 | fede-propflow | fix(pms-mirror): a tenant's AppFolio text never wakes Clara |
| #21568 | merged | 2026-10-21 | fede-propflow | fix(pms-mirror): keep peeking at the inbox after the send reserve is reached |
| #21569 | merged | 2026-10-21 | fede-propflow | fix(pms-doors): the PMS texting line is leasing only; tenant texts stay on Clara's line |
| #21576 | merged | 2026-10-21 | fede-propflow | fix(pms-doors): a tenancy match keeps the text on Clara's line even when leasing is declared |
| #21590 | merged | 2026-10-21 | fede-propflow | fix(pms-mirror): a door send starts fast mode, the reserve peek honors it, emails re-read every 6 h |
| #21592 | merged | 2026-10-21 | fede-propflow | fix(pms-mirror): charge the robot's real session-check count, not a constant |
| #21594 | merged | 2026-10-21 | fede-propflow | fix(pms-mirror): read the inbox every 60 s from 6 AM to midnight, every 2 min overnight |
Open issues and known gaps
| Item | What | State |
|---|---|---|
| #21600 | A lead who walks away after a tour: no explicit stop reaches the tour workflow's post-tour chase | open |
| #21603 | Reasoning-preamble guard: narrow the tools and third-person markers; fix the "tú" boundary | open; not door-related |
| #21604 | Reasoning-preamble guard runs before the history-stamp strip, leaves no trace on redraft | open; not door-related |
| #19292 | The hub cites it for "does reading a thread clear AppFolio's unread mark". One Willows probe passed (unread count unchanged), but its before reading is not on disk. The issue's current title is about the per-tick worst case (~110 requests) and the endpoint thread reads use | open; not proven at Western Slope |
| #21577 | A lead marked "not interested" and the card set inactive from an old STOP that a later START reversed (Willows card 197) | open |
| #21566 | Cut the copy's daily request spend: email re-read cadence, one probe per call | open; today's #21590 (emails every 6 h) and robot #586 (one session check per read) cover those two items |
| Robot #592 | Guest-card close drops the reason and note; AppFolio records "No Response" (Western Slope card 3023: she texted she took another place). About closing cards, not texting | open |
| Reserve peek proof (#21568) | Western Slope's state row showing peeks after the reserve is hit | pending |
| Evening schedule proof (#21594) | The first conversation after 7 PM Denver | pending |
| Walk-away lead's card | Card 3023 above is the only walk-away card found: closed "No Response" with no note. Confirm it is the one meant | UNVERIFIED |
| Lead with no AppFolio card | Gets no text and is held for good (4 of 35 leads) | watch the first caller |
| Some leasing texts stay on Clara's line | Weekend tour check-in, bounce fallback, crash fallback | follow-up |
| #20989 | Retry ignores quiet hours | open |
| #20636 | Starting a thread tries 3 of about 12 cards (labelled bug) | open |
| Unit-tested only | Teammate hold, held-text expiry, door-down alert, photos, a real text from Clara's Western Slope login, the tenancy match | not live-proven |
| Stale comments | Admin route, door card, door setting, copy settings and send helper say "nothing reads this yet"; it is read. One file header still says tenants wake | cleanup |
Where everything lives
| Thing | Where |
|---|---|
| Architecture hub (full record) | /a/pms-conversations-in-clara-picture |
| Oct 21 audit (D-A to D-E, rollback, watch) | #oct21-audit |
| Evening and overnight delay | #oct21-evening |
| Is the door ready (Oct 20) | #oct20-door-readiness |
| Guest-card notes policy | #oct20-notes-policy |
| Proof at the Willows | #oct19-proof |
| Western Slope go-live tracker rows | /a/western-slope-go-live: ws-appfolio-text-door-2026-10-19, ws-appfolio-inbox-mirror-2026-10-16 |
| Sibling pages | shared inbox · door flip readiness · gatekeeper · module send gate · every switch · testing playbook |
| Robot login and session playbook (robot repo, not app repo) | appfolio-browser-agent: docs/playbooks/appfolio-login-and-sessions.md. Covers the login budget, not the request budget |
| Inbox copy code | src/lib/domain/pms-mirror/ (adapters/ for AppFolio) |
| Door code | src/lib/domain/pms-doors/; agents/clara/lib/messaging/adapters/appfolio-inbox-text.ts; dispatcher.ts |
| Jobs, drain, alerts | src/lib/pms/pms-inbox-watch-job.ts, pms-text-door-drain.ts, pms-inbox-watch-alert.ts |
| Lambda wiring | lambda/appfolio-sync/handler.ts, schedules.json, pms-mirror-reply-wake.ts |
| Admin switches | src/app/api/admin/companies/[id]/pms-text-door/, pms-inbox-mirror/; PmsTextDoorCard.tsx; PmsInboxMirrorRow.tsx |
| Scripts | scripts/pms-mirror-report.ts (npm run report:pms-mirror), scripts/pms-mirror-backfill.ts |
| Harness | scripts/harness/pms-mirror on branch harness/pms-mirror, --prod-willows mode |
| Watch script | Only in a session's scratch folder (ws-door-watch.py), not in any repo |
| Main table keys | PMSMIRROR#<pms>#<db> with MSG# HOLD# SENT# STATE THREAD# TICK#; PMSTEXTOUTBOX#<org> with REC# EPISODE#; ORG#<id>/PROFILE |
| Log groups | /aws/lambda/propflow-appfolio-sync-prod (watch, filing, wake, drain); /aws/lambda/propflow-inbound-processor-prod (Clara's turn and send; group name UNVERIFIED) |
| Slack | #alerts (robot cold); each company's leasing or front-door channel (held cards) |
| Willows bench numbers | AppFolio line +15046087154; Clara's line +18442853526 |
| Western Slope Clara line | +19708220641 |
| Planning docs (app repo) | docs/planning/mirror-pms-automation-author-2026-10-19.md; docs/planning/2026-10-15-guest-card-mirror-read-contract.md |
Also on the call
| Topic | State |
|---|---|
| ConAm implementation agreement | Sean wants to push back. Fede suggested a calm "what changed?" reply and a call. No final reply recorded |
| "should we refund renewals for western?" | Gera asked in the same-day thread. Unanswered |
| Fede's focus | A customer-agnostic renewals document engine that also covers new leases, "for a couple days" |
| Transcript | Cuts off mid-sentence; anything decided after that is missing |