Widen the comms spine to an owner, in the order that keeps it safe

FeatureInitializerService
Shipped
August 26, 2026 at 1:55 AM UTC
Author
Kamo
Commit
f0f5063

LEAD_CONTACT_POINTS and LEAD_COMMUNICATIONS gain the document manager's (assoc_type, assoc_object_id) pair so an account, a patient or a commerce record can own communications without borrowing a lead. Four steps, and the order is the whole point. Backfill first: every existing row is a lead's, so assoc_type = 'LEAD' and assoc_object_id = lead_uid::text. Running it after the constraint swap would build the new unique index over nulls, enforcing nothing. Then swap the unique constraint onto 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 — and with lead_uid null for a patient, Postgres treats every one of those NULLs as distinct, so the second sighting inserts a duplicate. The new constraint is added BEFORE the old one is dropped, so a failure between them leaves the table protected by one rather than by neither. Only then relax lead_uid's NOT NULL. Hibernate's ddl-auto: update adds columns but never relaxes a constraint, so this cannot be left to the mapping — and doing it before the swap opens exactly the duplicate window above. Then verify, by reading pg_constraint and information_schema back. The clinical primary keys were correct in Java and wrong in the emitted DDL, and only reading pg_constraint showed it. A row left with no owner type would never appear on any timeline, so that case logs an error rather than reporting success. Idempotent throughout, and a missing table is skipped rather than failing the boot — an exception here would take every later runner with it. 176 tests green.

All changes

Like what you see shipping?

Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.

Start Free ForeverView Pricing