---
id: ATS-PFM-NOT-007
scope: CAP-PFM-NOT-007
product_area: platform
module: notifications
revision: 2
status: proposed
approved_by: null
approved_at: null
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
scenarios:
  - id: ATS-PFM-NOT-007-01
    title: Deliver personal notices in the console
    level: api
    proves:
      - BR-CAP-PFM-NOT-007-01
    preconditions: Prepare a definition supporting the console and e-mail.
    steps: Submit a notice addressed to Jane.
    expected: >-
      Console delivery records success; supported e-mail is skipped pending its
      release.
    negative: false
    data_needs: Use only records created and owned by this suite.
    bound_to:
      - >-
        services-platform:modules/notifications/tests/integration/services/orchestration/orchestrate-request.integration.spec.js#writes
        the console delivery delivered at once and records every other supported
        channel as not available until its milestone
      - >-
        services-platform:modules/notifications/tests/unit/channel-resolution.spec.js#chooses
        the console for everyone, by the definition
      - TST-PFM-NOT-079
      - TST-PFM-NOT-105
    provenance: test
    source: >-
      services-platform:modules/notifications/tests/integration/services/orchestration/orchestrate-request.integration.spec.js#writes
      the console delivery delivered at once and records every other supported
      channel as not available until its milestone
    category: rule
    binding_scope: >-
      Each binding supplies its own assertions; the aggregate references do not
      imply that one executable file proves every step.
  - id: ATS-PFM-NOT-007-02
    title: Explain unavailable and unsupported routes
    level: unit
    proves:
      - BR-CAP-PFM-NOT-007-02
    preconditions: >-
      Prepare a definition supporting every channel; another supporting the
      console alone.
    steps: Evaluate the recipient's available delivery routes.
    expected: >-
      Future channels identify their milestone, while channels outside the
      definition report unsupported selection.
    negative: false
    data_needs: Use only records created and owned by this suite.
    bound_to:
      - >-
        services-platform:modules/notifications/tests/unit/channel-resolution.spec.js#records
        a supported channel with no adapter yet as not available until its
        milestone
      - >-
        services-platform:modules/notifications/tests/unit/channel-resolution.spec.js#names
        the milestone of each channel that is not available
      - >-
        services-platform:modules/notifications/tests/unit/channel-resolution.spec.js#records
        a channel the definition does not support as unsupported, without a
        milestone
      - TST-PFM-NOT-105
    provenance: test
    source: >-
      services-platform:modules/notifications/tests/unit/channel-resolution.spec.js#records
      a supported channel with no adapter yet as not available until its
      milestone
    category: edge
    binding_scope: >-
      Each binding supplies its own assertions; the aggregate references do not
      imply that one executable file proves every step.
  - id: ATS-PFM-NOT-007-03
    title: Queue only eligible delivery-work channels
    level: api
    proves:
      - BR-CAP-PFM-NOT-007-04
    preconditions: 'Prepare queued deliveries for Slack, Teams and text.'
    steps: >-
      Dispatch channel work and separately attempt to insert e-mail into the
      delivery-work mechanism.
    expected: >-
      Slack and Teams use chat work, text uses text work, and e-mail is rejected
      by this queue mechanism.
    negative: false
    data_needs: Use only records created and owned by this suite.
    bound_to:
      - >-
        services-platform:modules/notifications/tests/unit/channel-resolution.spec.js#delivers
        chat and SMS as queued work, and the console and e-mail not
      - >-
        services-platform:modules/notifications/tests/integration/services/dispatch/dispatch-notifications.integration.spec.js#publishes
        Slack and Teams deliveries to the chat queue and SMS to its own,
        deleting the rows
      - >-
        services-platform:modules/notifications/tests/integration/services/dispatch/dispatch-notifications.integration.spec.js#does
        not publish a delivery before its time
      - >-
        services-platform:modules/notifications/tests/integration/services/persistence/delivery-works.integration.spec.js#refuses
        a channel that is queued as no work: e-mail and the console are
        delivered otherwise
      - TST-PFM-NOT-075
      - TST-PFM-NOT-086
      - TST-PFM-NOT-105
    provenance: test
    source: >-
      services-platform:modules/notifications/tests/unit/channel-resolution.spec.js#delivers
      chat and SMS as queued work, and the console and e-mail not
    category: rule
    binding_scope: >-
      Each binding supplies its own assertions; the aggregate references do not
      imply that one executable file proves every step.
  - id: ATS-PFM-NOT-007-04
    title: Skip final work on redelivery
    level: api
    proves:
      - BR-CAP-PFM-NOT-007-05
    preconditions: Prepare a queued delivery and two consumers.
    steps: >-
      Run competing consumers, then replay the queue item after delivery reaches
      a final state.
    expected: The first item sends once; redelivery performs no additional send.
    negative: false
    data_needs: Use only records created and owned by this suite.
    bound_to:
      - >-
        services-platform:modules/notifications/tests/integration/services/persistence/deliveries.integration.spec.js#lets
        only one of two consumers racing for the send win it
      - >-
        services-platform:modules/notifications/tests/integration/services/dispatch/deliver-delivery.integration.spec.js#does
        nothing when another consumer already holds the delivery
      - >-
        services-platform:modules/notifications/tests/integration/services/dispatch/deliver-delivery.integration.spec.js#does
        nothing for an item whose delivery is already final, however often it is
        redelivered
      - TST-PFM-NOT-074
      - TST-PFM-NOT-085
    provenance: test
    source: >-
      services-platform:modules/notifications/tests/integration/services/persistence/deliveries.integration.spec.js#lets
      only one of two consumers racing for the send win it
    category: invariant
    binding_scope: >-
      Each binding supplies its own assertions; the aggregate references do not
      imply that one executable file proves every step.
  - id: ATS-PFM-NOT-007-05
    title: Apply transient backoff and permanent failure
    level: api
    proves:
      - BR-CAP-PFM-NOT-007-06
    preconditions: Prepare a delivery whose adapter fails.
    steps: >-
      Exercise temporary failure, exhaustion on the final receive, permanent
      provider refusal and a thrown adapter error.
    expected: >-
      Transient and thrown errors retry with backoff, exhaustion dead-letters
      the item, and permanent refusal fails with its code immediately.
    negative: true
    data_needs: Use only records created and owned by this suite.
    bound_to:
      - >-
        services-platform:modules/notifications/tests/integration/services/dispatch/deliver-delivery.integration.spec.js#puts
        a delivery back in the queue after a retryable failure and gives the
        item back after the backoff
      - >-
        services-platform:modules/notifications/tests/integration/services/dispatch/deliver-delivery.integration.spec.js#waits
        longer after each receive, and sends the retry once the provider
        recovers
      - >-
        services-platform:modules/notifications/tests/integration/services/dispatch/deliver-delivery.integration.spec.js#fails
        the delivery attempts_exhausted on the last receive and gives the item
        back at once to reach the dead-letter queue
      - >-
        services-platform:modules/notifications/tests/integration/services/dispatch/deliver-delivery.integration.spec.js#fails
        a permanent refusal at once with the adapter’s code and acknowledges the
        item
      - >-
        services-platform:modules/notifications/tests/integration/services/dispatch/deliver-delivery.integration.spec.js#treats
        an adapter that throws as a retryable failure rather than losing the
        delivery
      - >-
        services-platform:modules/notifications/tests/unit/queue-items.spec.js#waits
        30 seconds after the first receive, doubling to a 30-minute cap
      - >-
        services-platform:modules/notifications/tests/unit/queue-items.spec.js#spreads
        retries by 20 percent either way
      - TST-PFM-NOT-074
      - TST-PFM-NOT-110
    provenance: test
    source: >-
      services-platform:modules/notifications/tests/integration/services/dispatch/deliver-delivery.integration.spec.js#puts
      a delivery back in the queue after a retryable failure and gives the item
      back after the backoff
    category: failure
    binding_scope: >-
      Each binding supplies its own assertions; the aggregate references do not
      imply that one executable file proves every step.
  - id: ATS-PFM-NOT-007-06
    title: Calculate notice progress from deliveries
    level: unit
    proves:
      - BR-CAP-PFM-NOT-007-07
    preconditions: >-
      Create notice fixtures covering every supported combination of delivery
      states.
    steps: >-
      Calculate aggregate progress for each fixture and repeat calculation for a
      superseded notice.
    expected: >-
      Each combination produces its declared aggregate status, while superseded
      items remain superseded.
    negative: false
    data_needs: Use only records created and owned by this suite.
    bound_to:
      - >-
        services-platform:modules/notifications/tests/unit/instance-status.spec.js#is
        routed while every outside delivery is queued
      - >-
        services-platform:modules/notifications/tests/unit/instance-status.spec.js#is
        partially delivered when one delivered and another failed, none still
        active
      - >-
        services-platform:modules/notifications/tests/unit/instance-status.spec.js#is
        delivered when the chosen deliveries delivered, the skipped one taking
        no part
      - >-
        services-platform:modules/notifications/tests/unit/instance-status.spec.js#is
        failed when every chosen delivery failed or bounced
      - >-
        services-platform:modules/notifications/tests/unit/instance-status.spec.js#is
        no channel when every delivery was cancelled or expired
      - >-
        services-platform:modules/notifications/tests/integration/services/persistence/instances.integration.spec.js#never
        overwrites a superseded status with the derivation
      - TST-PFM-NOT-089
      - TST-PFM-NOT-108
    provenance: test
    source: >-
      services-platform:modules/notifications/tests/unit/instance-status.spec.js#is
      routed while every outside delivery is queued
    category: transition
    binding_scope: >-
      Each binding supplies its own assertions; the aggregate references do not
      imply that one executable file proves every step.
  - id: ATS-PFM-NOT-007-07
    title: Reject invalid delivery lifecycle writes
    level: api
    proves:
      - BR-CAP-PFM-NOT-007-08
      - INV-DOM-PFM-NOT-003-07
    preconditions: Prepare delivery fixtures spanning the declared lifecycle states.
    steps: >-
      Try prohibited transitions, a skip without its reason and a nine-kilobyte
      rendered payload.
    expected: All invalid transitions or record shapes fail without mutation.
    negative: true
    data_needs: Use only records created and owned by this suite.
    bound_to:
      - >-
        services-platform:modules/notifications/tests/integration/services/persistence/deliveries.integration.spec.js#refuses
        a transition the lifecycle does not allow, changing nothing
      - >-
        services-platform:modules/notifications/tests/integration/services/persistence/deliveries.integration.spec.js#never
        moves a delivery out of a final status
      - >-
        services-platform:modules/notifications/tests/integration/services/persistence/deliveries.integration.spec.js#refuses
        a skipped delivery that gives no reason
      - >-
        services-platform:modules/notifications/tests/integration/services/persistence/deliveries.integration.spec.js#refuses
        rendered content over 8 KB
      - TST-PFM-NOT-085
    provenance: test
    source: >-
      services-platform:modules/notifications/tests/integration/services/persistence/deliveries.integration.spec.js#refuses
      a transition the lifecycle does not allow, changing nothing
    category: validation
    binding_scope: >-
      Each binding supplies its own assertions; the aggregate references do not
      imply that one executable file proves every step.
    invariant_source: >-
      services-platform:modules/notifications/tests/integration/services/persistence/deliveries.integration.spec.js#never
      moves a delivery out of a final status
  - id: ATS-PFM-NOT-007-08
    title: Select only reachable adapter destinations
    level: unit
    proves:
      - BR-CAP-PFM-NOT-007-03
    preconditions: Prepare a supported channel with a recording adapter.
    steps: >-
      Resolve once with a reachable recipient and a second time with an
      unreachable one.
    expected: >-
      Reachable channels are selected; unreachable channels retain the adapter's
      reason.
    negative: false
    data_needs: A supported channel with a recording adapter.
    bound_to:
      - >-
        services-platform:modules/notifications/tests/unit/channel-resolution.spec.js#chooses
        a supported channel that has an adapter and a reachable person
      - >-
        services-platform:modules/notifications/tests/unit/channel-resolution.spec.js#skips
        a channel whose adapter says the person cannot be reached, with the
        adapter’s reason
      - >-
        services-platform:modules/notifications/tests/unit/channel-resolution.spec.js#words
        a skipped channel with the adapter’s own sentence when it has one
      - TST-PFM-NOT-105
    provenance: test
    source: >-
      services-platform:modules/notifications/tests/unit/channel-resolution.spec.js#chooses
      a supported channel that has an adapter and a reachable person
    category: edge
    binding_scope: >-
      Each binding supplies its own assertions; the aggregate references do not
      imply that one executable file proves every step.
  - id: ATS-PFM-NOT-007-09
    title: Deduplicate and bound provider event history
    level: api
    proves:
      - BR-CAP-PFM-NOT-007-09
    preconditions: Prepare a stored delivery and distinct provider event references.
    steps: Record a provider event twice and next more than fifty distinct events.
    expected: 'Replay adds no duplicate event, and only the latest fifty events remain.'
    negative: false
    data_needs: A stored delivery and distinct provider event references.
    bound_to:
      - >-
        services-platform:modules/notifications/tests/integration/services/persistence/deliveries.integration.spec.js#keeps
        the newest fifty events of a delivery and drops the oldest, never
        refusing the report
      - >-
        services-platform:modules/notifications/tests/integration/services/persistence/deliveries.integration.spec.js#writes
        a replayed event once: the same provider and provider event id answers
        nothing
      - TST-PFM-NOT-085
    provenance: test
    source: >-
      services-platform:modules/notifications/tests/integration/services/persistence/deliveries.integration.spec.js#keeps
      the newest fifty events of a delivery and drops the oldest, never refusing
      the report
    category: boundary
    binding_scope: >-
      Each binding supplies its own assertions; the aggregate references do not
      imply that one executable file proves every step.
  - id: ATS-PFM-NOT-007-10
    title: Channel failure and operator retry preserve routing explanations
    category: interaction
    level: api
    proves:
      - BR-CAP-PFM-NOT-007-05
    exercises:
      - CAP-PFM-NOT-007
      - CAP-PFM-NOT-012
      - CAP-PFM-NOT-013
      - CAP-PFM-NOT-015
      - CAP-PFM-NOT-008
    preconditions: >-
      Prepare personal and operational occurrences plus stored personal channel
      preferences; use an adapter that exhausts its retry budget.
    steps: >-
      Process both occurrences, exhaust operational delivery, retry it from HQ,
      and read notice progress and channel explanations.
    expected: >-
      Stored preferences do not activate personal Slack. The exhausted
      operational delivery returns to queued work through the operator action,
      and its subsequent outcome agrees with its notice progress.
    negative: false
    data_needs: >-
      A suite-owned workspace, named staff accounts and unique occurrence keys;
      external adapters record attempts without shared writes.
    bound_to: []
    provenance: proposed
    source:
      - >-
        rhythm-v2-docs:specs/platform/notifications/planning/capabilities/CAP-PFM-NOT-007-deliver-each-notice-on-the-channels-it-may-use.md#frontmatter
      - >-
        rhythm-v2-docs:specs/platform/notifications/planning/capabilities/CAP-PFM-NOT-012-support-a-customers-notifications-from-rhythm-hq.md#frontmatter
      - >-
        rhythm-v2-docs:specs/platform/notifications/planning/capabilities/CAP-PFM-NOT-013-alert-rhythms-own-staff-in-slack.md#frontmatter
      - >-
        rhythm-v2-docs:specs/platform/notifications/planning/capabilities/CAP-PFM-NOT-015-let-the-organization-and-its-people-choose-how-they-hear.md#frontmatter
      - >-
        rhythm-v2-docs:specs/platform/notifications/planning/capabilities/CAP-PFM-NOT-008-keep-notifications-whole-when-part-of-the-pipeline-fails.md#frontmatter
    interaction_with:
      - CAP-PFM-NOT-012
      - CAP-PFM-NOT-013
      - CAP-PFM-NOT-015
      - CAP-PFM-NOT-008
evidence:
  - >-
    Pinned evidence inventory 1:
    services-platform:modules/notifications/tests/integration/services/orchestration/orchestrate-request.integration.spec.js;
    services-platform:modules/notifications/tests/unit/channel-resolution.spec.js;
    services-platform:modules/notifications/tests/integration/services/dispatch/dispatch-notifications.integration.spec.js;
    services-platform:modules/notifications/tests/integration/services/persistence/delivery-works.integration.spec.js;
    services-platform:modules/notifications/tests/integration/services/persistence/deliveries.integration.spec.js;
    services-platform:modules/notifications/tests/integration/services/dispatch/deliver-delivery.integration.spec.js;
    services-platform:modules/notifications/tests/unit/queue-items.spec.js;
    services-platform:modules/notifications/tests/unit/instance-status.spec.js;
    services-platform:modules/notifications/tests/integration/services/persistence/instances.integration.spec.js
coverage:
  rule: covered
  transition: 'gap: FND-PFM-NOT-068'
  validation: covered
  permission: not-applicable
  edge: covered
  failure: covered
  boundary: covered
  concurrency: 'gap: FND-PFM-NOT-068'
  invariant: covered
  interaction: covered
coverage_notes:
  permission: >-
    This capability declares no independent access key; callers enter through
    the producing or reading capability.
  concurrency: >-
    The suite lacks a complete race case with controlled interleaving and
    post-race assertions.
  transition: >-
    The lifecycle matrix records every transition as requiring independent
    verification of its guard and effects; existing scenarios remain useful
    candidates.
findings:
  - FND-PFM-NOT-068
transition_coverage:
  - state_model: STATE-PFM-NOT-003
    transition_index: 1
    from: pending
    to: delivered
    trigger: Creation records a delivered console item.
    source: >-
      services-platform:modules/notifications/instance-status.ts:deriveInstanceStatus;
      services-platform:modules/notifications/services/persistence/instances.ts:refreshInstanceStatus
    existing_candidates:
      - ATS-PFM-NOT-007-06
    decision: 'gap: independent guard and side-effect proof has not been verified'
  - state_model: STATE-PFM-NOT-003
    transition_index: 2
    from: pending
    to: routed
    trigger: Creation queues a chat or text channel.
    source: >-
      services-platform:modules/notifications/tests/integration/services/system/system-audience.integration.spec.js#is
      accepted as one request, one instance with no recipient and one queued
      slack_system delivery with the Block Kit payload
    existing_candidates:
      - ATS-PFM-NOT-007-06
    decision: 'gap: independent guard and side-effect proof has not been verified'
  - state_model: STATE-PFM-NOT-003
    transition_index: 3
    from: routed
    to: delivering
    trigger: A consumer claims a delivery for sending.
    source: >-
      services-platform:modules/notifications/services/dispatch/deliver-delivery.ts:deliverQueuedDelivery
    existing_candidates:
      - ATS-PFM-NOT-007-06
    decision: 'gap: independent guard and side-effect proof has not been verified'
  - state_model: STATE-PFM-NOT-003
    transition_index: 4
    from: delivering
    to: delivered
    trigger: Delivery completes with a successful channel result.
    source: >-
      services-platform:modules/notifications/instance-status.ts:deriveInstanceStatus
    existing_candidates:
      - ATS-PFM-NOT-007-06
    decision: 'gap: independent guard and side-effect proof has not been verified'
  - state_model: STATE-PFM-NOT-003
    transition_index: 5
    from: delivering
    to: partially delivered
    trigger: A channel fails while another has delivered.
    source: >-
      services-platform:modules/notifications/tests/unit/instance-status.spec.js#is
      partially delivered when one delivered and another failed, none still
      active
    existing_candidates:
      - ATS-PFM-NOT-007-06
    decision: 'gap: independent guard and side-effect proof has not been verified'
  - state_model: STATE-PFM-NOT-003
    transition_index: 6
    from: delivering
    to: failed
    trigger: All selected delivery attempts fail.
    source: >-
      services-platform:modules/notifications/services/dispatch/deliver-delivery.ts:deliverQueuedDelivery
    existing_candidates:
      - ATS-PFM-NOT-007-06
    decision: 'gap: independent guard and side-effect proof has not been verified'
  - state_model: STATE-PFM-NOT-003
    transition_index: 7
    from: routed
    to: no channel
    trigger: The remaining queued channel is skipped.
    source: >-
      services-platform:modules/notifications/instance-status.ts:deriveInstanceStatus
    existing_candidates:
      - ATS-PFM-NOT-007-06
    decision: 'gap: independent guard and side-effect proof has not been verified'
  - state_model: STATE-PFM-NOT-003
    transition_index: 8
    from: failed
    to: routed
    trigger: HQ retries an exhausted delivery.
    source: >-
      services-platform:modules/notifications/tests/integration/services/dispatch/deliver-delivery.integration.spec.js#queues
      a delivery that ran out of receives again, with its work row, and nothing
      else
    existing_candidates:
      - ATS-PFM-NOT-007-06
    decision: 'gap: independent guard and side-effect proof has not been verified'
  - state_model: STATE-PFM-NOT-003
    transition_index: 9
    from: delivered
    to: superseded
    trigger: Withdrawal reaches a notice with console delivery.
    source: >-
      services-platform:modules/notifications/tests/integration/services/orchestration/cancel-and-supersede.integration.spec.js#cancels
      the queued delivery and its work row, and does not recall one that already
      left
    existing_candidates:
      - ATS-PFM-NOT-007-06
    decision: 'gap: independent guard and side-effect proof has not been verified'
  - state_model: STATE-PFM-NOT-003
    transition_index: 10
    from: routed
    to: superseded
    trigger: Withdrawal reaches a notice before channel sending.
    source: >-
      services-platform:modules/notifications/tests/integration/services/dispatch/deliver-delivery.integration.spec.js#cancels,
      not sends, a delivery whose notification was superseded after it was
      queued
    existing_candidates:
      - ATS-PFM-NOT-007-06
    decision: 'gap: independent guard and side-effect proof has not been verified'
  - state_model: STATE-PFM-NOT-003
    transition_index: 11
    from: routed
    to: expired
    trigger: A consumer or nightly sweep detects expiry.
    source: >-
      services-platform:modules/notifications/services/orchestration/expire-request.ts;
      services-platform:modules/notifications/services/reconcile/reconcile-tenant.ts:reconcileTenant
    existing_candidates:
      - ATS-PFM-NOT-007-06
    decision: 'gap: independent guard and side-effect proof has not been verified'
  - state_model: STATE-PFM-NOT-005
    transition_index: 1
    from: queued
    to: sending
    trigger: The channel consumer claims queued work.
    source: >-
      services-platform:modules/notifications/tests/integration/services/persistence/deliveries.integration.spec.js#lets
      only one of two consumers racing for the send win it
    existing_candidates:
      - ATS-PFM-NOT-007-06
    decision: 'gap: independent guard and side-effect proof has not been verified'
  - state_model: STATE-PFM-NOT-005
    transition_index: 2
    from: sending
    to: sent
    trigger: The adapter accepts the delivery.
    source: >-
      services-platform:modules/notifications/services/dispatch/deliver-delivery.ts:deliverQueuedDelivery
    existing_candidates:
      - ATS-PFM-NOT-007-06
    decision: 'gap: independent guard and side-effect proof has not been verified'
  - state_model: STATE-PFM-NOT-005
    transition_index: 3
    from: sent
    to: delivered
    trigger: The adapter or provider confirms delivery.
    source: >-
      services-platform:modules/notifications/services/dispatch/deliver-delivery.ts:deliverQueuedDelivery
    existing_candidates:
      - ATS-PFM-NOT-007-06
    decision: 'gap: independent guard and side-effect proof has not been verified'
  - state_model: STATE-PFM-NOT-005
    transition_index: 4
    from: sending
    to: queued
    trigger: A retryable adapter result or exception leaves another receive available.
    source: >-
      services-platform:modules/notifications/tests/unit/slack-system-adapter.spec.js#answers
      a 429 as a retryable failure carrying Retry-After
    existing_candidates:
      - ATS-PFM-NOT-007-06
    decision: 'gap: independent guard and side-effect proof has not been verified'
  - state_model: STATE-PFM-NOT-005
    transition_index: 5
    from: sending
    to: failed
    trigger: >-
      The adapter refuses permanently, the last receive fails, or reconciliation
      detects fifteen minutes of ambiguity.
    source: >-
      services-platform:modules/notifications/tests/integration/services/dispatch/deliver-delivery.integration.spec.js#fails
      the delivery attempts_exhausted on the last receive and gives the item
      back at once to reach the dead-letter queue
    existing_candidates:
      - ATS-PFM-NOT-007-06
    decision: 'gap: independent guard and side-effect proof has not been verified'
  - state_model: STATE-PFM-NOT-005
    transition_index: 6
    from: sending
    to: skipped
    trigger: The adapter reports that delivery is unavailable.
    source: >-
      services-platform:modules/notifications/services/channels/slack-system/slack-system-adapter.ts
    existing_candidates:
      - ATS-PFM-NOT-007-06
    decision: 'gap: independent guard and side-effect proof has not been verified'
  - state_model: STATE-PFM-NOT-005
    transition_index: 7
    from: queued
    to: skipped
    trigger: Dispatch cannot find the named adapter.
    source: >-
      services-platform:modules/notifications/services/dispatch/deliver-delivery.ts:deliverQueuedDelivery
    existing_candidates:
      - ATS-PFM-NOT-007-06
    decision: 'gap: independent guard and side-effect proof has not been verified'
  - state_model: STATE-PFM-NOT-005
    transition_index: 8
    from: queued
    to: cancelled
    trigger: Withdrawal or replacement reaches queued work.
    source: >-
      services-platform:modules/notifications/tests/integration/services/dispatch/deliver-delivery.integration.spec.js#cancels,
      not sends, a delivery whose notification was superseded after it was
      queued
    existing_candidates:
      - ATS-PFM-NOT-007-06
    decision: 'gap: independent guard and side-effect proof has not been verified'
  - state_model: STATE-PFM-NOT-005
    transition_index: 9
    from: queued
    to: expired
    trigger: Processing or reconciliation detects an expired request.
    source: >-
      services-platform:modules/notifications/services/orchestration/expire-request.ts
    existing_candidates:
      - ATS-PFM-NOT-007-06
    decision: 'gap: independent guard and side-effect proof has not been verified'
  - state_model: STATE-PFM-NOT-005
    transition_index: 10
    from: failed
    to: queued
    trigger: An HQ operator requests an exhausted-delivery retry.
    source: >-
      services-platform:modules/notifications/tests/integration/services/dispatch/deliver-delivery.integration.spec.js#queues
      a delivery that ran out of receives again, with its work row, and nothing
      else
    existing_candidates:
      - ATS-PFM-NOT-007-06
    decision: 'gap: independent guard and side-effect proof has not been verified'
workflow_coverage:
  - workflow: WF-PFM-NOT-003
    capabilities:
      - CAP-PFM-NOT-003
      - CAP-PFM-NOT-004
      - CAP-PFM-NOT-005
      - CAP-PFM-NOT-006
      - CAP-PFM-NOT-007
      - CAP-PFM-NOT-008
    scenario_candidate: ATS-PFM-NOT-007-10
    coverage: 'gap: FND-PFM-NOT-084'
    reason: >-
      The proposed related-capability sequence does not independently isolate
      every workflow hop, failure seam and interleaving.
  - workflow: WF-PFM-NOT-005
    capabilities:
      - CAP-PFM-NOT-007
      - CAP-PFM-NOT-008
    scenario_candidate: ATS-PFM-NOT-007-10
    coverage: 'gap: FND-PFM-NOT-084'
    reason: >-
      The proposed related-capability sequence does not independently isolate
      every workflow hop, failure seam and interleaving.
  - workflow: WF-PFM-NOT-006
    capabilities:
      - CAP-PFM-NOT-013
      - CAP-PFM-NOT-003
      - CAP-PFM-NOT-007
    scenario_candidate: ATS-PFM-NOT-007-10
    coverage: 'gap: FND-PFM-NOT-084'
    reason: >-
      The proposed related-capability sequence does not independently isolate
      every workflow hop, failure seam and interleaving.
domain_invariant_coverage:
  - invariant: INV-DOM-PFM-NOT-003-07
    scenarios:
      - ATS-PFM-NOT-007-07
    coverage: specified
    source: >-
      rhythm-v2-docs:specs/platform/notifications/design/objects/DOM-PFM-NOT-003-staff-notification.md
    verification_note: >-
      The candidate supplies the fixture outcome; individual binding limits
      remain applicable.
---

# Route and send notification deliveries: Capability Test Specification

## Coverage

Each scenario names the guarantee it proves. Category coverage describes specified cases; executable bindings retain their narrower assertion boundaries. Lifecycle coverage remains a gap wherever guards and effects lack independent verification.

| Rule | Proof |
|---|---|
| `BR-CAP-PFM-NOT-007-01` | `ATS-PFM-NOT-007-01`, `ATS-PFM-NOT-007-10` |
| `BR-CAP-PFM-NOT-007-02` | `ATS-PFM-NOT-007-02` |
| `BR-CAP-PFM-NOT-007-03` | `ATS-PFM-NOT-007-08` |
| `BR-CAP-PFM-NOT-007-04` | `ATS-PFM-NOT-007-03` |
| `BR-CAP-PFM-NOT-007-05` | `ATS-PFM-NOT-007-04` |
| `BR-CAP-PFM-NOT-007-06` | `ATS-PFM-NOT-007-05` |
| `BR-CAP-PFM-NOT-007-07` | `ATS-PFM-NOT-007-06` |
| `BR-CAP-PFM-NOT-007-08` | `ATS-PFM-NOT-007-07` |
| `BR-CAP-PFM-NOT-007-09` | `ATS-PFM-NOT-007-09` |

## Interactions With Related Capabilities

Prepare personal and operational occurrences plus stored personal channel preferences; use an adapter that exhausts its retry budget. Process both occurrences, exhaust operational delivery, retry it from HQ, and read notice progress and channel explanations.

The interaction case combines the producing, processing and reading outcomes named in its frontmatter. It remains proposed until an executable suite performs that complete sequence.
