---
id: DOM-PFM-OPS-002
title: Record Activity Event
product_area: platform
module: operations
revision: 1
status: draft
realises: CON-PFM-OPS-002
design_intent: confirmed
origin: 'services-platform:docs/audit-events.md#2026-10-12 — Final acceptance (accepted by Andrew)'
implementation_evidence: observed
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
owned_by: SVC-PFM-OPS-audit
readable_by: []
custom_fields: none
identity: >-
  The database identity locates the event; a person’s occurrence key also identifies the visit within that
  workspace.
attributes:
  - name: event identity
    kind: text
    required: true
    computed: true
    notes: A database-generated time-based identity gives tied event times a stable ordering.
  - name: workspace
    kind: reference
    required: true
    computed: false
    notes: Every event belongs to the workspace in its execution context.
  - name: actor
    kind: reference
    required: true
    computed: false
    notes: Attribution comes from the verified context and survives later account removal.
  - name: event type
    kind: enum
    required: true
    computed: false
    notes: >-
      The supported vocabulary covers visits, merges, intervention, eligibility, custom-record change and
      attention events.
  - name: record type and record
    kind: reference
    required: true
    computed: false
    notes: Target type and identity remain even when the business record is removed.
  - name: occurred at
    kind: date
    required: true
    computed: true
    notes: The context supplies a server timestamp rounded to milliseconds.
  - name: occurrence key
    kind: text
    required: true
    computed: false
    notes: Console visits reuse a normalized UUID; mutation writers generate their own occurrence identities.
  - name: metadata
    kind: collection
    required: true
    computed: false
    notes: Each event type has bounded allowed metadata keys; visits carry only application and screen tokens.
relationships:
  - to: DOM-CRM-ENT-001
    cardinality: one contact to many visit events
    ownership: >-
      The link belongs to this object; the event names the contact by type and identity, with no constraint on
      the contact
    cross_service: true
  - to: DOM-CRM-ENT-002
    cardinality: one organization to many visit events
    ownership: >-
      The link belongs to this object; the event names the organization by type and identity, with no
      constraint on it
    cross_service: true
invariants:
  - id: INV-DOM-PFM-OPS-002-01
    statement: >-
      Ordinary application privileges prohibit updates and deletion; workspace purge is the supported removal
      path.
    enforced_at:
      - database
    provenance: test
    source: >-
      services-platform:modules/audit/tests/integration/db/audit-events.table.integration.spec.js#refuses an
      UPDATE of the event by the application role, so a trail cannot be rewritten
  - id: INV-DOM-PFM-OPS-002-02
    statement: Workspace policies restrict event insertion and reading to the session’s workspace.
    enforced_at:
      - database
    provenance: test
    source: >-
      services-platform:modules/audit/tests/integration/db/audit-events.table.integration.spec.js#refuses a
      row written for another tenant from this tenant’s session, by the policy’s WITH CHECK
  - id: INV-DOM-PFM-OPS-002-03
    statement: >-
      Database checks admit only declared event types, their permitted metadata keys and at most one kilobyte
      of metadata.
    enforced_at:
      - database
    provenance: test
    source: >-
      services-platform:modules/audit/tests/integration/db/audit-events.table.integration.spec.js#refuses an
      event type the store does not hold, by audit_events_event_type_check
  - id: INV-DOM-PFM-OPS-002-04
    statement: >-
      An occurrence key is unique for a person within a workspace; an identical target returns the old event
      and a different target causes conflict.
    enforced_at:
      - database
      - service
    provenance: test
    source: >-
      services-platform:modules/audit/tests/integration/services/record-views/record-view.integration.spec.js#reports
      the retry as a duplicate rather than refusing it, because the client has nothing to fix
  - id: INV-DOM-PFM-OPS-002-05
    statement: Public visit attribution and time come from verified execution context instead of the request body.
    enforced_at:
      - api
      - service
    provenance: test
    source: >-
      services-platform:modules/audit/tests/unit/aws/api/audit-api-mapper.spec.ts#refuses a body naming the
      actor, which is how a trail would be written in somebody else’s name
  - id: INV-DOM-PFM-OPS-002-06
    statement: >-
      The public visit writer records a target only after resolving it under current record and dataset
      authority.
    enforced_at:
      - service
    provenance: test
    source: >-
      services-platform:modules/audit/tests/integration/services/record-views/record-view.integration.spec.js#writes
      nothing at all for any of those refusals
  - id: INV-DOM-PFM-OPS-002-07
    statement: Millisecond precision and event identity provide stable ordering for history pagination.
    enforced_at:
      - database
    provenance: test
    source: >-
      services-platform:modules/audit/tests/integration/db/audit-events.table.integration.spec.js#refuses an
      instant finer than a millisecond, by audit_events_occurred_at_precision_check
  - id: INV-DOM-PFM-OPS-002-08
    statement: >-
      Mutation activity commits with the business change only when the owning caller provides that
      transaction.
    enforced_at:
      - service
    provenance: observed
    source: 'services-platform:modules/audit/services/persistence/audit-events.ts:recordEligibilityChange'
  - id: INV-DOM-PFM-OPS-002-09
    statement: The query-reader role receives read privileges under the same workspace isolation.
    enforced_at:
      - database
    provenance: test
    source: >-
      services-platform:modules/audit/tests/integration/db/audit-events.table.integration.spec.js#refuses an
      INSERT by that role, because the grant is SELECT and nothing else
physical:
  - audit_events
evidence:
  - >-
    services-platform at ad3f6a6: modules/audit (db/audit-events.table.ts, registry/viewable-record-types.ts,
    services/record-views, services/persistence/audit-events.ts, services/lifecycle/tenant-participant.ts,
    aws/api); docs/audit-events.md; docs/row-level-security.md.
  - >-
    react-console at baf6dae: lib/record-views/tracking (visit.ts, client.ts, use-record-view-tracking.ts);
    lib/api/audit.ts.
  - >-
    codex examination 2026-10-23: checked the pinned source anchors, planning references and enforcement
    boundaries; unresolved limits are recorded in module findings.
---

# Record Activity Event

## What It Models

A record activity event preserves a small account of something a person did to a record. Visits share the store with supported mutation events from other modules. Security decisions have a separate history and purpose.

## Identity and Attributes

The database assigns a time-based event identity. Each event names its workspace, person, type, target and occurrence key, with a millisecond timestamp supplied by the execution context. Metadata holds bounded tokens rather than record content. A visit key is unique within a person’s workspace history.

## Relationships

Target references retain the record type and identity without requiring the business record to survive. Contacts and organizations are examples of cross-service targets; committee visits are registered too. The query layer uses visits to find current readable records, whereas raw history can retain references to removed records.

## Invariants

The application role can insert and read but cannot modify or remove stored events. Workspace isolation and metadata shape are database concerns. Visit reporting resolves the target under current authority before the insert transaction, then returns the earlier event for a duplicate key or refuses a changed target.

## How It Is Stored and Shared

Audit owns the activity table and writer functions. Mutation writers share a business transaction only when their caller supplies that transaction. Raw visit-history reads page by timestamp and identity; Recently Viewed results belong to Queries. The protected workspace purge path removes events in bounded batches. No custom fields are declared.

## Open Design Questions

The retention period is still undecided. Current history APIs read visits only, and neither a person nor an administrator has a dedicated raw-history screen here.
