---
id: ATS-CRM-REG-008
scope: CAP-CRM-REG-008
product_area: crm
module: regions
revision: 3
status: proposed
approved_by: null
approved_at: null
source_sha:
  services-crm: a8d1364f1dd44b723bb5f9acba12f983e9ec8d69
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
scenarios:
  - id: ATS-CRM-REG-008-01
    title: Placement is a reaction to committed creation
    level: api
    proves:
      - BR-CAP-CRM-REG-008-01
      - AS-JNY-CRM-REG-001-02
    preconditions: Enabled coverage matches a new contact’s address.
    steps: 'Create the contact, inspect its response, process the change and read it again.'
    expected: The response has no calculated region and the later read has the assigned region.
    negative: false
    data_needs: A contact covered by a single policy entry.
    bound_to:
      - TST-CRM-REG-058
      - TST-CRM-REG-047
      - TST-CRM-REG-021
      - TST-CRM-REG-062
    provenance: test
    source: >-
      services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#initial
      assignment › answers no region from the create itself, because the assignment is a reaction to the
      committed save
    category: transition
    test_assertions:
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#initial
        assignment › answers no region from the create itself, because the assignment is a reaction to
        the committed save
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#initial
        assignment › gives a contact created with a matching address that address's region
      - >-
        services-crm:modules/regions/tests/e2e/regions.e2e.spec.js#answers a contact posted through the
        contacts route with no region: the assignment is a reaction to the save
      - >-
        services-crm:modules/regions/tests/e2e/regions.e2e.spec.js#gives that contact its region once the
        change event has been delivered
      - 'react-console:acceptance/crm/playwright/journeys/contacts/CRM-ACC-0058.spec.ts#CRM-ACC-0058'
      - >-
        services-crm:modules/regions/tests/legacy/rolodex-regions/tests/integration/assign-region.int.spec.js#Assign
        Regions › Contacts › the region has been set
  - id: ATS-CRM-REG-008-02
    title: Moving the deciding address changes placement
    level: api
    proves:
      - BR-CAP-CRM-REG-008-02
    preconditions: A contact and organization currently belong to the first region.
    steps: Move their deciding addresses into the second region’s coverage.
    expected: Both placements move to the second region.
    negative: false
    data_needs: Two regional targets with corresponding coverage.
    bound_to:
      - TST-CRM-REG-058
      - TST-CRM-REG-066
      - TST-CRM-REG-060
    provenance: test
    source: >-
      services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#moving
      › moves a contact from region A to region B when its address moves
    category: transition
    test_assertions:
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#moving
        › moves a contact from region A to region B when its address moves
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#moving
        › moves an organization when its main address moves
      - >-
        services-crm:tests/e2e/dev/regions.dev.spec.ts#moves the contact when its address moves to
        another region
      - >-
        services-crm:modules/regions/tests/legacy/rolodex-regions/tests/e2e/assign-region.e2e.spec.js#Assign
        Regions (e2e) › Contacts › should get the contact and check that the region is correct
  - id: ATS-CRM-REG-008-03
    title: Lost deciding input clears calculated placement
    level: api
    proves:
      - BR-CAP-CRM-REG-008-03
    preconditions: Records occupy a region under policy without fallback.
    steps: Remove one deciding address and move another beyond all coverage.
    expected: Both calculated regions clear.
    negative: false
    data_needs: A pair of CRM records.
    bound_to:
      - TST-CRM-REG-058
      - TST-CRM-REG-066
    provenance: test
    source: >-
      services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#moving
      › clears the region when the deciding address is removed
    category: transition
    test_assertions:
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#moving
        › clears the region when the deciding address is removed
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#moving
        › clears the region when the new address matches no rule
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#moving
        › clears an organization's region when its main address is removed
      - 'services-crm:tests/e2e/dev/regions.dev.spec.ts#clears the region when the address matches no rule'
  - id: ATS-CRM-REG-008-04
    title: Other address edits preserve placement
    level: api
    proves:
      - BR-CAP-CRM-REG-008-04
    preconditions: Contact assignment uses Work addresses.
    steps: Edit a contact’s Home address.
    expected: The regional placement remains unchanged.
    negative: false
    data_needs: A contact with two separate address purposes.
    bound_to:
      - TST-CRM-REG-058
    provenance: test
    source: >-
      services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#moving
      › ignores a change to an address that does not decide the region
    category: invariant
    test_assertions:
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#moving
        › ignores a change to an address that does not decide the region
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#the
        deciding address type › ignores a change of the home address then
  - id: ATS-CRM-REG-008-05
    title: Configured sources determine reassignment
    level: api
    proves:
      - BR-CAP-CRM-REG-008-05
    preconditions: Contacts use preferred addresses and organizations use a named type.
    steps: Select another preferred contact address and move the organization’s typed address.
    expected: Each placement follows its configured deciding source.
    negative: false
    data_needs: CRM examples with multiple address details.
    bound_to:
      - TST-CRM-REG-058
      - TST-CRM-REG-057
    provenance: test
    source: >-
      services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#moving
      › follows the address that became preferred
    category: rule
    test_assertions:
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#moving
        › follows the address that became preferred
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#the
        deciding address type › decides by the address of the named type, not by an address of another
        type
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#the
        deciding address type › decides an organization by the address type the settings name, not by its
        main address
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment-rules.integration.spec.js#an
        organization decided by its preferred address › moves the organization when another of its
        addresses becomes the preferred one
  - id: ATS-CRM-REG-008-06
    title: No past address preserves a manual region
    level: api
    proves:
      - BR-CAP-CRM-REG-008-06
    preconditions: Automatic placement is enabled.
    steps: Create an addressless contact with a supplied region.
    expected: The manually supplied region remains.
    negative: false
    data_needs: A single regional definition.
    bound_to:
      - TST-CRM-REG-058
    provenance: test
    source: >-
      services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#initial
      assignment › keeps a region the client sends with a contact that has no address (nothing decides
      it)
    category: edge
    test_assertions:
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#initial
        assignment › keeps a region the client sends with a contact that has no address (nothing decides
        it)
  - id: ATS-CRM-REG-008-07
    title: Locks and deletion cause successful skips
    level: api
    proves:
      - BR-CAP-CRM-REG-008-07
    preconditions: One contact is fully locked and another is deleted before delivery.
    steps: Process both pending changes.
    expected: Processing succeeds without updating either record.
    negative: false
    data_needs: A pair of contact records.
    bound_to: []
    provenance: proposed
    source: FND-CRM-REG-022
    category: edge
  - id: ATS-CRM-REG-008-08
    title: Current state defeats duplicate and reversed delivery
    level: api
    proves:
      - BR-CAP-CRM-REG-008-08
    preconditions: A contact has committed two address saves.
    steps: 'Deliver the later event first, the earlier event afterward and a duplicate event.'
    expected: Placement reflects current address state and repeated delivery adds no write.
    negative: false
    data_needs: A single contact record.
    bound_to:
      - TST-CRM-REG-056
    provenance: test
    source: >-
      services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment-delivery.integration.spec.js#two
      messages delivered out of order › ends in the region of the address the contact has now, not of the
      one the older message described
    category: concurrency
    test_assertions:
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment-delivery.integration.spec.js#two
        messages delivered out of order › ends in the region of the address the contact has now, not of
        the one the older message described
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment-delivery.integration.spec.js#two
        messages delivered out of order › writes once for the two deliveries, because the second finds
        the fingerprint the first wrote
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment-delivery.integration.spec.js#the
        same message delivered twice › writes nothing the second time: the fingerprint says the deciding
        address is the one it decided from
  - id: ATS-CRM-REG-008-09
    title: Assignment changes do not loop
    level: api
    proves:
      - BR-CAP-CRM-REG-008-09
    preconditions: Automatic placement is enabled.
    steps: 'Save an address, process assignment and inspect its generated change.'
    expected: The generated change contains regional fields without addresses and invokes no further assignment.
    negative: false
    data_needs: A single contact record.
    bound_to:
      - TST-CRM-REG-056
    provenance: test
    source: >-
      services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment-delivery.integration.spec.js#one
      delivery of one address change › records its own write as a change of the region and the
      fingerprint, and of no address
    category: invariant
    test_assertions:
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment-delivery.integration.spec.js#one
        delivery of one address change › records its own write as a change of the region and the
        fingerprint, and of no address
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment-delivery.integration.spec.js#one
        delivery of one address change › is not called again by its own write, so an assignment cannot
        set off another
  - id: ATS-CRM-REG-008-10
    title: Version effects depend on placement change
    level: api
    proves:
      - BR-CAP-CRM-REG-008-10
    preconditions: A contact and organization belong to the first region.
    steps: 'Edit within that region, then move both into the second region.'
    expected: The first assignment preserves save stamps; the changed placements each add one version increment.
    negative: false
    data_needs: A pair of CRM records.
    bound_to:
      - TST-CRM-REG-056
      - TST-CRM-REG-058
    provenance: test
    source: >-
      services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment-delivery.integration.spec.js#a
      deciding-address change that leaves the region where it was › bumps no version, so the contact
      stands at the version its save answered
    category: invariant
    test_assertions:
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment-delivery.integration.spec.js#a
        deciding-address change that leaves the region where it was › bumps no version, so the contact
        stands at the version its save answered
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment-delivery.integration.spec.js#a
        deciding-address change that leaves the region where it was › leaves the last-modified stamp the
        save wrote
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment-delivery.integration.spec.js#a
        deciding-address change that moves the region › bumps the version exactly once past the save,
        because the region moved
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#the
        version an organization’s assignment bumps › leaves an organization at the version its save
        answered when its region does not change
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#the
        version an organization’s assignment bumps › bumps it once past the save when the region moves
  - id: ATS-CRM-REG-008-11
    title: Linked placement follows organization address edits
    level: api
    proves:
      - BR-CAP-CRM-REG-008-11
      - AS-JNY-CRM-REG-005-01
    preconditions: A contact uses an address linked from its employer.
    steps: Move the employer address into new coverage.
    expected: The contact region and version change without writing its address rows.
    negative: false
    data_needs: An organization and a contact with an effective affiliation.
    bound_to:
      - TST-CRM-REG-058
      - TST-CRM-REG-028
    provenance: test
    source: >-
      services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#a
      linked address (docs/affiliations/README.md §0b.3, the fan-out) › moves the linked contact when its
      organization’s address moves, though no contact row was written
    category: interaction
    test_assertions:
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#a
        linked address (docs/affiliations/README.md §0b.3, the fan-out) › moves the linked contact when
        its organization’s address moves, though no contact row was written
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#a
        linked address (docs/affiliations/README.md §0b.3, the fan-out) › bumps the contact’s version
        once, for the region that moved and nothing else
      - 'react-console:acceptance/crm/playwright/journeys/organizations/CRM-ACC-0119.spec.ts#CRM-ACC-0119'
  - id: ATS-CRM-REG-008-12
    title: Expiry alone leaves the remembered placement
    level: api
    proves:
      - BR-CAP-CRM-REG-008-12
    preconditions: A linked deciding address uses an affiliation ending tomorrow.
    steps: Pass the end date and perform daily affiliation reconciliation.
    expected: 'The contact retains its stored region, exposing the expiry-trigger gap.'
    negative: false
    data_needs: A dated relationship reaching its end boundary.
    bound_to: []
    provenance: proposed
    source: >-
      services-crm:modules/entities/services/persistence/contacts.ts:readContactRegionAddresses;
      services-crm:modules/entities/services/affiliations/affiliation-date-reconciliation.ts
    category: edge
  - id: ATS-CRM-REG-008-13
    title: Redelivery can recover assignment
    level: api
    proves:
      - BR-CAP-CRM-REG-008-13
    preconditions: The first delivery encounters a failure.
    steps: Process a failing delivery and a subsequent successful one.
    expected: The failure assigns nothing and the subsequent delivery assigns the expected region.
    negative: false
    data_needs: An injected assignment failure.
    bound_to:
      - TST-CRM-REG-056
    provenance: test
    source: >-
      services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment-delivery.integration.spec.js#a
      delivery that fails and is made again › assigned nothing while it was failing
    category: failure
    test_assertions:
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment-delivery.integration.spec.js#a
        delivery that fails and is made again › assigned nothing while it was failing
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment-delivery.integration.spec.js#a
        delivery that fails and is made again › assigns the region on the next delivery of the same
        message
  - id: ATS-CRM-REG-008-14
    title: Workspace policies do not leak
    level: api
    proves:
      - BR-CAP-CRM-REG-008-14
    preconditions: Two workspaces have different policies and a third has no settings.
    steps: Create equally addressed contacts in all three.
    expected: Each configured workspace uses its own rules and the third leaves placement unchanged.
    negative: false
    data_needs: Three separate customer workspaces.
    bound_to:
      - TST-CRM-REG-057
      - TST-CRM-REG-058
    provenance: test
    source: >-
      services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment-rules.integration.spec.js#tenant
      isolation, with rules on both sides › leaves a contact at that same address unassigned here,
      because the rule that covers it is the neighbour’s
    category: boundary
    test_assertions:
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment-rules.integration.spec.js#tenant
        isolation, with rules on both sides › leaves a contact at that same address unassigned here,
        because the rule that covers it is the neighbour’s
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment-rules.integration.spec.js#tenant
        isolation, with rules on both sides › assigns a contact of the neighbour tenant from the
        neighbour’s own rules
      - >-
        services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js#tenant
        isolation › assigns nothing in a tenant with no settings, whatever another tenant's rules say
  - id: ATS-CRM-REG-008-15
    title: Placement combines source selection matching and exceptions
    category: interaction
    level: api
    proves:
      - BR-CAP-CRM-REG-003-04
      - BR-CAP-CRM-REG-008-05
      - BR-CAP-CRM-REG-009-01
      - BR-CAP-CRM-REG-009-03
      - BR-CAP-CRM-REG-011-06
    preconditions: Two regions cover the home and work addresses of a contact with both addresses.
    steps: >-
      Select work as the deciding source, deliver a work move, hold a manual region, move work again and
      recalculate; release the hold and deliver the override reaction.
    expected: >-
      Unheld placement follows the selected source and matching coverage; the held value survives address
      processing and recalculation, and release evaluates changed input.
    negative: false
    data_needs: Two regions cover the home and work addresses of a contact with both addresses.
    bound_to: []
    provenance: proposed
    source: WF-CRM-REG-001
    exercises:
      - CAP-CRM-REG-003
      - CAP-CRM-REG-006
      - CAP-CRM-REG-008
      - CAP-CRM-REG-009
      - CAP-CRM-REG-011
  - id: ATS-CRM-REG-008-16
    title: Update locks exclude placement writes without requiring retries
    category: invariant
    level: api
    proves:
      - INV-DOM-CRM-REG-003-06
      - BR-CAP-CRM-REG-008-07
    preconditions: >-
      An eligible contact and organization carry full update locks while their deciding addresses match
      saved rules.
    steps: 'Deliver assignment and force recalculation, then read placement, version and remembered input.'
    expected: Locked targets are skipped successfully and processing commits no regional write on them.
    negative: false
    data_needs: >-
      An eligible contact and organization carry full update locks while their deciding addresses match
      saved rules.
    bound_to: []
    provenance: proposed
    source: >-
      services-crm:modules/regions/services/region-assignment/region-assignment.ts:reconcileRegionAssignments
evidence:
  - >-
    services-crm: Examined the pinned regional implementation, its declared constraints and the
    supporting records.
  - 'react-console: Examined the pinned regional configuration surfaces and their available test evidence.'
  - >-
    services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment.integration.spec.js
  - 'services-crm:modules/regions/tests/e2e/regions.e2e.spec.js'
  - 'react-console:acceptance/crm/playwright/journeys/contacts/CRM-ACC-0058.spec.ts'
  - 'services-crm:modules/regions/tests/legacy/rolodex-regions/tests/integration/assign-region.int.spec.js'
  - 'services-crm:tests/e2e/dev/regions.dev.spec.ts'
  - 'services-crm:modules/regions/tests/legacy/rolodex-regions/tests/e2e/assign-region.e2e.spec.js'
  - >-
    services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment-rules.integration.spec.js
  - >-
    services-crm:modules/regions/tests/integration/services/region-assignment/region-assignment-delivery.integration.spec.js
  - 'react-console:acceptance/crm/playwright/journeys/organizations/CRM-ACC-0119.spec.ts'
  - 'services-crm:modules/entities/services/persistence/contacts.ts'
  - 'services-crm:modules/entities/services/affiliations/affiliation-date-reconciliation.ts'
coverage:
  rule: covered
  transition: covered
  validation: 'gap: FND-CRM-REG-053'
  permission: not-applicable
  edge: covered
  failure: covered
  boundary: covered
  concurrency: covered
  invariant: covered
  interaction: covered
coverage_notes:
  permission: >-
    Captured-event processing declares no user action key; record saves are authorized by the owning
    record capability.
---

# Keep placements aligned with address changes: Capability Test Specification

## Coverage

Each row links a required rule to its authored proof. A covered category records a specification; executable and remaining variant gaps stay in the finding register.

| Rule | Proved By |
|---|---|
| `BR-CAP-CRM-REG-008-01` | `ATS-CRM-REG-008-01` |
| `BR-CAP-CRM-REG-008-02` | `ATS-CRM-REG-008-02` |
| `BR-CAP-CRM-REG-008-03` | `ATS-CRM-REG-008-03` |
| `BR-CAP-CRM-REG-008-04` | `ATS-CRM-REG-008-04` |
| `BR-CAP-CRM-REG-008-05` | `ATS-CRM-REG-008-05`, `ATS-CRM-REG-008-15` |
| `BR-CAP-CRM-REG-008-06` | `ATS-CRM-REG-008-06` |
| `BR-CAP-CRM-REG-008-07` | `ATS-CRM-REG-008-07`, `ATS-CRM-REG-008-16` |
| `BR-CAP-CRM-REG-008-08` | `ATS-CRM-REG-008-08` |
| `BR-CAP-CRM-REG-008-09` | `ATS-CRM-REG-008-09` |
| `BR-CAP-CRM-REG-008-10` | `ATS-CRM-REG-008-10` |
| `BR-CAP-CRM-REG-008-11` | `ATS-CRM-REG-008-11` |
| `BR-CAP-CRM-REG-008-12` | `ATS-CRM-REG-008-12` |
| `BR-CAP-CRM-REG-008-13` | `ATS-CRM-REG-008-13` |
| `BR-CAP-CRM-REG-008-14` | `ATS-CRM-REG-008-14` |

## Interactions With Related Capabilities

Placement combines source selection matching and exceptions. The interaction scenario connects configure automatic placement and its address sources, determine an address’s geographic region, preserve a deliberate regional exception, apply the current policy across existing records with this capability. Cross-workflow expectations are proposed where no executable binding is present.
