---
id: WF-CRM-REG-004
title: Record a policy change and ask for recalculation
trigger: >-
  A complete policy save, an individual-rule API write or retirement of an enabled regional target records a
  policy change.
product_area: crm
module: regions
revision: 2
status: draft
design_intent: confirmed
origin: >-
  Andrew, 2026-10-11 (rules are managed one at a time; rule or setting changes move no record; manual
  Recalculate with a recalculation-needed banner) and 2026-10-15 (a read-only Assignment Rules tab with its
  own editor screen and the banner), cited in services-crm:docs/migrations/crm-regions/README.md; DEF-CRM-0339
  (an individual rule write advances the settings version)
implementation_evidence: observed
inference: []
source_sha:
  services-crm: a8d1364f1dd44b723bb5f9acba12f983e9ec8d69
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
implements:
  - CAP-CRM-REG-003
  - CAP-CRM-REG-004
  - CAP-CRM-REG-005
  - CAP-CRM-REG-010
  - CAP-CRM-REG-002
moves:
  - STATE-CRM-REG-002
  - STATE-CRM-REG-001
crosses:
  - SVC-CRM-REG-regions
idempotency: >-
  Accepted policy writes increment generation even when values are unchanged. A versioned repeat can become
  stale after its first save; unversioned settings writes can repeat and request further recalculation.
concurrency: >-
  Settings and individual-rule writes lock an existing policy row. Individual writes advance settings version
  so an older versioned whole-policy save conflicts. Omitted settings versions bypass that protection, and
  competing first saves can collide during insertion.
hops:
  - 'n': 1
    actor_or_service: Console assignment rules editor
    action: >-
      The editor stages assignment choices and ordered coverage. Enabled assignment activates blocking checks;
      coverage and inactive-target analysis supplies warnings.
    anchor: 'react-console:components/regions/assignment-draft.ts:assignmentIssues'
    seam: sync
    on_failure: Blocking issues stop submission and can be revealed at their corresponding controls.
    left_inconsistent: Unsaved policy remains confined to the editor draft.
    observer: 'Staff see the staged change count, issues and warnings.'
    provenance: test
    source: 'react-console:components/regions/__tests__/assignment-draft.test.ts'
  - 'n': 2
    actor_or_service: Regions API (updateRegionAssignmentSettings)
    action: >-
      The API validates request shape before normalization and service-level problem collection. Reference
      refusals are resolved after transaction rollback.
    anchor: >-
      services-crm:modules/regions/services/persistence/region-assignment-settings.ts:updateRegionAssignmentSettings
    seam: sync
    on_failure: >-
      Invalid input is rejected before persistence; schema and service checks do not promise one combined list
      of every possible database problem.
    left_inconsistent: Rejected input leaves stored policy unchanged.
    observer: The editor shows validation or save feedback.
    provenance: observed
    source: >-
      services-crm:modules/regions/services/persistence/region-assignment-settings.ts:assertRegionAssignmentSettingsValid
  - 'n': 3
    actor_or_service: Settings persistence
    action: >-
      Persistence locks existing settings, checks any supplied version, updates settings and generation, and
      replaces all rules when the request includes a list.
    anchor: >-
      services-crm:modules/regions/services/persistence/region-assignment-settings.ts:saveRegionAssignmentSettings
    seam: sync
    on_failure: >-
      Stale versions or invalid references roll back the transaction and return their refusal. Concurrent
      first insertion can fail on the unique settings key.
    left_inconsistent: >-
      Failure leaves policy intact. Success recreates retained rule identifiers with fresh version-one
      records.
    observer: The returned settings update the editor and saved-policy cache.
    provenance: observed
    source: 'services-crm:modules/regions/services/persistence/region-assignment-settings.ts:insertRule'
  - 'n': 4
    actor_or_service: Rule persistence (individual rule routes)
    action: >-
      Individual writes and reorder take the settings lock, creating disabled defaults when needed, then
      update rules and stamp generation and settings version.
    anchor: 'services-crm:modules/regions/services/persistence/region-assignment-settings.ts:stampRulesChanged'
    seam: sync
    on_failure: The transaction rolls back on failure; applicable rule version conflicts return a stale-write refusal.
    left_inconsistent: No partial individual-rule write commits.
    observer: The client receives the saved rule or reordered list.
    provenance: test
    source: >-
      services-crm:modules/regions/tests/e2e/regions.e2e.spec.js#regions API (end to end) › the rules, one at
      a time › answers 409 for a replace from a stale read
  - 'n': 5
    actor_or_service: Region persistence (deactivation)
    action: >-
      Retiring an active region checks for an enabled referencing rule and stamps policy generation and change
      time without changing settings version.
    anchor: >-
      services-crm:modules/regions/services/persistence/region-assignment-settings.ts:noteRuleRegionDeactivated
    seam: sync
    on_failure: Failure rolls back the enclosing regional update.
    left_inconsistent: No partial region retirement or policy stamp commits.
    observer: A subsequent settings read can report outstanding recalculation.
    provenance: observed
    source: 'services-crm:modules/regions/services/persistence/regions.ts:updateRegion'
  - 'n': 6
    actor_or_service: Console Assignment Rules tab
    action: >-
      The saved-policy view reads outstanding recalculation and offers its banner action when enabled. No
      policy save has directly moved a record.
    anchor: 'react-console:components/regions/assignment-rules-view.tsx'
    seam: sync
    on_failure: A failed policy load displays its retry control.
    left_inconsistent: Older placements can remain while new address reactions already evaluate changed coverage.
    observer: Staff see the saved policy and applicable needed banner.
    provenance: test
    source: 'react-console:components/regions/__tests__/assignment-rules-view.test.tsx'
branches:
  - A request without a rule list preserves rules while replacing settings and still increments generation.
  - Writes while disabled keep advancing generation; enabling later requests a covering recalculation.
waits:
  - >-
    Existing placements await an explicit recalculation request; policy persistence schedules no bulk
    placement work.
evidence:
  - >-
    services-crm at a8d1364f: modules/regions/services/persistence/region-assignment-settings.ts;
    services/persistence/regions.ts; aws/api/regions.routes.ts; tests/e2e/regions.e2e.spec.js;
    tests/integration/services/region-assignment/region-assignment-rules.integration.spec.js.
  - >-
    react-console at baf6dae3: components/regions/assignment-rules-editor.tsx, assignment-draft.ts,
    assignment-rules-view.tsx; lib/configuration/regions.ts (assignmentsBody).
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-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-003, TC-PI-004, TC-PI-005, TC-PRV-001, TC-PRV-002, TC-TEN-001, TC-TEN-002, TC-TEN-003]
---

# Record a policy change and ask for recalculation

## What It Does

Saving geographic coverage records another policy generation without updating contacts or organizations. The same principle applies to individual-rule operations and enabled-target retirement. Staff inspect the saved result and choose when recalculation should revisit existing records. Later address reactions use the policy available when they process.

## The Flow

```mermaid
sequenceDiagram
  participant C as Staff
  participant E as Policy editor
  participant A as API
  participant P as Policy persistence
  C->>E: Stage coverage
  E->>A: Save complete policy with baseline version
  A->>A: Validate and normalize
  A->>P: Lock existing settings and check version
  A->>P: Commit settings, rules and generation
  A-->>E: Saved policy
  E-->>C: Return to policy view
```

## When Something Fails

Rejected writes roll back their transaction. Validation feedback precedes persistence where possible; missing references can instead be reported after rollback. Whole-list replacement resets retained rule versions, while omitted settings versions allow unconditional saves. Competing first writes can fail during insertion because no row existed to lock.

## Open Design Questions

Safe coexistence of whole-policy and individual editing requires resolving the rule-version reset. Unchanged saves, optional settings versions, initialization races and unlocked policy reads remain implementation or product gaps.
