---
id: CAP-PFM-MTD-015
title: Deliver Committed Record and Link Events
feature: FEAT-PFM-MTD-05
product_area: platform
module: metadata
revision: 1
status: draft
product_intent: proposed
implementation_evidence: observed
supersedes:
  - CAP-CO-EVT-001
  - CAP-CO-EVT-002
  - CAP-CO-EVT-003
  - CAP-CO-EVT-004
  - CAP-CO-EVT-005
  - CAP-CO-EVT-006
  - CAP-CO-EVT-007
  - CAP-EXT-EVT-002
  - CAP-EXT-EVT-003
  - CAP-EXT-EVT-004
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
intent_sources:
  - >-
    services-platform:docs/milestones/PLAT-M7-custom-record-loads-reports-events.md#Scope (Proposed;
    architecture approved 2026-10-18)
  - 'services-platform:docs/proposals/custom-objects/09-decisions.md#2. Choices taken as internal (event names)'
purpose: >-
  Other Rhythm workflows need reliable notification of relevant custom-record changes. A retained event ledger
  and dispatch work separate that notification from the originating save.
outcomes:
  - Other Rhythm workflows need reliable notification of relevant custom-record changes.
  - A nightly sweep removes eligible old ledger entries without deleting events still waiting for dispatch.
actors:
  - ACT-PFM-012
surfaces:
  - job
preconditions: []
postconditions: []
permissions:
  - key: system (no user permission key; events are written by the record and link writes)
    where_checked: service
    scope: tenant
rules:
  - id: BR-CAP-PFM-MTD-015-01
    proved_by: [ATS-PFM-MTD-012-18, ATS-PFM-MTD-013-10, ATS-PFM-MTD-015-01, ATS-PFM-MTD-015-08, ATS-PFM-MTD-018-13, TST-PFM-MTD-043, TST-PFM-MTD-048, TST-PFM-MTD-064]
    statement: >-
      Record lifecycle and property mutations and link creation, ending or deletion append their event in the
      mutation transaction, while refused or rolled-back writes append nothing.
    kind: invariant
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/custom-objects/tests/integration/services/persistence/custom-object-records.events.integration.spec.js#publishes
      nothing when the transaction that wrote the record rolls back, in the ledger or the queue
    origin: none
  - id: BR-CAP-PFM-MTD-015-02
    proved_by: [ATS-PFM-MTD-015-02, TST-PFM-MTD-048, TST-PFM-MTD-064]
    statement: >-
      Record events identify the object, record, prefixed number, name, publication state and author, with
      update events listing changed property keys in sorted order.
    kind: calculation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/custom-objects/tests/integration/services/persistence/custom-object-records.events.integration.spec.js#publishes
      objects.record.updated naming the properties whose value changed, sorted
    origin: none
  - id: BR-CAP-PFM-MTD-015-03
    proved_by: [ATS-PFM-MTD-015-03, TST-PFM-MTD-043, TST-PFM-MTD-064]
    statement: >-
      Unchanged property values, external-identifier-only changes, repeated publication states and link
      updates that do not end the link produce no business event.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/custom-objects/tests/integration/services/persistence/custom-object-records.events.integration.spec.js#publishes
      nothing for an update that changes no property’s value
    origin: none
  - id: BR-CAP-PFM-MTD-015-04
    proved_by: [ATS-PFM-MTD-015-04, TST-PFM-MTD-056]
    statement: >-
      Events retain a ledger entry and waiting work until dispatch destinations accept their messages; any
      refused destination leaves work for another run without removing the ledger fact.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/custom-objects/tests/integration/services/dispatch/business-events.integration.spec.js#keeps
      its queue row, claimed, for the next run to offer again
    origin: none
  - id: BR-CAP-PFM-MTD-015-05
    proved_by: [ATS-PFM-MTD-015-05, TST-PFM-MTD-056]
    statement: >-
      Consumption sends the event to native subscribers or its versioned external-router contract to the
      router and reports only failed messages for redelivery.
    kind: policy
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/custom-objects/tests/integration/services/dispatch/business-events.integration.spec.js#answers
      the one message whose destination refused, so SQS offers that one again
    origin: none
  - id: BR-CAP-PFM-MTD-015-06
    proved_by: []
    statement: >-
      The nightly ninety-day sweep retains events still referenced by waiting queue rows; workspace reset or
      deletion purges the workspace events.
    kind: lifecycle
    enforced_at:
      - service
    must_prove: true
    provenance: observed
    source: 'services-platform:modules/custom-objects/services/persistence/business-events.ts:sweepBusinessEvents'
    origin: none
  - id: BR-CAP-PFM-MTD-015-07
    proved_by: [ATS-PFM-MTD-015-06, ATS-PFM-MTD-015-08, ATS-PFM-MTD-019-10, TST-PFM-MTD-064]
    statement: Deletions announce link removals and cascaded records before the records they depend on.
    kind: policy
    enforced_at:
      - service
      - database
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/custom-objects/tests/integration/services/persistence/custom-object-records.events.integration.spec.js#publishes
      the deletion of the record a cascade takes, with its own object’s number, and the association’s
    origin: 'services-platform:docs/milestones/PLAT-M5-custom-records.md#PLAT-M5 B1'
  - id: BR-CAP-PFM-MTD-015-08
    proved_by: []
    statement: >-
      Platform has no native subscribers yet, and CRM merge moves or endings of links produce no business
      event.
    kind: policy
    enforced_at:
      - service
    must_prove: false
    provenance: documented
    source: 'services-platform:docs/custom-objects.md#Events'
    origin: none
  - id: BR-CAP-PFM-MTD-015-09
    proved_by: [ATS-PFM-MTD-015-07, TST-PFM-MTD-058]
    statement: Retention sweeps refuse durations below one day or durations that are not whole numbers.
    kind: validation
    enforced_at:
      - service
    must_prove: true
    provenance: test
    source: >-
      services-platform:modules/custom-objects/tests/integration/services/persistence/business-events.integration.spec.js#refuses
      a retention shorter than a day
    origin: none
scenarios:
  - id: SC-CAP-PFM-MTD-015-01
    given: A registrar submits a valid boat create.
    when: Commit it and compare a refused create.
    then: >-
      The committed create leaves its creation event and dispatch work with the actor recorded; the refusal
      leaves no event.
    provenance: test
    source: >-
      services-platform:modules/custom-objects/tests/e2e/business-events.e2e.spec.js#leaves no event for a
      create the API refuses
edge_cases:
  - id: SC-CAP-PFM-MTD-015-02
    given: No destination subscribes to an event.
    when: Run dispatch.
    then: No delivery is sent and the waiting queue entry is removed.
    provenance: test
    source: >-
      services-platform:modules/custom-objects/tests/integration/services/dispatch/business-events.integration.spec.js#deletes
      its queue row where it stands
constraints: []
depends_on:
  - CAP-PFM-MTD-012
  - CAP-PFM-MTD-018
related:
  - CAP-PFM-MTD-013
journeys: []
open_questions: []
concepts:
  - CON-PFM-MTD-005
evidence:
  - >-
    services-platform at ad3f6a6582843008ed0f5a17b131878d15829af1: modules/custom-objects/record-events.ts,
    services/dispatch/business-events.ts, services/persistence/business-events.ts,
    aws/logic/business-events.lambda.ts , aws/resources/business-events.sqs.ts, business-events.settings.ts,
    db/business-events.table.ts, business-event-queue.table.ts; modules/associations/association-events.ts;
    tests e2e/business-events.e2e.spec.js, integration/services/dispatch/business-events.integration.spec.js,
    integration/services/persistence/custom-object-records.events.integration.spec.js,
    business-events*.integration.spec.js, associations
    integration/services/persistence/associations.events.integration.spec.js; docs/business-events.md,
    docs/custom-objects.md#Events.
  - >-
    react-console at baf6dae382d6f41dce976c7f848667ccc37e076f: acceptance/extensibility/inventory ,
    acceptance/custom-objects/inventory .
controls: [TC-AVL-004, TC-AVL-005, TC-AZ-001, TC-AZ-002, TC-AZ-003, TC-AZ-004, TC-DAT-001, TC-DAT-002, TC-DAT-003, TC-DAT-004, TC-IAM-001, TC-IAM-006, TC-LOG-004, TC-LOG-005, TC-MON-001, TC-PI-001, TC-PI-002, TC-PI-003, TC-PI-004, TC-PI-005, TC-TEN-001, TC-TEN-002, TC-TEN-003, TC-VEN-001, TC-VEN-002, TC-VEN-003]
exercised_by: [FAS-PFM-MTD-05-02]
suites: [TST-PFM-MTD-043, TST-PFM-MTD-048, TST-PFM-MTD-056, TST-PFM-MTD-058, TST-PFM-MTD-064]
---

# Deliver Committed Record and Link Events

## Purpose

Other Rhythm workflows need reliable notification of relevant custom-record changes. A retained event ledger and dispatch work separate that notification from the originating save.

## How It Works

A publisher writes the event inside the mutation transaction. After commit it can wake dispatch without moving the fact outside that transaction. The dispatcher resolves destinations and offers queued deliveries. Accepted work can leave the waiting queue while the ledger retains the event. The consumer reports failed deliveries for retry. A nightly sweep removes eligible old ledger entries without deleting events still waiting for dispatch. Ordinary link updates and CRM merge link changes currently have no event publication, and Platform has no native subscriber yet.

## Relationships

Records and relationships publish events. Integration delivery and shared event infrastructure provide destinations and transport.
