- Shipped
- August 26, 2026 at 1:37 AM UTC
- Author
- Kamo
- Commit
- 78d9861
The spine was built lead-shaped — LEAD_CONTACT_POINTS.LEAD_UID, LEAD_COMMUNICATIONS.LEAD_UID — and the association it actually needs is broader. An account owns several leads, typically sharing the same point of contact because they are the same person enquiring twice. A commerce record hangs off the account rather than any one lead. And a patient chart has calls and e-mails about it and no lead at all. Keying to leads meant each of those either borrowed a lead or grew a parallel timeline. Both tables now carry the document manager's (assoc_type, assoc_object_id) pair, with LEAD, ACCOUNT, PATIENT and COMMERCE_RECORD as owners. LEAD_UID is KEPT and still filled for lead-owned rows, so every existing /leads query, index and call site is untouched — the lead-keyed repository methods and the lead timeline overload both remain, and the new owner-keyed ones sit beside them. Three things this had to get right, and one I nearly got wrong. The unique constraint moves to the owner pair. (ORG_ID, LEAD_UID, CHANNEL, SOURCE_ID) is what makes ingestion idempotent — a RingCentral call is seen twice, once by the webhook and once by the reconciliation sweep. With LEAD_UID null for a patient, Postgres treats every one of those NULLs as distinct and the second sighting inserts a duplicate. The contact-point table matters more than the timeline. Pointing a timeline at a new owner changes what is READ; inbound traffic only ever LANDS on an owner that has contact points, because that is what the linker matches against. A patient whose number is not a contact point receives calls that resolve to nobody. The one I nearly shipped: the linker de-duped matches by cp.getLeadUid(). For a non-lead contact point that is null, so seenLeads.add(null) would let exactly ONE non-lead owner through per inbound message — a call to a number two patients share would reach one of them, silently. It now de-dupes on the owner. assoc_type is a STRING, never an ordinal. Hibernate freezes a CHECK (col BETWEEN 0 AND N) over an ordinal enum column at CREATE TABLE and never revisits it, so appending a value later rejects every insert carrying it — silently, because the failing statement sits inside a @Transactional method whose rollback removes the evidence. Seven of those were found full and already breaking things on this platform in August. A stored null reads as LEAD, because every row written before the column existed is a lead's and returning null for those would blank the timeline this change exists to widen. An UNKNOWN value reads as null rather than LEAD — attaching somebody else's traffic to a lead timeline is the one wrong answer worse than none. Also in this commit: the TEFCA exchange layer (partners, the disclosure ledger, cross-organization patient matching). Its purpose codes are now the same HL7 v3 ActReason strings PhiPurposeOfUse already uses — a test caught me inventing "T" for treatment where the platform says "TREAT", and an exchange row and an audit row describe the same disclosure from two angles, so a report joining them joins on those strings. 1932 tests green.