Skip to content

CAP-CRM-ENT-015Control record locks and system protection

Markdown

CAP-CRM-ENT-015

Control record locks and system protection

Markdown
  • DocumentApproved
  • Product intentConfirmed
  • ImplementationPartially observed

KindCapabilityProduct areaCRMModuleEntitiesRevision1Approved byAndrewApproved at2026-10-22

Read atservices-crm 7ea48a41react-console da630758

The Formal Contract

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

Outcomes
A lock is set and released only by an authorised, audited action.A locked record or element rejects what its lock names.A protected system record cannot be unlocked or deleted.
Surfaces
consoleapi
Intent sources
services-crm:docs/milestones/CRM-M1-contacts-and-organizations.md#D-08services-crm:docs/milestones/CRM-M1-contacts-and-organizations.plan.md#A17services-crm:docs/milestones/CRM-M1-contacts-and-organizations.plan.md#A18services-crm:docs/milestones/CRM-M2-affiliations-roles-compliance.plan.md#A5

Controls

22 controls apply, from the corpus control catalogue.

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-LOG-001 TC-LOG-002 TC-LOG-003 TC-LOG-004 TC-LOG-005 TC-PI-001 TC-PI-003 TC-PI-005 TC-PRV-001 TC-PRV-002 TC-TEN-001 TC-TEN-002 TC-TEN-003

Repositories Read

RepositoryPaths
undefined:modules/entities/
undefined:docs/record-locks.md
undefined:modules/entities/aws/api/record-locks.routes.ts
undefined:modules/entities/tests/e2e/contacts.e2e.spec.js
undefined:modules/entities/tests/integration/services/persistence/record-locks.integration.spec.js
undefined:modules/entities/tests/integration/services/persistence/record-locks.queries.integration.spec.js
undefined:app/(console)/console/[tenantId]/crm/contacts/
undefined:acceptance/crm/playwright/journeys/contacts/CRM-ACC-0035.spec.ts

Where It Lives

Permissions

KeyChecked atScope
crm.contacts.lockapitenant
crm.contacts.unlockapitenant
crm.organizations.lockapitenant
crm.organizations.unlockapitenant
crm.contact-roles.lockapitenant
crm.contact-roles.unlockapitenant
crm.contacts.readapitenant
crm.organizations.readapitenant
crm.contact-roles.readapitenant

Business Rules

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

IdRuleKindEnforced atProvenance
01must prove
Confirmed
Contacts, organizations and contact roles expose separate lock and unlock operations, each requiring its own administrator permission.
permissionapiobserved
02must prove
Confirmed
Lock changes require the read version and a nonblank reason up to 1,000 characters, reject stale versions, invalid locks and missing records, and make no audit or record change when the requested lock already applies.
validationapi, servicetest
03must prove
Confirmed
Lock changes advance record version and append actor, time, reason and before-and-after lock values to system-owned audit entries that readers can page newest first.
lifecycleservice, databasetest
services-crm:modules/entities/tests/integration/services/persistence/record-locks.queries.integration.spec.js#answers the newest two changes of the record, every property mapped, and where the page ended
04must prove
Confirmed
Ordinary contact and organization writes accept unchanged locks but reject lock changes; contact-role writes can change delete locks, while full-lock changes use audited actions.
permissionapitest
services-crm:modules/entities/tests/e2e/contacts.e2e.spec.js#answers 403 naming sys_locked for a replacement that sets a lock (E-14)
05must prove
Confirmed
A full lock blocks ordinary updates and deletion until released, while a delete lock blocks deletion alone.
invariantservicetest
services-crm:modules/entities/tests/e2e/contacts.e2e.spec.js#answers 409 IllegalOperation, not 403, for a replacement of a contact locked against updates, the lock sent back unchanged
06must prove
Confirmed
Element full locks block changes and all element locks block removal; audited unlock releases all locks or selected element locks and rejects unknown elements without releasing anything.
lifecycleservicetest
services-crm:modules/entities/tests/integration/services/persistence/record-locks.integration.spec.js#refuses an element id the organization does not have, releasing nothing
07must prove
Confirmed
A write that only clears an element lock requires the record’s unlock permission and otherwise fails.
permissionservicetest
services-crm:modules/entities/tests/e2e/contacts.e2e.spec.js#answers 409 to a replacement that only clears an element lock without the unlock key, and 200 with it (CRM-ACC-0035)
08must prove
Confirmed
Customer permissions cannot lock, unlock or delete protected system records or change their locks through ordinary writes, but ordinary fields remain editable; no seeded record currently carries protection.
invariantservice, databasetest
09
Confirmed
Contact, organization, affiliation, address and phone types enforce their locks but currently expose no lock or unlock action.
policyservicedocumented

Scenarios

IdGivenWhenThenProvenance
01A contact locked against updates and deletes by an earlier process.An administrator unlocks it with a reason.It can be edited and deleted again and the unlock is audited.test
02A standard user tries to lock a contact.The request is made.It is rejected for want of the lock key.test

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.