---
id: WF-CRM-DQ-006
title: Execute a merge
trigger: >-
  An administrator confirms the preview and accepts its defaulted choices and
  disclosed omissions.
product_area: crm
module: data-quality
revision: 1
status: draft
design_intent: proposed
implementation_evidence: observed
inference: []
source_sha:
  services-crm: a8d1364f1dd44b723bb5f9acba12f983e9ec8d69
implements:
  - CAP-CRM-DQ-016
  - CAP-CRM-DQ-014
  - CAP-CRM-DQ-015
  - CAP-CRM-DQ-017
  - CAP-CRM-DQ-018
  - CAP-CRM-DQ-019
moves:
  - STATE-CRM-DQ-004
  - STATE-CRM-DQ-005
  - STATE-CRM-DQ-006
  - STATE-CRM-DQ-001
crosses:
  - SVC-CRM-DQ-merges
  - SVC-CRM-ENT-entities
  - SVC-CRM-DQ-duplicates
idempotency: >-
  Execution requests reuse the plan’s active or successful attempt and job;
  partial uniqueness resolves concurrent insertion. Worker claim and terminal
  settlement are conditional, making repeated delivery and failure recording
  safe.
concurrency: >-
  Execution obtains participant advisory locks and row locks in fixed order.
  Lock waits are limited to five seconds and statements to five minutes;
  overlapping attempts may wait or time out, and an attempt reaching an absorbed
  participant fails.
hops:
  - 'n': 1
    actor_or_service: Merges route
    action: >-
      Require planning authority, enforce expiry and readiness, reuse an
      existing attempt or recompute and check approval and execution limits.
    anchor: >-
      services-crm:modules/merges/services/merges/merge-plans.ts:queueMergeExecution
    seam: sync
    on_failure: Return a refreshed refusal without creating an attempt.
    left_inconsistent: No merge business changes are applied at submission refusal.
    observer: administrator.
    provenance: test
    source: >-
      services-crm:modules/merges/tests/integration/services/merges/approval-binding.integration.spec.js#refuses
      an execution that submits no hash too, because the stored plan is the one
      last shown
  - 'n': 2
    actor_or_service: Merges persistence
    action: >-
      Commit the queued attempt, captured participant context, executing-plan
      marker and durable job together.
    anchor: >-
      services-crm:modules/merges/services/merges/merge-plans.ts:queueTheMergeOfThisPlan
    seam: sync
    on_failure: Roll back the submission transaction.
    left_inconsistent: None of the submitted records partially commits.
    observer: administrator.
    provenance: test
    source: >-
      services-crm:modules/merges/tests/integration/services/jobs/job-reconciliation.integration.spec.js#settles
      the job failed, rather than leaving it in flight for ever
  - 'n': 3
    actor_or_service: Merges route
    action: Start the committed job workflow after the request transaction.
    anchor: >-
      services-crm:modules/merges/services/jobs/record-merge-job.ts:startRecordMergeJob
    seam: state_machine
    on_failure: >-
      Leave a lost start for reconciliation without reversing accepted
      submission.
    left_inconsistent: >-
      The durable attempt may remain queued without workflow execution until
      settlement.
    observer: job monitoring page and the reconciler.
    provenance: observed
    source: >-
      services-crm:modules/merges/services/jobs/record-merge-job.ts:startRecordMergeJob
  - 'n': 4
    actor_or_service: Record-merge state machine
    action: >-
      Publish job definitions and invoke the single merge task under the
      workflow retry policy.
    anchor: >-
      services-crm:modules/merges/services/jobs/record-merge-job.ts:recordMergeWorkflow
    seam: state_machine
    on_failure: Invoke workflow failure settlement when execution cannot finish.
    left_inconsistent: Job reporting can lag the business attempt outcome.
    observer: Job steps and events.
    provenance: observed
    source: >-
      services-crm:modules/merges/services/jobs/record-merge-job.ts:recordMergeWorkflow
  - 'n': 5
    actor_or_service: Merge executor
    action: >-
      Claim and lock in one transaction, replan against approval, move
      references through owners, save the survivor, remove absorbed records and
      persist aliases, outcomes, audit and contact events.
    anchor: >-
      services-crm:modules/merges/services/merges/merge-execution.ts:executeMerge
    seam: sync
    on_failure: >-
      Roll back business changes, then record failure and reopen an eligible
      plan separately.
    left_inconsistent: >-
      Rollback restores queued standing until failure settlement; previously
      committed job reporting can say running.
    observer: administrator through polling; job steps.
    provenance: observed
    source: >-
      services-crm:modules/merges/services/merges/merge-execution.ts:executeMerge
  - 'n': 6
    actor_or_service: Merge job task
    action: >-
      Map the business result to the four watched steps and settle their
      outcome.
    anchor: 'services-crm:modules/merges/services/jobs/record-merge-job-tasks.ts'
    seam: state_machine
    on_failure: Workflow retry handles eligible task-reporting errors.
    left_inconsistent: A committed merge can precede complete job reporting.
    observer: Job monitoring.
    provenance: observed
    source: 'services-crm:modules/merges/db/record-merge-jobs.table.ts'
  - 'n': 7
    actor_or_service: CRM business-events dispatcher
    action: >-
      Nudge contact-event dispatch after commit, with scheduled delivery as
      fallback.
    anchor: >-
      services-crm:modules/merges/services/merges/merge-events.ts:nudgeMergeEvents
    seam: event
    on_failure: Retain the event for the dispatcher’s recovery path.
    left_inconsistent: Subscribers can learn the committed outcome later.
    observer: Subscribers.
    provenance: test
    source: >-
      services-crm:modules/merges/tests/e2e/business-events.e2e.spec.js#hands
      the subscriber the envelope of crm.contact.merged, about the survivor, in
      the merge’s tenant
  - 'n': 8
    actor_or_service: Change-event handlers
    action: >-
      Send ordinary save and delete changes to duplicate, search and region
      consumers.
    anchor: >-
      services-crm:modules/duplicates/services/detection/duplicate-change-events.ts:detectForChangeEvents
    seam: event
    on_failure: Each owning handler uses its documented queue recovery.
    left_inconsistent: Derived projections can lag the successful merge.
    observer: Handler queues and their dead-letter queues.
    provenance: documented
    source: >-
      services-crm:docs/merging/architecture.md#7.3 Downstream effects of the
      deletes and the survivor update
branches:
  - >-
    Organization execution moves and combines affiliations and repoints linked
    employee details using the reviewed slot decisions.
  - >-
    Platform participation owns single-login relinking and refusal of multiple
    login access.
  - >-
    The owner-deletion safety switch defaults to enabled in development only;
    when disabled, delete-time detection of associations missed by participants
    does not run.
waits:
  - >-
    The console polls at one-second intervals initially and five-second
    intervals after thirty seconds; leaving the page does not cancel execution.
evidence:
  - >-
    services-crm at a8d1364f: modules/merges/services/merges/merge-plans.ts,
    merge-execution.ts, merge-settlement.ts, merge-events.ts;
    services/jobs/*.ts; services/persistence/merge-executions.ts,
    merge-participants.ts, merged-record-aliases.ts, record-merges.ts;
    aws/crm.record-merges.names.ts (task 900 s, workflow 1,260 s);
    aws/crm.names.ts (CRM_OWNER_DELETION_RULES, on in dev only);
    docs/merging/architecture.md §5.
  - >-
    react-console at baf6dae3:
    components/duplicates/merge/use-merge-execution.ts, merge-progress.tsx.
controls: [TC-AVL-004, 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-DAT-004, TC-IAM-001, TC-IAM-002, TC-IAM-004, TC-IAM-005, TC-IAM-006, TC-LOG-001, TC-LOG-002, TC-LOG-003, TC-LOG-004, TC-LOG-005, TC-MON-001, TC-MON-002, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-004, TC-PI-005, TC-PRV-001, TC-PRV-002, TC-PRV-003, TC-TEN-001, TC-TEN-002, TC-TEN-003]
---

# Execute A Merge

## What It Does

Submission persists a durable attempt before background execution. The worker replans under lock and applies all business changes within one transaction, including calls to application-owned participant functions.

## The Flow

```mermaid
flowchart TD
  H1["Require planning authority, enforce expiry and readiness, reuse an existing attempt or recompute and check approval and execution limits."]
  H2["Commit the queued attempt, captured participant context, executing-plan marker and durable job together."]
  H1 --> H2
  H3["Start the committed job workflow after the request transaction."]
  H2 --> H3
  H4["Publish job definitions and invoke the single merge task under the workflow retry policy."]
  H3 --> H4
  H5["Claim and lock in one transaction, replan against approval, move references through owners, save the survivor, remove absorbed records and persist aliases, outcomes, audit and contact events."]
  H4 --> H5
  H6["Map the business result to the four watched steps and settle their outcome."]
  H5 --> H6
  H7["Nudge contact-event dispatch after commit, with scheduled delivery as fallback."]
  H6 --> H7
  H8["Send ordinary save and delete changes to duplicate, search and region consumers."]
  H7 --> H8
```

## When Something Fails

Business rollback requires no compensating changes. The executor normally records caught failures, including lock timeouts, and the task reports those as refusals rather than retrying the business attempt. Transient errors outside that completed failure handling may be retried. Lost workflows need reconciliation, and post-commit projections and announcements can still lag.

## Open Design Questions

The default-off owner-deletion safety check outside development needs explicit preparation. Organization announcements, deletion of later survivors and conflicting lost-start timing remain recorded open edges.
