- Shipped
- 26 अगस्त 2026 को 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.