I'm parked on you

Everything I could decide myself is decided. What's left needs you. Each one has a recommendation from Fable — if you agree, pick it and press Done. "I'm not sure" is a real answer and becomes work for me. You don't need to open the session — pressing Done sends your answer back and it picks up.

1The Camellia move-in-special wording is fixed in prod, but concession-watcher re-scrapes camelliadenver.com daily at 13:00 UTC (7am MDT) and REPLACES the whole concessions array, so the fix is overwritten in ~9h. A manual edit silently losing to a scrape affects every property, not just this one. How do we make it stick?

In plain terms. Camellia's move-in special was wrong in our system and is now corrected. But we re-read their website every morning at 7am and replace the whole specials list with whatever it says, so the correction gets wiped in about nine hours. This is not just Camellia: any hand correction, on any property, loses to the next scrape. Either we fix the wording on camelliadenver.com so the website stays the single source of truth, or we change the rule so a hand-written special survives a scrape. Worth weighing: the website is what prospects read, so wrong copy there misleads people whether or not Clara has it right.
Fable recommends: Update the camelliadenver.com copy — website stays the single source of truth
The scraper treating the website as authoritative looks deliberate (concessionSource already distinguishes website/manual, and the new-property flow advertises 'Clara will pull concessions from this site'), so option 2 changes a designed semantic across all properties and should not ride along with a copy fix; also the website is what prospects read, so wrong copy there misleads people whether or not Clara is right.
Tried first, unsuccessfully: Verified in prod: two contradictory concession entries, one stating the FIRST month is free (opposite of the policy Sean locked). Replaced both with one correct entry — confirmed written, source=manual — then found scrapers/shared.ts:600 replaces the array wholesale on any scrape returning concessions, and lastScrapedAt was 8 minutes old.

0d84ff5f (adopted from df70453a) is parked on this.

243 scripts in propflowai/scripts/ carry `if (!process.env.DYNAMODB_TABLE_NAME) … = 'propflow-prod'` — dead code, because .env.local pins propflow-stage, so the guard never fires and they silently run against STAGE. This is what produced today's phantom message (real email delivered, conversation row written to stage, prod showed nothing). Fix the class, or leave it?

In plain terms. 43 of our scripts say 'if nobody tells me which database to use, use production'. That line never actually runs, because the local config always names the staging database -- so those scripts quietly work against staging while looking like they are on production. That is what caused today's ghost message: a real email went out, but the record of it was written to staging, so production showed nothing. Reading the wrong database is as misleading as writing to it -- staging numbers were reported as production for several turns today and it all looked perfectly consistent. The fix is one shared helper that prints which database it picked and refuses to guess.
Fable recommends: Fix the class — shared scripts/lib/resolve-table.ts that prints the resolved table and refuses a silent default; retrofit all 43
Reading the wrong table is as damaging as writing to it — I reported stage data as prod for several turns today and it looked completely coherent, so 'read-only is safe' is false; the failure is silence, and one shared loader fixes it everywhere instead of 43 chances to forget.
Tried first, unsuccessfully: Root-caused the phantom message: pull-link.ts defaults to PROD while every hand-rolled script preamble resolves to STAGE via .env.local, so a session reads real data and writes mirror data and both halves look right. Stage also carries prod's REAL Graph token and rootProviderMessageId (byte-identical), which is why a stage-driven send still delivered a real email.

0d84ff5f (adopted from df70453a) is parked on this.

3#49 needs a second decision — the first is executed as far as it can go. Merging #41 ahead of it created a collision its author never saw: main's redmain callback now messages the architect DIRECTLY, the exact shape #49 abolishes, and #49's guard catches it ('no fallback when delivery fails — the branch production ran 27/27'). Six hunks resolved, 496/499 pass; I will not weaken the guard to go green. The deciding issue: red-main alarms should close on main-going-green, but resolveAnsweredAlarms closes on acknowledgement.

In plain terms. Two fixes collided tonight and I need one call. The alarm fix (#49) makes sure an alert to me never silently vanishes if delivery fails — that path failed 27 times out of 27 in production. But the red-main fix I merged an hour earlier now pings me directly, which is exactly what #49 forbids, so #49 refuses to go green. I can wire red-main into the safe path properly — but a 'main is red' alert should clear when main turns GREEN, and the alarm system only knows how to clear when you press Done. Wire it naively and you either get alerts that never clear, or alerts that clear while main is still broken. Do I do the wiring properly first, or merge #49 now and carve red-main out of it?
Fable recommends: Wire red-main through escalate() properly — widen RedMainDeps for the per-incident key, and close red-main alarms on main-going-green rather than on a click. #49 waits for that.
Both carve-outs keep the one delivery path production actually ran (27/27) with no fallback — the hole #49 exists to close.
Tried first, unsuccessfully: Subagent resolved all six hunks against post-#47 main, tsc clean, 496/499 passing, stopped rather than guess: the key needs RedMainDeps widened (a third file) and the close rule is undecided. Held locally at 27d91f4, unpushed.

0d84ff5f is parked on this.

Pick an option above, then press Done.
PropFlow Docs