Skip to content

CAP-CRM-ENT-016Inspect a record’s changes over time

Markdown

CAP-CRM-ENT-016

Inspect a record’s changes over time

Markdown
  • DocumentApproved
  • Product intentConfirmed
  • ImplementationObserved

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
Every committed save leaves exactly one revision, and a save that changes nothing leaves none.A reader can filter the timeline and open any revision section by section.History cannot be forged or rewritten by the application.
Surfaces
consoleapi
Intent sources
services-crm:docs/milestones/CRM-M1-contacts-and-organizations.plan.md#2. Scopeservices-crm:docs/milestones/CRM-M1-contacts-and-organizations.plan.md#A2services-crm:docs/milestones/CRM-M2-affiliations-roles-compliance.plan.md#2. Scope

Controls

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

Repositories Read

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

Where It Lives

Permissions

KeyChecked atScope
crm.contacts.historyapitenant
crm.organizations.historyapitenant
crm.contacts.readapitenant
crm.organizations.readapitenant

Business Rules

9 rules, 4 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
Each changed contact or organization save commits one revision containing field-level old and new values and the contemporaneous record name, while an unchanged save creates no revision.
invariantdatabasetest
02must prove
Confirmed
History access requires the history permission and reach to an existing record, returns not found for inaccessible live records and uses the history permission alone for deleted records marked as deleted.
permissionapi, servicetest
services-crm:modules/entities/tests/e2e/organizations-history.e2e.spec.js#answers a caller with the history key but no read of organizations 404, as the organization itself would
03must prove
Confirmed
History pages are newest first, use a stable cursor, accept actor, operation, source and date filters, return at most 100 revisions and reject unsupported parameters.
validationapi, servicetest
services-crm:modules/entities/tests/e2e/contacts-history.e2e.spec.js#refuses a query parameter it does not know, rather than ignoring the filter
04
Confirmed
Revision attribution uses the actor’s current display name including former users, falls back to the captured name when unavailable and identifies console, portal, public, integration, job or maintenance as the source.
calculationservicetest
services-crm:modules/entities/tests/integration/services/persistence/record-revisions.queries.integration.spec.js#still answers for a user who has been deleted, because former users are named too
05
Confirmed
Contact revision details group changes into named contact, affiliation, role, e-mail, phone, address, preference and custom-field sections, combine name parts and resolve relationship and custom-field labels while hiding excluded captured columns.
policyservicetest
services-crm:modules/entities/tests/e2e/contacts-history.e2e.spec.js#shows the role removed and the role added, each under the name it has now
06
Confirmed
Merged-away contact history routes identify the survivor with a gone response, and the survivor provides access to that contact’s retained history.
origin: D-20 (cited in services-crm:modules/entities/aws/api/contacts.routes.ts)
policyapiobserved
07must prove
Confirmed
Application access to revisions is read-only and tenant-isolated, and revisions have no record foreign key so deletion does not remove them.
invariantdatabasetest
services-crm:modules/entities/tests/integration/db/record-revisions.table.integration.spec.js#may not insert a revision, so nothing the application runs can forge one
08
Confirmed
A record change without actor and source attribution fails together with its attempted revision.
invariantdatabasedocumented
services-crm:docs/record-history.md#Fail closed, and the maintenance helper
09
Confirmed
Record revisions remain until the tenant is purged.
policydatabasedocumented

Scenarios

IdGivenWhenThenProvenance
01Ruth Kim edited three times by two staff members.A reader opens her history.Three revisions newest first, each naming the person, the console and the fields changed.test
02An organization renamed and archived.Its history is opened.One revision per save, the rename with both values and the archive with its reason.test
services-crm:modules/entities/tests/e2e/organizations-history.e2e.spec.js#shows the rename with its label, data type and both values

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.