---
id: ATS-PFM-INT-015
scope: CAP-PFM-INT-015
product_area: platform
module: integrations
revision: 3
status: proposed
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
  macroservice-libraries: 54536a441935c6e09fdb7f96f0bec71c52ba4e03
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
scenarios:
  - id: ATS-PFM-INT-015-01
    title: Consumer registration replaces earlier interests
    level: api
    proves:
      - BR-CAP-PFM-INT-015-01
    preconditions: Accounting is registered for payment events.
    steps: Register a narrower replacement list.
    expected: Only the replacement event interests remain stored.
    negative: false
    data_needs: 'Fixtures establishing the stated accounts, declarations and history in an isolated workspace.'
    bound_to:
      - TST-PFM-INT-537
    provenance: test
    source: >-
      services-platform:modules/integrations/tests/integration/services/events/event-router.integration.spec.js#replaces
      what a principal registered before, whole
    category: rule
    binding_evidence:
      - >-
        services-platform:modules/integrations/tests/integration/services/events/event-router.integration.spec.js#replaces
        what a principal registered before, whole
    binding_scope: >-
      contributions: the cited tests support their own asserted outcomes. Additional setup, branches
      and combined sequences remain proposed unless the cited assertions exercise them.
  - id: ATS-PFM-INT-015-02
    title: Routing retains failures and unclaimed events visibly
    level: api
    proves:
      - BR-CAP-PFM-INT-015-02
    preconditions: 'Prepare two consumers, one requesting retry, and an event with no match.'
    steps: Route batches through repeated attempts.
    expected: >-
      Matching consumers receive events, retry is scheduled and can end dead at six attempts, and
      unmatched events become unclaimed without invocation.
    negative: false
    data_needs: 'Fixtures establishing the stated accounts, declarations and history in an isolated workspace.'
    bound_to:
      - TST-PFM-INT-537
    provenance: test
    source: >-
      services-platform:modules/integrations/tests/integration/services/events/event-router.integration.spec.js#records
      an event every consumer handled or ignored as delivered, to each of them
    category: failure
    binding_evidence:
      - >-
        services-platform:modules/integrations/tests/integration/services/events/event-router.integration.spec.js#records
        an event every consumer handled or ignored as delivered, to each of them
      - >-
        services-platform:modules/integrations/tests/integration/services/events/event-router.integration.spec.js#invokes
        each consumer once for the batch, as platform, at the consume route of the batch’s tenant
      - >-
        services-platform:modules/integrations/tests/integration/services/events/event-router.integration.spec.js#backs
        off an event a consumer asked to retry, queuing its next attempt a minute out
      - >-
        services-platform:modules/integrations/tests/integration/services/events/event-router.integration.spec.js#dead-letters
        an event after six attempts of retries
      - >-
        services-platform:modules/integrations/tests/integration/services/events/event-router.integration.spec.js#records
        an event no consumer takes as no_consumer, invoking nobody
    binding_scope: >-
      contributions: the cited tests support their own asserted outcomes. Additional setup, branches
      and combined sequences remain proposed unless the cited assertions exercise them.
  - id: ATS-PFM-INT-015-03
    title: Completion goes to its calling application
    level: api
    proves:
      - BR-CAP-PFM-INT-015-03
    preconditions: Accounting and Events both have event consumers.
    steps: Complete Accounting’s asynchronous operation.
    expected: Only Accounting receives that completion and its response.
    negative: false
    data_needs: 'Fixtures establishing the stated accounts, declarations and history in an isolated workspace.'
    bound_to:
      - TST-PFM-INT-480
    provenance: test
    source: >-
      services-platform:modules/integrations/tests/e2e-adapters/events-dummy.adapters.spec.js#reaches the
      consumer of the application that invoked it, carrying the response
    category: interaction
    binding_evidence:
      - >-
        services-platform:modules/integrations/tests/e2e-adapters/events-dummy.adapters.spec.js#reaches
        the consumer of the application that invoked it, carrying the response
    binding_scope: >-
      contributions: the cited tests support their own asserted outcomes. Additional setup, branches
      and combined sequences remain proposed unless the cited assertions exercise them.
  - id: ATS-PFM-INT-015-04
    title: Adapter emissions are validated as a whole
    level: api
    proves:
      - BR-CAP-PFM-INT-015-04
    preconditions: The test adapter has its own connection.
    steps: >-
      Emit undeclared types, malformed data, one hundred and one events and a foreign-connection batch,
      then valid events.
    expected: Each invalid batch refuses; the valid batch saves event identities.
    negative: true
    data_needs: 'Fixtures establishing the stated accounts, declarations and history in an isolated workspace.'
    bound_to:
      - TST-PFM-INT-509
    provenance: test
    source: >-
      services-platform:modules/integrations/tests/integration/services/adapter-callbacks/adapter-callbacks.integration.spec.js#refuses
      a type the manifest does not produce, InvalidRequest
    category: boundary
    binding_evidence:
      - >-
        services-platform:modules/integrations/tests/integration/services/adapter-callbacks/adapter-callbacks.integration.spec.js#refuses
        a type the manifest does not produce, InvalidRequest
      - >-
        services-platform:modules/integrations/tests/integration/services/adapter-callbacks/adapter-callbacks.integration.spec.js#refuses
        data its type’s schema refuses, InvalidRequest
      - >-
        services-platform:modules/integrations/tests/integration/services/adapter-callbacks/adapter-callbacks.integration.spec.js#refuses
        more than a hundred events in one call
      - >-
        services-platform:modules/integrations/tests/integration/services/adapter-callbacks/adapter-callbacks.integration.spec.js#refuses
        events that name another connection than the call’s, AccessDenied
      - >-
        services-platform:modules/integrations/tests/integration/services/adapter-callbacks/adapter-callbacks.integration.spec.js#writes
        each event with its id, its source and its rhythm block, and answers the ids
    binding_scope: >-
      contributions: the cited tests support their own asserted outcomes. Additional setup, branches
      and combined sequences remain proposed unless the cited assertions exercise them.
  - id: ATS-PFM-INT-015-05
    title: Stale event dispatch cannot repeat its attempt
    level: api
    proves:
      - BR-CAP-PFM-INT-015-05
    preconditions: An event dispatch was already routed.
    steps: Redeliver that same work message.
    expected: Routing performs no further work.
    negative: false
    data_needs: 'Fixtures establishing the stated accounts, declarations and history in an isolated workspace.'
    bound_to:
      - TST-PFM-INT-537
    provenance: test
    source: >-
      services-platform:modules/integrations/tests/integration/services/events/event-router.integration.spec.js#does
      nothing for a message of an attempt already made
    category: concurrency
    binding_evidence:
      - >-
        services-platform:modules/integrations/tests/integration/services/events/event-router.integration.spec.js#does
        nothing for a message of an attempt already made
    binding_scope: >-
      contributions: the cited tests support their own asserted outcomes. Additional setup, branches
      and combined sequences remain proposed unless the cited assertions exercise them.
  - id: ATS-PFM-INT-015-06
    title: Staff event inspection spans customers
    level: api
    proves:
      - BR-CAP-PFM-INT-015-06
    preconditions: Two workspaces contain unclaimed events.
    steps: List with a staff identity and a customer identity.
    expected: Staff can inspect both; the customer can reach only its own workspace event list.
    negative: false
    data_needs: 'Fixtures establishing the stated accounts, declarations and history in an isolated workspace.'
    bound_to:
      - TST-PFM-INT-537
    provenance: test
    source: >-
      services-platform:modules/integrations/tests/integration/services/events/event-router.integration.spec.js#lists
      the events nobody took for Rhythm HQ, across tenants
    category: permission
    binding_evidence:
      - >-
        services-platform:modules/integrations/tests/integration/services/events/event-router.integration.spec.js#lists
        the events nobody took for Rhythm HQ, across tenants
    binding_scope: >-
      contributions: the cited tests support their own asserted outcomes. Additional setup, branches
      and combined sequences remain proposed unless the cited assertions exercise them.
  - id: ATS-PFM-INT-015-07
    level: api
    negative: true
    data_needs: Run-owned records and principals in an isolated workspace.
    bound_to: []
    provenance: proposed
    title: Exercise the complete integration event routing
    category: transition
    proves:
      - BR-CAP-PFM-INT-015-02
      - BR-CAP-PFM-INT-015-05
    preconditions: >-
      Prepare one independent record in each starting state, including absent-record cases where the
      model defines creation.
    steps: >-
      Run each listed transition once with its guard satisfied, then repeat it with the guard denied.
      Inspect the resulting record and listed side effects after commit.
    expected: >-
      Each positive case reaches its listed destination and records its defined side effects. Each denied
      case preserves the starting record and creates no transition side effects.
    source: >-
      rhythm-v2-docs:specs/platform/integrations/design/state-models/STATE-PFM-INT-010-integration-event-routing.md#transitions
    state_model: STATE-PFM-INT-010
    transition_cases:
      - from: pending
        to: delivered
        trigger: Complete initial routing
        guard: Every selected consumer answers handled or ignored
        side_effects:
          - Retain the successful consumer identities.
        provenance: proposed
        source: 'services-platform:modules/integrations/services/events/event-router.ts:routeIntegrationEvents'
      - from: pending
        to: retrying
        trigger: Schedule another routing attempt
        guard: At least one consumer asks to retry or cannot be reached and backoff permits retry
        side_effects:
          - Save next routing dispatch work.
        provenance: proposed
        source: 'services-platform:modules/integrations/services/events/event-router.ts:routeIntegrationEvents'
      - from: retrying
        to: delivered
        trigger: Complete later routing
        guard: Every selected consumer answers handled or ignored
        side_effects:
          - Retain the successful consumer identities.
        provenance: proposed
        source: 'services-platform:modules/integrations/services/events/event-router.ts:routeIntegrationEvents'
      - from: retrying
        to: dead
        trigger: Exhaust routing attempts
        guard: The sixth attempt still has a failed consumer outcome
        side_effects:
          - Record dead-event telemetry for staff investigation.
        provenance: proposed
        source: 'services-platform:modules/integrations/services/events/event-router.ts:routeIntegrationEvents'
      - from: pending
        to: no_consumer
        trigger: Find no eligible consumer
        guard: 'No type or prefix matches, or the initiating application has no completion consumer'
        side_effects:
          - Record no-consumer telemetry and preserve the event for staff.
        provenance: proposed
        source: >-
          services-platform:modules/integrations/tests/integration/services/events/event-router.integration.spec.js#lists
          the events nobody took for Rhythm HQ, across tenants
      - from: retrying
        to: retrying
        trigger: Defer another routing attempt
        guard: A consumer still cannot handle the event and the attempt ceiling remains
        side_effects:
          - Schedule the next numbered attempt.
        provenance: proposed
        source: 'services-platform:modules/integrations/services/events/event-router.ts:routeIntegrationEvents'
      - from: retrying
        to: no_consumer
        trigger: Find no consumer during retry
        guard: No currently registered consumer matches the retried event
        side_effects:
          - Save the unclaimed standing and telemetry.
        provenance: proposed
        source: 'services-platform:modules/integrations/services/events/event-router.ts:routeIntegrationEvents'
    proof_boundary: >-
      The parameterized cases prove the listed lifecycle outcomes. Separate assertions are needed for
      independent object invariants, numeric limits and simultaneous requests.
  - id: ATS-PFM-INT-015-08
    level: api
    negative: false
    data_needs: Run-owned records and principals in an isolated workspace.
    bound_to: []
    provenance: proposed
    title: Settled asynchronous work reaches only its caller
    category: interaction
    proves:
      - BR-CAP-PFM-INT-010-05
      - BR-CAP-PFM-INT-015-03
    exercises:
      - CAP-PFM-INT-015
      - CAP-PFM-INT-010
    preconditions: Two applications have completion consumers and the first owns an unanswered asynchronous operation.
    steps: 'Settle the operation, route its completion and repeat settlement.'
    expected: Only the calling application receives the response and no second completion is created.
    source:
      - >-
        services-platform:modules/integrations/tests/integration/services/executions/execution-settlement.integration.spec.js#is
        written once for two settlements that both read the execution running, the loser moving nothing
      - >-
        services-platform:modules/integrations/tests/e2e-adapters/events-dummy.adapters.spec.js#reaches
        the consumer of the application that invoked it, carrying the response
    workflows: []
  - id: ATS-PFM-INT-015-09
    level: api
    negative: false
    data_needs: Run-owned records and principals in an isolated workspace.
    bound_to: []
    provenance: proposed
    title: Redelivered change work preserves delivery identity
    category: interaction
    proves:
      - BR-CAP-PFM-INT-014-02
      - BR-CAP-PFM-INT-015-05
    exercises:
      - CAP-PFM-INT-015
      - CAP-PFM-INT-014
    preconditions: A subscription accepts a change event and the routed change has a stable identity.
    steps: Fan out the same change twice and redeliver already claimed routing work.
    expected: One projected delivery exists and stale routing work makes no further attempt.
    source:
      - >-
        services-platform:modules/integrations/tests/integration/services/subscriptions/subscriptions.integration.spec.js#turns
        a CRM change event into a queued delivery of the catalog projection, never the row
      - >-
        services-platform:modules/integrations/tests/integration/services/events/event-router.integration.spec.js#does
        nothing for a message of an attempt already made
    workflows: []
  - id: ATS-PFM-INT-015-10
    level: api
    negative: false
    data_needs: Run-owned records and principals in an isolated workspace.
    bound_to: []
    provenance: proposed
    title: Verified notifications become routed events
    category: interaction
    proves:
      - BR-CAP-PFM-INT-013-04
      - BR-CAP-PFM-INT-015-02
    exercises:
      - CAP-PFM-INT-015
      - CAP-PFM-INT-013
    preconditions: A provisioned endpoint and matching application consumer are available.
    steps: 'Process a valid notification, route its emitted event, then process a tampered notification.'
    expected: The valid message reaches its consumer; signature refusal emits no event.
    source:
      - >-
        services-platform:modules/integrations/tests/integration/services/webhooks/webhook-worker.integration.spec.js#rejects
        a request whose signature does not verify, writing no event
      - 'services-platform:modules/integrations/services/events/event-router.ts:routeIntegrationEvents'
    workflows:
      - WF-PFM-INT-015
evidence:
  - >-
    Each binding identifies an existing source test at the pinned revision; unbound scenarios remain
    proposed proof obligations.
coverage:
  rule: covered
  transition: covered
  validation: 'gap: FND-PFM-INT-080'
  permission: covered
  edge: 'gap: FND-PFM-INT-080'
  failure: covered
  boundary: covered
  concurrency: covered
  invariant: 'gap: FND-PFM-INT-080'
  interaction: covered
coverage_notes:
  covered: A designed scenario exists; executable coverage and a passing run require separate evidence.
  gaps: >-
    The finding records categories without a designed scenario and remaining permission and rule audit
    obligations.
---
# Route integration events to their application owners: Capability Test Specification

## Coverage

| Rule | Proved By |
|---|---|
| `BR-CAP-PFM-INT-015-01` | `ATS-PFM-INT-015-01` |
| `BR-CAP-PFM-INT-015-02` | `ATS-PFM-INT-015-02`, `ATS-PFM-INT-015-07`, `ATS-PFM-INT-015-10` |
| `BR-CAP-PFM-INT-015-03` | `ATS-PFM-INT-015-03`, `ATS-PFM-INT-015-08` |
| `BR-CAP-PFM-INT-015-04` | `ATS-PFM-INT-015-04` |
| `BR-CAP-PFM-INT-015-05` | `ATS-PFM-INT-015-05`, `ATS-PFM-INT-015-07`, `ATS-PFM-INT-015-09` |
| `BR-CAP-PFM-INT-015-06` | `ATS-PFM-INT-015-06` |
| `BR-CAP-PFM-INT-015-07` | `FND-PFM-INT-080` |
| `BR-CAP-PFM-INT-015-08` | `FND-PFM-INT-080` |

## Interactions With Related Capabilities

Integration events connect provider-message processing and execution completion to application consumers. The interaction cases inspect acknowledgement and repeated routing alongside the originating work, retaining failures for later recovery.

The structured scenarios name the capabilities and rules involved. A bound test contributes only the outcomes it asserts; proposed transition matrices and composed interactions still require executable proof.
