---
id: WF-CRM-REG-002
title: Follow an organization address change to its linked contacts
trigger: An organization address update commits and reaches its registered change handlers.
product_area: crm
module: regions
revision: 2
status: draft
design_intent: proposed
implementation_evidence: observed
inference: []
source_sha:
  services-crm: a8d1364f1dd44b723bb5f9acba12f983e9ec8d69
implements:
  - CAP-CRM-REG-008
moves:
  - STATE-CRM-REG-003
crosses:
  - SVC-CRM-ENT-entities
  - SVC-CRM-REG-regions
idempotency: >-
  Each contact decision compares resolved input with the remembered fingerprint. A redelivery revisits already
  committed batches but ordinarily performs no new write for equal input. Later input changes can still
  require another decision.
concurrency: >-
  The handler processes at most 500 collected contact identifiers per reconciliation transaction. Those
  contact locks coordinate competing contact saves. Organization placement is a separate handler reaction, and
  organization address reads are not locked by contact processing.
hops:
  - 'n': 1
    actor_or_service: Entities organization save
    action: >-
      Entities saves the changed organization address. Linked contact values resolve on read, so this address
      edit does not itself save every contact.
    anchor: 'services-crm:modules/entities/services/persistence/organizations.ts (organization save)'
    seam: sync
    on_failure: Failure rolls back the organization save and its capture.
    left_inconsistent: No committed organization address change remains from that failure.
    observer: The organization caller receives its save outcome.
    provenance: documented
    source: 'services-crm:docs/change-events.md#The handlers CRM registers'
  - 'n': 2
    actor_or_service: Change-events dispatcher and consumer
    action: Shared change delivery routes the organization-address event to the linked-contact regional handler.
    anchor: 'services-crm:aws/crm.change-events.ts (organization stream)'
    seam: queue
    on_failure: The queue retries failed handling and eventually dead-letters an exhausted message.
    left_inconsistent: Linked contacts can retain placements derived from the preceding address.
    observer: Consumer results and dead-letter messages identify unsuccessful delivery.
    provenance: observed
    source: 'services-crm:aws/crm.change-events.ts'
  - 'n': 3
    actor_or_service: Linked-contact handler
    action: The handler extracts related address identifiers and asks Entities for contacts linked to them.
    anchor: >-
      services-crm:modules/regions/services/region-assignment/region-assignment.ts:linkedContactRegionAssignmentHandler
    seam: sync
    on_failure: A failed lookup propagates the handler error and requests another delivery through the queue.
    left_inconsistent: No contact changes occur before identifiers are collected.
    observer: Successful results report linked-contact and regional work counts.
    provenance: observed
    source: 'services-crm:modules/entities/services/persistence/contacts.ts:listContactsLinkingOrganizationRecords'
  - 'n': 4
    actor_or_service: 'Region assignment reconcile, per batch'
    action: >-
      Each collected batch uses common reconciliation to resolve current effective linked addresses and update
      eligible placements.
    anchor: 'services-crm:modules/regions/services/region-assignment/region-assignment.ts:reconcileRegionAssignments'
    seam: sync
    on_failure: >-
      The failed batch rolls back while earlier batches remain committed; redelivery revisits the collected
      contacts.
    left_inconsistent: Unprocessed contacts can retain earlier regions even though the organization address has changed.
    observer: Handler results count examined contacts and changed regional values.
    provenance: test
    source: >-
      services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#region
      assignment (integration) › a linked address (docs/affiliations/README.md §0b.3, the fan-out) › gives a
      contact linked to its organization's address that address's region
branches:
  - >-
    Removing an organization address converts linked rows into copies; those contact writes supply their own
    ordinary address reactions.
  - >-
    A link through an expired affiliation supplies no effective address. A later organization event can clear
    a remembered placement, but expiry alone starts no regional work.
waits: []
evidence:
  - >-
    services-crm at a8d1364f: modules/regions/services/region-assignment/region-assignment.ts
    (linkedContactRegionAssignmentHandler); modules/entities/services/persistence/contacts.ts
    (listContactsLinkingOrganizationRecords, readContactRegionAddresses); docs/change-events.md;
    tests/integration/services/region-assignment/region-assignment.integration.spec.js.
controls: [TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-IAM-001, TC-IAM-005, TC-IAM-006, 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-TEN-001, TC-TEN-002, TC-TEN-003]
---

# Follow an organization address change to its linked contacts

## What It Does

A contact can use an address linked from its organization. Editing the organization changes the resolved value without copying it into each contact. Regions therefore handles that organization event separately, finds contacts linked to the affected addresses and revisits their placement.

## The Flow

```mermaid
sequenceDiagram
  participant O as Organization save
  participant Q as Change queue
  participant R as Linked-contact handler
  participant E as Entities
  O->>Q: Captured address change
  Q->>R: Deliver reaction
  R->>E: Find contacts linked to changed addresses
  loop Collected batches of 500
    R->>E: Lock contacts and resolve deciding input
    R->>R: Evaluate regional coverage
    R->>E: Persist changed placements
  end
  R-->>Q: Delivery completed
```

## When Something Fails

Batches commit independently. Failure leaves earlier batches intact and rolls back the failing batch. A redelivery revisits contacts, using their fingerprints to avoid unnecessary writes. Queue exhaustion leaves remaining placement differences for operator redrive or a later regional trigger; no batch compensation reverses successful work.

## Open Design Questions

An affiliation’s end date does not itself start placement processing. Contact locks also do not freeze the linked organization address. The product and implementation must resolve these limits before promising immediate placement accuracy across linked records.
