Read-only production read, 2026-11-04, for the spine-health nightly red (Alert-ID automation_run_failure/co.propflow.spine-health-daily). Code read at origin/main 8c2ac122cbe. No writes. Phone numbers and emails are redacted; companies are named by class only. Board: spine-split-writer.
SESSION#anonymous-<raw> key itself: conversation-spine-stamp.ts:295 passes the raw caller phone (the E.164 normaliser accepts US numbers only), and claim-normalization.ts:64 adds the anonymous- prefix. Relay addresses are keyed at :288, and the conv:<id> fallback is at :305. There is no other writer.resolve.ts ~1357/1721) before the stamp runs. The stamp has nothing to key on and mints a prospect:<id> sentinel (leasing.ts:604).isMintableEmail refuses it. A shared relay key function already exists (forwardingAddressAnchor, email-lead-new-prospect.ts:91) and that door does converge.resolveAnchorPerson (person-stamp.ts ~948) refuses the conversation's person as an anchor because it carries an expiry, so a conversation-first lead still mints a sentinel.| Class | Records | What the two saves had | Verdict |
|---|---|---|---|
| A · international phone (voice) | 3 prospects, 1 property | Conversation keyed on the raw +593… phone; all 3 calls already share one person. Prospect side: contact dropped by the resolver. Order: conversation first, prospect 1–3 min later (post-call capture). | Converges with the fix. Merge the 3 sentinels into the call person. |
| B · listing-site relay (email) | 1 prospect | Both sides held the same relay address; the prospect side keyed on prospect:<id> instead. Order: prospect first. Original writer (tour-reply processor) retired by #25005; Clara's tour tools hit the same seam today. | Converges with the fix. Merge the sentinel into the relay person. |
| C · company's own forwarded line | 1 prospect | The caller came in on the customer's forwarded main line (an org-own number in config/phone-registry.json). dropOrgOwnContacts strips it, so the conversation is keyed on conv:<id>. The prospect person is a real staff/test person with a stale phone claim on that line. | Must NOT converge: keying on that line would merge every after-hours caller into one person. Heal the stale claim only. |
| D · guest card | 1 prospect | The prospect's sentinel was already merged into a real backfilled person (guest card), which still carries a stray sentinel claim. The conversation's relay person is separate. | Clean-up only (deferred guest-card link). Merge the relay person into the real one. |
Separately, one duplicated tenancy (a former primary row left after a co-tenant swap) heals through scripts/dedup-occupancy-rows.ts; it is not a save-path problem.