---
id: CAP-CRM-DQ-009
title: Record a duplicate that matching missed
feature: FEAT-CRM-DQ-02
product_area: crm
module: data-quality
revision: 1
status: draft
product_intent: confirmed
implementation_evidence: partially-observed
supersedes:
  - CAP-CRM-MRG-009
source_sha:
  services-crm: a8d1364f1dd44b723bb5f9acba12f983e9ec8d69
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources:
  - >-
    services-crm:docs/merging/experience/functional-requirements.md#MAN-7 and REV-6 (package approved by
    Andrew 2026-10-13)
  - 'services-crm:docs/merging/README.md#Status (architecture and plan approved by Andrew 2026-10-11)'
purpose: Reviewers can assert that two records describe one identity even without matching evidence.
outcomes:
  - Manual confirmation covers known shared identities missed by detection.
  - The product should let nonmerging reviewers hand confirmed duplicates to administrators.
concepts:
  - CON-CRM-DQ-001
actors:
  - ACT-CRM-008
surfaces:
  - api
  - console
permissions:
  - key: crm.duplicates.review
    where_checked: api
    scope: tenant
rules:
  - id: BR-CAP-CRM-DQ-009-01
    proved_by: [ATS-CRM-DQ-009-01, ATS-CRM-DQ-009-07, ATS-CRM-DQ-009-09, TST-CRM-DQ-180]
    statement: >-
      Manual confirmation stores a confirmed pair at score 100 with manual origin and one reason identifying
      the reviewer and note.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/duplicates/tests/integration/services/persistence/duplicate-candidates.integration.spec.js#is
      confirmed, manual and at full score: certainty, not a computation
    origin: 'services-crm:docs/merging/duplicate-detection.md#8. Dismiss, confirm, reconsider'
  - id: BR-CAP-CRM-DQ-009-02
    proved_by: [ATS-CRM-DQ-009-02, ATS-CRM-DQ-009-05, ATS-CRM-DQ-009-06, ATS-CRM-DQ-009-09, TST-CRM-DQ-180]
    statement: >-
      Confirming an existing candidate updates it in place, replaces scored reasons with the human reason and
      overrules any dismissal.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/duplicates/tests/integration/services/persistence/duplicate-candidates.integration.spec.js#takes
      the dismissal off, the person having just overruled it
    origin: none
  - id: BR-CAP-CRM-DQ-009-03
    proved_by: [ATS-CRM-DQ-009-03, ATS-CRM-DQ-009-09, TST-CRM-DQ-180, TST-CRM-DQ-182]
    statement: >-
      Confirmation requires exactly two distinct, existing workspace records of one type, rejects repetition
      and returns not found for a missing record.
    kind: validation
    enforced_at:
      - api
      - service
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/duplicates/tests/e2e/duplicates.e2e.spec.js#refuses a manual pair that does not
      name two records (400)
    origin: none
  - id: BR-CAP-CRM-DQ-009-04
    proved_by: [ATS-CRM-DQ-009-04, ATS-CRM-DQ-009-09, TST-CRM-DQ-182]
    statement: Duplicate-review authority is required to confirm; duplicate-read authority alone is refused.
    kind: permission
    enforced_at:
      - api
    must_prove: true
    provenance: test
    source: >-
      services-crm:modules/duplicates/tests/e2e/duplicates.e2e.spec.js#answers 403 to that reader saying two
      records are the same
    origin: 'services-crm:docs/merging/architecture.md#9. Security and tenant isolation'
  - id: BR-CAP-CRM-DQ-009-05
    proved_by: []
    statement: >-
      The response presents the confirmed pair identically whether the service inserted it or converted an
      existing candidate.
    kind: policy
    enforced_at:
      - api
    must_prove: false
    provenance: test
    source: >-
      services-crm:modules/duplicates/tests/e2e/duplicates.e2e.spec.js#confirms a pair somebody says is the
      same person, at full score and by hand, answering 200 and not 201
    origin: none
  - id: BR-CAP-CRM-DQ-009-06
    proved_by: [ATS-CRM-DQ-009-08, ATS-CRM-DQ-009-09]
    statement: >-
      The product should let reviewers without merge authority confirm duplicates and add another record to
      the reviewed group from the console.
    kind: policy
    enforced_at:
      - console
    must_prove: true
    provenance: unknown
    source: FND-CRM-DQ-012
    origin: 'services-crm:docs/merging/experience/functional-requirements.md#MAN-7'
scenarios:
  - id: SC-CAP-CRM-DQ-009-01
    given: A candidate is dismissed.
    when: A reviewer manually confirms the shared identity.
    then: The same candidate becomes confirmed at full score with the human reason and no group membership.
    provenance: test
    source: >-
      services-crm:modules/duplicates/tests/integration/services/persistence/duplicate-candidates.integration.spec.js#is
      the same candidate, confirmed in place
  - id: SC-CAP-CRM-DQ-009-02
    given: A pair has manual origin and confirmed status.
    when: A record edit reduces its computed resemblance.
    then: The updated score retains the confirmation and manual origin.
    provenance: unknown
    source: FND-CRM-DQ-031
edge_cases: []
constraints: []
depends_on: []
related:
  - CAP-CRM-DQ-007
  - CAP-CRM-DQ-012
journeys: []
open_questions:
  - OQ-CRM-DQ-004
evidence:
  - repository: services-crm
    source_sha: a8d1364f1dd44b723bb5f9acba12f983e9ec8d69
    paths:
      - docs/merging/README.md
      - docs/merging/architecture.md
      - docs/merging/duplicate-detection.md
      - docs/merging/experience/functional-requirements.md
      - modules/duplicates/db/duplicate-candidates.table.ts
      - modules/duplicates/tests/e2e/duplicates.e2e.spec.js
      - modules/duplicates/tests/integration/services/persistence/duplicate-candidates.integration.spec.js
    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-006, TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-LOG-001, TC-LOG-002, TC-LOG-003, TC-LOG-004, TC-LOG-005, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-005, TC-PRV-001, TC-PRV-002, TC-TEN-001, TC-TEN-002, TC-TEN-003]
exercised_by: [FAS-CRM-DQ-02-04, FAS-CRM-DQ-02-05]
suites: [TST-CRM-DQ-180, TST-CRM-DQ-182]
---

# Record a duplicate that matching missed

## Purpose

Reviewers can assert that two records describe one identity even without matching evidence.

## How It Works

A reviewer submits two records of the same type through the API. Both must exist in the current workspace and be different records. The service records a manual confirmation at full score. The reason retains the reviewer and optional note. If a pair already exists, confirmation changes that pair in place. It replaces computed evidence and overrules a dismissal. Confirmed pairs leave ordinary groups. Subsequent detection can change their score but retains the manual origin and confirmed decision. Duplicate-review permission is required. The product should connect the planned console controls so reviewers without merge authority can make this assertion.

## Relationships

Detection supplies computed suggestions, while manual confirmation supplies a human decision. Merge planning remains a separate action requiring additional authority.
