---
id: DOM-CRM-REG-004
title: Recalculation Run
realises: CON-CRM-REG-004
product_area: crm
module: regions
revision: 2
status: draft
design_intent: proposed
implementation_evidence: observed
source_sha:
  services-crm: a8d1364f1dd44b723bb5f9acba12f983e9ec8d69
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
owned_by: SVC-CRM-REG-regions
readable_by: []
lifecycle: STATE-CRM-REG-004
custom_fields: none
identity: >-
  The service generates a workspace-specific identifier for each run. A partial unique index permits one
  running run per workspace; completed and failed runs remain available for inspection.
attributes:
  - name: identifier
    kind: text
    required: true
    computed: true
    notes: The identifier is allocated when persistence records the run.
  - name: status
    kind: enum
    required: true
    computed: false
    notes: 'The persisted outcome is running, completed or failed.'
  - name: phase
    kind: enum
    required: true
    computed: false
    notes: 'Processing traverses contacts, then organizations, and finishes in done.'
  - name: traversal position
    kind: text
    required: false
    computed: false
    notes: >-
      The private cursor identifies the last completed record in bytewise order and is absent at a phase’s
      start.
  - name: examined
    kind: number
    required: true
    computed: false
    notes: 'This count includes every traversed record, including records matching skips.'
  - name: changed
    kind: number
    required: true
    computed: false
    notes: This count records actual regional changes and cannot exceed the traversal count.
  - name: continuation count
    kind: number
    required: true
    computed: false
    notes: >-
      The private continuation counter requests another worker when the current budget expires with unfinished
      work.
  - name: last failure
    kind: text
    required: false
    computed: false
    notes: >-
      The stored explanation contains redacted error information bounded to 1,000 characters. A successful
      progress update clears it.
  - name: policy generation started under
    kind: number
    required: true
    computed: false
    notes: >-
      The run records the current policy generation at insertion; that private value is not returned by run
      reads.
  - name: completion time and stamps
    kind: date
    required: false
    computed: true
    notes: >-
      The run records its requester, creation and modification times, and completion time only after
      finishing.
relationships:
  - to: DOM-CRM-REG-002
    cardinality: Each run belongs to one workspace policy context and records its starting generation.
    ownership: The run owns its generation marker; batches independently reread policy.
    cross_service: false
  - to: DOM-CRM-REG-003
    cardinality: A run traverses the regional facets of that workspace’s contacts and organizations.
    ownership: Entities owns placement storage and performs the updates; the run retains progress counts.
    cross_service: false
invariants:
  - id: INV-DOM-CRM-REG-004-01
    statement: 'The database permits one running run per workspace, and another start selects that existing run.'
    enforced_at:
      - service
      - database
    provenance: observed
    source: >-
      services-crm:modules/regions/tests/integration/services/persistence/region-recalculations.integration.spec.js#region
      recalculations table (integration) › refuses a second running run for the tenant;
      services-crm:modules/regions/services/region-assignment/region-recalculation.ts:startRegionRecalculation
  - id: INV-DOM-CRM-REG-004-02
    statement: 'Completed status, done phase and a present completion time must coincide.'
    enforced_at:
      - database
    provenance: test
    source: >-
      services-crm:modules/regions/tests/integration/services/persistence/region-recalculations.integration.spec.js#region
      recalculations table (integration) › refuses completed before the run is done
  - id: INV-DOM-CRM-REG-004-03
    statement: 'Examined and changed counts are nonnegative, with changed bounded by examined.'
    enforced_at:
      - database
    provenance: test
    source: >-
      services-crm:modules/regions/tests/integration/services/persistence/region-recalculations.integration.spec.js#region
      recalculations table (integration) › refuses more records changed than examined
  - id: INV-DOM-CRM-REG-004-04
    statement: >-
      Run locking serializes batches, and each batch commits regional writes, counts and cursor movement
      together.
    enforced_at:
      - service
      - database
    provenance: observed
    source: >-
      services-crm:modules/regions/services/region-assignment/region-recalculation.ts:recalculateBatch;
      services-crm:modules/regions/services/persistence/region-recalculations.ts:lockRecalculation
  - id: INV-DOM-CRM-REG-004-05
    statement: 'Failure recording uses redacted information rather than record data, SQL statements or bound values.'
    enforced_at:
      - service
    provenance: test
    source: >-
      services-crm:modules/regions/tests/integration/services/region-assignment/region-recalculation-errors.integration.spec.js#a
      region recalculation that fails (integration) › the reason it records › records no value of the batch
      that failed: not the contact, the tenant, the statement or its parameters
  - id: INV-DOM-CRM-REG-004-06
    statement: >-
      Final batch failure attempts to settle the run as failed; earlier failure retains running status, and
      settlement itself can fail.
    enforced_at:
      - service
    provenance: observed
    source: >-
      services-crm:modules/regions/tests/integration/services/region-assignment/region-recalculation-errors.integration.spec.js#a
      region recalculation that fails (integration) › the last delivery of a run’s event › fails the run,
      because nothing will deliver its event again;
      services-crm:modules/regions/services/region-assignment/region-recalculation.ts:recordFailure
  - id: INV-DOM-CRM-REG-004-07
    statement: Run reads use the execution workspace and do not return a run absent from it.
    enforced_at:
      - api
      - service
      - database
    provenance: observed
    source: 'services-crm:modules/regions/services/persistence/region-recalculations.ts:getRegionRecalculation'
physical:
  - crm.region_recalculations
  - crm.region_recalculation_change_events
evidence:
  - >-
    services-crm at a8d1364f: modules/regions/db/region-recalculations.table.ts;
    services/persistence/region-recalculations.ts; services/region-assignment/region-recalculation.ts;
    aws/crm.change-events.ts (region_recalculation stream); docs/change-events.md
    (region-recalculation-runner);
    tests/integration/services/persistence/region-recalculations.integration.spec.js;
    tests/integration/services/region-assignment/region-recalculation-errors.integration.spec.js.
  - 'react-console at baf6dae3: components/regions/recalculation.tsx.'
---

# Recalculation Run

## What It Models

A recalculation run represents staff’s request to revisit existing placements under saved coverage. It provides persistent progress and an outcome while processing continues across requests and queue deliveries. It is separate from the file-import engine’s jobs.

## Identity and Attributes

The service allocates an identifier and begins with running status in the contact phase. The cursor and counts advance with committed batches. After contacts, processing walks organizations and then completes. A private starting generation supports the policy’s outstanding-work flag, and a continuation counter requests further delivery.

## Relationships

Each batch reads current policy rather than a frozen copy. Changes made during processing can affect later records while leaving another recalculation needed. Regional values are written through Entities persistence; the run stores progress rather than record copies.

## Invariants

The unique running-run constraint prevents parallel runs in one workspace. Workers take turns on the run lock and commit progress with each batch. Failed batches roll back while earlier batches remain. Failure settlement stores a redacted reason when possible and attempts terminal status on the final queue delivery.

## How It Is Stored and Shared

Regions owns runs and their regional continuation stream. Insert and continuation events reach the runner; ordinary progress does not start another worker. Workspace exports withhold this operational bookkeeping, and reset or purge removes it.

## Open Design Questions

Regional routes provide no cancellation or direct repair of a stuck run. If terminal failure settlement fails, a dead-lettered message can leave the run running. An in-request failure can also report a failed start even though a run exists, and the console does not restore failed historical runs.
