Merge audit trail maintained
Why it matters
When two contact records are merged, the losing record's data — consent history, suppression state, engagement timestamps, and source attribution — is permanently destroyed if no audit trail is kept. This is a compliance gap: GDPR Art. 17 right-to-erasure requests require demonstrating that all instances of the data subject's information were found and erased. Without merge history, you cannot prove the losing record existed. CAN-SPAM § 5 and GDPR Art. 21 suppression obligations are similarly undermined — if the losing record held the unsubscribe reason and it was destroyed in a merge, you cannot prove the suppression origin when challenged.
Severity rationale
High because merge operations without audit trails destroy consent and suppression evidence that is required for regulatory compliance and cannot be reconstructed after deletion.
Remediation
Log every merge to a dedicated table before deleting or overwriting the losing record:
CREATE TABLE contact_merges (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
winner_id UUID NOT NULL REFERENCES contacts(id),
loser_id UUID NOT NULL,
loser_data JSONB NOT NULL,
merged_by TEXT NOT NULL DEFAULT 'system',
merged_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
Wrap the merge in a transaction so the audit record and the deletion are atomic:
await db.$transaction(async (tx) => {
const loser = await tx.contact.findUniqueOrThrow({ where: { id: loserId } })
await tx.contactMerge.create({
data: { winner_id: winnerId, loser_id: loserId, loser_data: loser, merged_by: 'system' }
})
await tx.contact.delete({ where: { id: loserId } })
})
Never drop the contact_merges table during GDPR erasure requests — the record of the merge is separate from the personal data it references.
DetectionPro
The detection method for this pattern is part of AuditBuffet Pro.
The full method (what to look for, pass and fail conditions, and how results get reported) ships inside the Data Quality & List Hygiene Audit. The pattern definition, severity rationale, and remediation remain openly licensed under CC-BY-4.0.
External references
- iso-25010:2011 · functional-correctness — Functional Correctness (functional suitability)
- iso-25010:2011 · operability — Operability (usability — operator visibility into changes)
- gdpr · Art. 17 — Right to erasure ('right to be forgotten')
- gdpr · Art. 21 — Right to object
Taxons
History
- 2026-04-18·v1.0.0·Initial import from data-quality-list-hygiene·automated