---
id: CAP-CRM-DQ-014
title: Preserve the right information on the surviving contact
feature: FEAT-CRM-DQ-03
product_area: crm
module: data-quality
revision: 1
status: draft
product_intent: confirmed
implementation_evidence: observed
supersedes:
  - CAP-CRM-MRG-017
source_sha:
  services-crm: a8d1364f1dd44b723bb5f9acba12f983e9ec8d69
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources:
  - 'services-crm:docs/merging/decisions.md#D6 (decided 2026-10-12: most restrictive wins)'
  - 'services-crm:docs/merging/README.md#Status (architecture and plan approved by Andrew 2026-10-11)'
  - >-
    services-crm:docs/merging/experience/functional-requirements.md#AUTO-1 to AUTO-4, CON-2 to CON-5, CFM-3
    (package approved by Andrew 2026-10-13)
purpose: Administrators can combine contact information while seeing every value the merge discards.
outcomes:
  - 'The survivor retains the contact details, affiliations and custom values selected by the preview.'
  - Consent cannot be widened by combining records.
  - Omitted values retain an explanation of the responsible decision or rule.
concepts:
  - CON-CRM-DQ-003
actors:
  - ACT-CRM-009
  - ACT-CRM-005
surfaces:
  - console
  - api
permissions:
  - key: crm.contacts.merge
    where_checked: api
    scope: tenant
rules:
  - id: BR-CAP-CRM-DQ-014-01
    proved_by: [ATS-CRM-DQ-014-01, ATS-CRM-DQ-014-14, ATS-CRM-DQ-014-16, TST-CRM-DQ-176]
    statement: >-
      Consent takes the most restrictive result without a choice: any mail, call or e-mail prohibition
      applies, subscriptions rank unsubscribed before digest before subscribed and communication enablement
      requires every participant to enable it.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/merges/tests/unit/services/planning/contact-collections.spec.js#combines two
      preferences of one type to the most restrictive status, and offers no choice about it
    origin: 'services-crm:docs/merging/decisions.md#D6 (decided 2026-10-12: most restrictive wins)'
  - id: BR-CAP-CRM-DQ-014-02
    proved_by: [ATS-CRM-DQ-014-02, ATS-CRM-DQ-014-16]
    statement: >-
      Contacts retain one address and one phone per type, collapse identical items and ask about different
      same-type items with the survivor default; rejected items are dropped rather than retyped, while
      identical addresses under different types remain separate.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/merges/tests/unit/services/planning/contact-collections.spec.js#writes the other
      address when staff choose it, and attributes the survivor’s to the slot conflict
    origin: 'services-crm:docs/merging/README.md#Status (architecture and plan approved by Andrew 2026-10-11)'
  - id: BR-CAP-CRM-DQ-014-03
    proved_by: [ATS-CRM-DQ-014-03, ATS-CRM-DQ-014-16]
    statement: >-
      Contacts retain no more than three distinct e-mails and one chosen primary, selecting excess addresses
      with survivor-first and frequency defaults; an outside holder of the chosen primary blocks the merge.
    kind: validation
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/merges/tests/unit/services/planning/contact-collections.spec.js#blocks the merge
      when the primary address belongs to a contact outside the merge
    origin: 'services-crm:docs/merging/experience/functional-requirements.md#CON-5'
  - id: BR-CAP-CRM-DQ-014-04
    proved_by: [ATS-CRM-DQ-014-04, ATS-CRM-DQ-014-16]
    statement: >-
      The result has at most one preferred address and phone, defaults to the survivor's preference, allows no
      preferred address and excludes seasonal addresses from preference.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/merges/tests/unit/services/planning/contact-collections.spec.js#never prefers a
      seasonal address, which the table refuses
    origin: none
  - id: BR-CAP-CRM-DQ-014-05
    proved_by: [ATS-CRM-DQ-014-05, ATS-CRM-DQ-014-14, ATS-CRM-DQ-014-16, TST-CRM-DQ-176]
    statement: >-
      A disagreement over deceased status requires an answer without a recommendation, and the death date
      follows that answer.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/merges/tests/unit/services/planning/contact-fields.spec.js#offers that question no
      default, because neither state may be assumed
    origin: 'services-crm:docs/merging/experience/functional-requirements.md#CON-2'
  - id: BR-CAP-CRM-DQ-014-06
    proved_by: [ATS-CRM-DQ-014-06, ATS-CRM-DQ-014-14, ATS-CRM-DQ-014-16, TST-CRM-DQ-176]
    statement: >-
      A spouse connection between participants is removed with a warning to prevent a self-link, while outside
      spouse references to absorbed records follow the survivor.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/merges/tests/unit/services/planning/contact-fields.spec.js#is cleared when the
      survivor itself is the spouse a participant names, which no contact may be
    origin: 'services-crm:docs/merging/experience/functional-requirements.md#CON-3'
  - id: BR-CAP-CRM-DQ-014-07
    proved_by: [ATS-CRM-DQ-014-07, ATS-CRM-DQ-014-16, TST-CRM-DQ-181]
    statement: >-
      Overlapping or touching affiliations across records with one organization and type combine using
      earliest start, latest or open end, survivor title and notes and widest role spans; discarded
      information is listed and the contact limit remains 100 affiliations.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/merges/tests/integration/services/merges/merge-execution.integration.spec.js#combines
      the two Acme affiliations into one, from the earliest start, open, with the survivor’s title and both
      roles
    origin: 'services-crm:docs/merging/README.md#Status (architecture and plan approved by Andrew 2026-10-11)'
  - id: BR-CAP-CRM-DQ-014-08
    proved_by: [ATS-CRM-DQ-014-08, ATS-CRM-DQ-014-16]
    statement: >-
      Competing primary affiliations at different organizations create a survivor-defaulted choice with none
      available; a sole primary from an absorbed record fills the gap, and future or ended affiliations are
      excluded.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/merges/tests/unit/services/planning/contact-collections.spec.js#keeps the merged
      contact’s primary as the primary organization when the survivor has none, without asking
    origin: 'services-crm:docs/merging/experience/functional-requirements.md#CON-4'
  - id: BR-CAP-CRM-DQ-014-09
    proved_by: [ATS-CRM-DQ-014-09, ATS-CRM-DQ-014-16]
    statement: >-
      Linked details follow retained affiliations and identical organization sources collapse; unresolved
      links are not defaults and are dropped as replaced, while details losing their affiliation become
      copies.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/merges/tests/unit/services/planning/linked-contact-information.spec.js#turns a
      linked item whose affiliation the merged contact does not keep into a copy of its value
    origin: 'services-crm:docs/merging/README.md#Status (architecture and plan approved by Andrew 2026-10-11)'
  - id: BR-CAP-CRM-DQ-014-10
    proved_by: [ATS-CRM-DQ-014-10, ATS-CRM-DQ-014-16]
    statement: >-
      Names, automatic region and compliance are recomputed without choices; the survivor retains its number,
      identity, creation time and external identity, with absorbed identities retained in aliases.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/merges/tests/unit/services/planning/contact-fields.spec.js#recalculates the region
      rather than choosing between two, and says so
    origin: 'services-crm:docs/merging/experience/functional-requirements.md#AUTO-4'
  - id: BR-CAP-CRM-DQ-014-11
    proved_by: [ATS-CRM-DQ-014-15, ATS-CRM-DQ-014-16]
    statement: >-
      The chosen photo attaches to the survivor; unused photos are detached for deletion after 30 days and
      produce a preview warning.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/merges/tests/unit/services/planning/merge-decisions.spec.js#says so when the
      survivor keeps its own photo and a participant’s is dropped
    origin: none
  - id: BR-CAP-CRM-DQ-014-12
    proved_by: [ATS-CRM-DQ-014-11, ATS-CRM-DQ-014-16, TST-CRM-DQ-181, TST-CRM-DQ-183]
    statement: >-
      Custom fields are decided independently; archived definitions offer no choice or write, preserving
      survivor values and listing absorbed-only values as discarded.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: 'services-crm:modules/merges/tests/e2e/merges.e2e.spec.js#offers no conflict about the archived field'
    origin: none
  - id: BR-CAP-CRM-DQ-014-13
    proved_by: [ATS-CRM-DQ-014-12, ATS-CRM-DQ-014-16, TST-CRM-DQ-181]
    statement: >-
      The ordinary survivor save receives only changed custom values, allowing previously missing required
      fields to remain missing but rejecting removal of an existing required value.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/merges/tests/integration/services/merges/merge-execution.integration.spec.js#refuses
      the merge that would clear a required field a record does hold a value for
    origin: none
  - id: BR-CAP-CRM-DQ-014-14
    proved_by: [ATS-CRM-DQ-014-13, ATS-CRM-DQ-014-14, ATS-CRM-DQ-014-16, TST-CRM-DQ-176]
    statement: >-
      Every absorbed value missing from the result is listed with its sources, reason and responsible choice
      or rule, and console confirmation requires acknowledgement.
    kind: invariant
    enforced_at:
      - service
      - console
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/merges/tests/unit/services/planning/merge-decisions.spec.js#names a conflict, a
      default or a rule for every value it drops, over a snapshot of every kind of difference
    origin: 'services-crm:docs/merging/experience/functional-requirements.md#CFM-3'
scenarios:
  - id: SC-CAP-CRM-DQ-014-01
    given: 'Four member records disagree on names, contact details and communication choices.'
    when: The worked merge applies the reviewer's selections.
    then: >-
      Selected values survive, newsletter consent remains unsubscribed and discarded values retain
      attribution.
    provenance: test
    source: >-
      services-crm:modules/merges/tests/unit/services/planning/worked-example.spec.js#attributes every value
      the survivor will not hold to the conflict or the rule that dropped it
  - id: SC-CAP-CRM-DQ-014-02
    given: A sparse unsubscribed record contains a birth date absent from a fuller member record.
    when: The sparse record is absorbed into the member.
    then: 'The birth date fills the gap and the member stays unsubscribed, as shown before confirmation.'
    provenance: test
    source: 'react-console:acceptance/crm/playwright/journeys/merging/CRM-ACC-0254.spec.ts#CRM-ACC-0254'
edge_cases:
  - id: SC-CAP-CRM-DQ-014-03
    given: Two participant records disagree on deceased status and are linked as spouses.
    when: The administrator plans their merge.
    then: >-
      Status needs an answer, their internal spouse link clears and an outside spouse reference follows the
      survivor.
    provenance: test
    source: 'react-console:acceptance/crm/playwright/journeys/merging/CRM-ACC-0256.spec.ts#CRM-ACC-0256'
constraints: []
depends_on:
  - CAP-CRM-DQ-013
related:
  - CAP-CRM-DQ-015
  - CAP-CRM-DQ-017
  - CAP-CRM-ENT-005
  - CAP-CRM-ENT-009
journeys:
  - JNY-CRM-DQ-002
open_questions: []
evidence:
  - repository: services-crm
    source_sha: a8d1364f1dd44b723bb5f9acba12f983e9ec8d69
    paths:
      - docs/merging/README.md
      - docs/merging/decisions.md
      - docs/merging/experience/functional-requirements.md
      - modules/merges/services/merges/merge-execution.ts
      - modules/merges/services/planning/contact-merge-model.ts
      - modules/merges/tests/e2e/merges.e2e.spec.js
      - modules/merges/tests/integration/services/merges/merge-execution.integration.spec.js
      - modules/merges/tests/unit/services/planning/contact-collections.spec.js
      - modules/merges/tests/unit/services/planning/contact-fields.spec.js
      - modules/merges/tests/unit/services/planning/linked-contact-information.spec.js
      - modules/merges/tests/unit/services/planning/merge-decisions.spec.js
      - modules/merges/tests/unit/services/planning/worked-example.spec.js
    note: 'Static examination connects rule claims to implementation, declared checks and the cited tests.'
  - repository: react-console
    source_sha: baf6dae382d6f41dce976c7f848667ccc37e076f
    paths:
      - acceptance/crm/playwright/journeys/merging/CRM-ACC-0254.spec.ts
      - acceptance/crm/playwright/journeys/merging/CRM-ACC-0256.spec.ts
    note: 'Static examination connects rule claims to implementation, declared checks and the cited tests.'
preconditions: []
postconditions: []
controls: [TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-AZ-005, TC-AZ-006, TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-IAM-001, TC-IAM-002, TC-IAM-004, TC-IAM-005, TC-IAM-006, TC-LOG-004, TC-LOG-005, TC-PI-001, TC-PI-003, TC-PRV-001, TC-PRV-002, TC-TEN-001, TC-TEN-002, TC-TEN-003]
exercised_by: [FAS-CRM-DQ-03-02, FAS-CRM-DQ-04-01]
suites: [TST-CRM-DQ-176, TST-CRM-DQ-181, TST-CRM-DQ-183]
---

# Preserve the right information on the surviving contact

## Purpose

Administrators can combine contact information while seeing every value the merge discards.

## How It Works

The contact preview separates automatic combinations from real choices. Consent always takes the most restrictive result and cannot be relaxed through a merge. Identical contact details collapse, while incompatible items compete for the available slots. E-mail choices respect the three-address limit and require one primary address. The plan combines overlapping stays at an employer and keeps the broadest role periods. Linked organization details follow retained affiliations or become copies when their link is no longer usable. Special choices handle deceased status and conflicting primary organizations. Computed values are recalculated rather than selected. The survivor is saved through ordinary validation using only changed custom values. Staff review discarded values and acknowledge them before confirmation. A wrong result must be corrected afterwards through an ordinary edit or recreation.

## Relationships

Decision review records contact choices. Related-record handling moves references and login links. Merge history preserves the originals and explains values omitted from the survivor.
