Skip to content

CAP-CRM-REG-001Maintain the region catalogue

Markdown

CAP-CRM-REG-001

Maintain the region catalogue

Markdown
  • DocumentDraft
  • Product intentProposed
  • ImplementationObserved

KindCapabilityProduct areaCRMModuleRegionsRevision2

Read atservices-crm a8d1364freact-console baf6dae3

The Formal Contract

The narrative above explains; the sections below are authoritative — the rules, scenarios and tests this document binds.

Outcomes
Regions have stable identifiers, readable names, optional descriptions and an active state.Validation failures identify the affected field and commit no region.Versioned replacement protects intervening edits.
Surfaces
consoleapi
Preconditions
The caller has regional read or write authority for the requested workspace.
Postconditions
Reading a completed save returns the persisted region.
Intent sources
services-crm:docs/debt/region-custom-fields.md#Custom-field values of a region (Andrew, 2026-10-19, R2-ARCH-001: only contacts and organizations carry custom fields)services-crm:docs/milestones/CRM-M1-contacts-and-organizations.md#D-05services-crm:docs/milestones/CRM-M8-activities-and-tasks.md#D-43

Controls

14 controls apply, from the corpus control catalogue.

TC-AZ-001 TC-AZ-002 TC-AZ-003 TC-AZ-004 TC-AZ-006 TC-DAT-001 TC-DAT-003 TC-LOG-004 TC-LOG-005 TC-PI-001 TC-PI-003 TC-TEN-001 TC-TEN-002 TC-TEN-003

Permissions

KeyChecked atScope
crm.regions.readapitenant
crm.regions.createapitenant
crm.regions.updateapitenant

Business Rules

13 rules, 9 to be proved before approval. A rule's product intent is derived from its origin: 2 confirmed by a product decision, the rest inheriting this capability's proposed.

IdRuleKindEnforced atProvenance
01must prove
Proposed
Region identifiers accept letters, digits, underscores, hyphens and vertical bars; other characters cause a refusal that describes the permitted characters.
✓ A mixed identifier containing a hyphen, underscore and vertical bar is valid.✗ An identifier containing a space is rejected.
no proof recorded yet
validationapi, databasetest
services-crm:modules/regions/tests/e2e/regions.e2e.spec.js#answers 400 for an id with a space, naming the characters an id may hold
02must prove
Proposed
A region requires a nonblank name, which is trimmed before acceptance; a name containing only spaces is rejected against the name field.
no proof recorded yet
validationapi, databasetest
services-crm:modules/regions/tests/e2e/regions.e2e.spec.js#answers 400 naming the field for a name of nothing but spaces
03must prove
Proposed
Creation generates an identifier when none is supplied and rejects an identifier already used by a region in the workspace.
no proof recorded yet
validationservice, databaseobserved
04
Proposed
A region starts active when creation does not specify its active state.
invariantdatabasetest
05must prove
Confirmed
Replacement requires the version previously read, and patching tests that version; a missing replacement version or a stale version is rejected.
no proof recorded yet
validationapi, servicetest
services-crm:modules/regions/tests/e2e/regions.e2e.spec.js#the regions › answers 409 for a replace from a stale read
06must prove
Proposed
Replacement supplies the complete region, so an omitted description or external identifier is removed.
no proof recorded yet
policyservicetest
services-crm:modules/regions/tests/integration/services/persistence/regions.queries.integration.spec.js#the record a replace left › answers the whole record as the replace left it: the fields it did not state cleared, the version bumped
07must prove
Proposed
Region lists use character-by-character identifier order and return pages with a continuation when another page exists.
no proof recorded yet
policyapi, servicetest
services-crm:modules/regions/tests/integration/services/persistence/regions.integration.spec.js#lists the tenant's regions in id order by bytes, a page at a time
08must prove
Proposed
Region reads and writes stay within the requested workspace, even for callers with access to several workspaces, and a conflicting workspace in the request body is rejected.
no proof recorded yet
invariantapi, service, databaseobserved
services-crm:modules/regions/tests/e2e/regions.e2e.spec.js#the regions › answers 404 for this tenant's region read in another tenant, to a context that covers both; services-crm:modules/regions/aws/api/regions.routes.ts:toWriteInput
09
Proposed
Regions have no custom fields; CRM reserves custom fields for contacts and organizations.
origin: services-crm:docs/debt/region-custom-fields.md#Custom-field values of a region (Andrew, 2026-10-19, R2-ARCH-001: only contacts and organizations carry custom fields)
policyservicedocumented
services-crm:docs/debt/region-custom-fields.md#Custom-field values of a region
10must prove
Confirmed
The console chooses a new region’s identifier before sending creation, allowing a retry after an ambiguous response to find the original region.
no proof recorded yet
policyconsoletest
react-console:lib/configuration/regions.test.ts#the regions api writes (D-05, D-35, D-43) › creates a region under the id the drawer chose and finds it when the answer was lost
11
Proposed
Console replacement retains the fields returned by the read, including the external identifier and lock, while applying the visible edits.
policyconsoleobserved
12must prove
Proposed
Every successful replacement or patch increments the region version, including a write that leaves its fields unchanged.
no proof recorded yet
lifecycleserviceobserved
13
Proposed
The console uses a drawer on the right to edit a region, labels it by record kind and requests confirmation before discarding unsaved edits.
origin: services-crm:docs/migrations/crm-regions/README.md#2026-10-15 — Final acceptance (console follow-ups) (Andrew approved 2026-10-15: two tabs, Test an Address dialog, drawers, a read-only Assignment Rules tab with its own editor screen and the banner)
policyconsoletest
react-console:components/regions/__tests__/editor-drawers.test.tsx#the region editor › renders in a right-side drawer, not in a dialog

Scenarios

IdGivenWhenThenProvenance
01The region catalogue is being established.An administrator creates Northeast with a description and leaves it active.The new active region appears in the catalogue and is available as a rule target.test
02A new region uses an identifier containing a space.The coordinator submits creation.Validation identifies the identifier grammar and creates nothing.test
services-crm:modules/regions/tests/e2e/regions.e2e.spec.js#answers 400 for an id with a space, naming the characters an id may hold
03Two editors read one region at the same version.One saves and the other submits the older replacement.The later request conflicts and preserves the earlier edit.test
services-crm:modules/regions/tests/e2e/regions.e2e.spec.js#the regions › answers 409 for a replace from a stale read

Edge Cases

IdGivenWhenThenProvenance
04An identifier combines upper and lower case with a vertical bar.A client creates that region.The legacy identifier grammar accepts it.test
services-crm:modules/regions/tests/integration/db/regions.table.integration.spec.js#accepts the characters of the legacy grammar (word characters, | and -)

The Markdown files under specs/ in rhythmsoftware/rhythm-v2-docs are the canonical source of truth. This reader is a generated, read-only projection of them.