---
id: ATS-PFM-NOT-019
scope: CAP-PFM-NOT-019
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-019-01
    title: Correlate and deduplicate provider reports
    level: api
    proves:
      - BR-CAP-PFM-NOT-019-01
    preconditions: Prepare a sent compliance notice.
    steps: 'Process delivery, open and click feedback and replay one event.'
    expected: >-
      Each report is associated with the tagged message once; replay is
      recognized without another event.
    negative: false
    data_needs: Use only records created and owned by this suite.
    bound_to:
      - >-
        services-platform:modules/communications/tests/integration/lifecycle/contact-role-compliance-communication.integration.spec.js#records
        each new event and recognizes the repeated delivery as a duplicate
      - >-
        services-platform:modules/communications/tests/integration/services/persistence/messages.integration.spec.js#records
        the event against its message
      - >-
        services-platform:modules/communications/tests/integration/services/persistence/messages.integration.spec.js#answers
        not_found for a message this tenant does not have
      - TST-PFM-NOT-030
      - TST-PFM-NOT-042
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/lifecycle/contact-role-compliance-communication.integration.spec.js#records
      each new event and recognizes the repeated delivery as a duplicate
    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-019-02
    title: Preserve delivery precedence across report order
    level: api
    proves:
      - BR-CAP-PFM-NOT-019-02
      - INV-DOM-PFM-NOT-005-08
    preconditions: Prepare a delivered message.
    steps: >-
      Apply deferral after delivery, replay an existing event and process a
      reverse-ordered event sequence.
    expected: >-
      A delivered message stays delivered, duplicate feedback does not increase
      counters and event listing follows occurrence time.
    negative: false
    data_needs: Use only records created and owned by this suite.
    bound_to:
      - >-
        services-platform:modules/communications/tests/integration/lifecycle/contact-role-compliance-communication.integration.spec.js#keeps
        the message delivered when a deferral arrives after the delivery
      - >-
        services-platform:modules/communications/tests/integration/lifecycle/contact-role-compliance-communication.integration.spec.js#keeps
        the delivery time the provider reported
      - >-
        services-platform:modules/communications/tests/integration/lifecycle/contact-role-compliance-communication.integration.spec.js#lists
        the provider’s events in the order they happened, whatever order they
        arrived in
      - >-
        services-platform:modules/communications/tests/integration/services/persistence/messages.integration.spec.js#leaves
        the summary exactly where the first copy put it, so a redelivery counts
        nothing twice
      - TST-PFM-NOT-030
      - TST-PFM-NOT-042
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/lifecycle/contact-role-compliance-communication.integration.spec.js#keeps
      the message delivered when a deferral arrives after the delivery
    category: concurrency
    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/communications/db/communication-messages.table.ts:DELIVERY_STATE_PRECEDENCE
  - id: ATS-PFM-NOT-019-03
    title: Separate automated engagement from likely-human activity
    level: api
    proves:
      - BR-CAP-PFM-NOT-019-03
    preconditions: Prepare a delivered message.
    steps: >-
      Supply image-proxy open feedback and click feedback containing URL query
      parameters.
    expected: >-
      The automated open increments signal counts without human counts; the
      retained click URL excludes its query.
    negative: false
    data_needs: Use only records created and owned by this suite.
    bound_to:
      - >-
        services-platform:modules/communications/tests/integration/lifecycle/contact-role-compliance-communication.integration.spec.js#counts
        an open by an image proxy as an open, not as a likely human one
      - >-
        services-platform:modules/communications/tests/integration/lifecycle/contact-role-compliance-communication.integration.spec.js#counts
        the click and keeps its link without the query string
      - TST-PFM-NOT-030
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/lifecycle/contact-role-compliance-communication.integration.spec.js#counts
      an open by an image proxy as an open, not as a likely human one
    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-019-04
    title: Filter and page communication history
    level: api
    proves:
      - BR-CAP-PFM-NOT-019-05
    preconditions: >-
      Prepare messages from multiple requests associated with two different
      source records.
    steps: >-
      Retrieve the list of by each filter; page through; ask for 500 per page;
      send a bad cursor and a type without an id.
    expected: >-
      Each view matches its filter in descending time order, pages contain at
      most two hundred and invalid inputs fail.
    negative: false
    data_needs: Use only records created and owned by this suite.
    bound_to:
      - >-
        services-platform:modules/communications/tests/e2e/messages.e2e.spec.js#lists
        only the messages of the request asked about
      - >-
        services-platform:modules/communications/tests/e2e/messages.e2e.spec.js#lists
        only the messages about the source record asked about
      - >-
        services-platform:modules/communications/tests/e2e/messages.e2e.spec.js#answers
        the rest of the list after that cursor, repeating nothing
      - >-
        services-platform:modules/communications/tests/e2e/messages.e2e.spec.js#refuses
        a cursor this list never answered as 400
      - >-
        services-platform:modules/communications/tests/e2e/messages.e2e.spec.js#refuses
        a source type given without its id as 400
      - >-
        services-platform:modules/communications/tests/integration/services/persistence/messages.queries.integration.spec.js#answers
        at most the two hundred one page holds, however many were asked for
      - >-
        services-platform:modules/communications/tests/integration/services/persistence/messages.queries.integration.spec.js#considers
        at most the 500 newest of them, answering their messages newest first
      - TST-PFM-NOT-023
      - TST-PFM-NOT-043
    provenance: test
    source: >-
      services-platform:modules/communications/tests/e2e/messages.e2e.spec.js#lists
      only the messages of the request asked about
    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-019-05
    title: Require sensitive permission for available body content
    level: api
    proves:
      - BR-CAP-PFM-NOT-019-07
    preconditions: Prepare a sent message and one whose body object is gone.
    steps: >-
      Retrieve the content of each with the read-content permission; read it
      with only the read permission.
    expected: >-
      Authorized reads return subject, bodies and sender. Missing storage is
      reported as unavailable; callers without content permission are denied.
    negative: true
    data_needs: Use only records created and owned by this suite.
    bound_to:
      - >-
        services-platform:modules/communications/tests/e2e/messages.e2e.spec.js#answers
        the HTML and text bodies rendered for this recipient
      - >-
        services-platform:modules/communications/tests/integration/services/storage/communication-objects.integration.spec.js#says
        the content is gone, rather than answering an empty e-mail, when the
        object is not there
      - >-
        services-platform:modules/communications/tests/integration/services/storage/communication-objects.integration.spec.js#refuses
        a message this tenant cannot see
      - TST-PFM-NOT-023
      - TST-PFM-NOT-060
    provenance: test
    source: >-
      services-platform:modules/communications/tests/e2e/messages.e2e.spec.js#answers
      the HTML and text bodies rendered for this recipient
    category: permission
    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-019-06
    title: Read changes using the supplied service filter
    level: api
    proves:
      - BR-CAP-PFM-NOT-019-09
    preconditions: Messages of two services in two workspaces.
    steps: >-
      Retrieve changes as one service in pages of two; change a message a second
      time; read as a service that sent nothing; exceed the page ceiling.
    expected: >-
      Only matching service names return in ascending change order with a resume
      cursor. A later change reappears, unmatched results are empty and invalid
      cursors fail.
    negative: true
    data_needs: Use only records created and owned by this suite.
    bound_to:
      - >-
        services-platform:modules/communications/tests/integration/services/persistence/messages.changes.queries.integration.spec.js#answers
        the messages of the named service’s requests in the tenant
      - >-
        services-platform:modules/communications/tests/integration/services/persistence/messages.changes.queries.integration.spec.js#leaves
        out another service’s messages in the same tenant
      - >-
        services-platform:modules/communications/tests/integration/services/persistence/messages.changes.queries.integration.spec.js#answers
        it with its latest state, the provider’s report included
      - >-
        services-platform:modules/communications/tests/integration/services/requests/changed-messages.integration.spec.js#refuses
        a limit beyond the page ceiling
      - >-
        services-platform:modules/communications/tests/integration/services/persistence/messages.changes.queries.integration.spec.js#refuses
        a cursor it did not issue
      - TST-PFM-NOT-041
      - TST-PFM-NOT-054
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/services/persistence/messages.changes.queries.integration.spec.js#answers
      the messages of the named service’s requests in the tenant
    category: validation
    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-019-07
    title: Enforce workspace isolation on all mail detail
    level: api
    proves:
      - BR-CAP-PFM-NOT-019-10
      - INV-DOM-PFM-NOT-005-10
    preconditions: Two workspaces with messages.
    steps: 'Retrieve the other''s requests, messages and content by id.'
    expected: 'Every foreign request, message, body and event lookup returns not found.'
    negative: true
    data_needs: Use only records created and owned by this suite.
    bound_to:
      - >-
        services-platform:modules/communications/tests/integration/lifecycle/contact-role-compliance-communication.integration.spec.js#shows
        another tenant nothing of it
      - >-
        services-platform:modules/communications/tests/integration/lifecycle/contact-role-compliance-communication.integration.spec.js#refuses
        another tenant the message itself
      - >-
        services-platform:modules/communications/tests/e2e/messages.e2e.spec.js#lists
        nothing of this tenant under the other tenant
      - >-
        services-platform:modules/communications/tests/e2e/requests.e2e.spec.js#lists
        nothing of this tenant under the other tenant
      - TST-PFM-NOT-023
      - TST-PFM-NOT-024
      - TST-PFM-NOT-030
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/lifecycle/contact-role-compliance-communication.integration.spec.js#shows
      another tenant nothing of it
    category: permission
    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:docs/row-level-security.md#What it covers'
  - id: ATS-PFM-NOT-019-08
    title: Keep copy feedback separate from primary delivery
    level: api
    proves:
      - BR-CAP-PFM-NOT-019-12
    preconditions: Prepare a message with a CC copy.
    steps: 'Report the copy''s bounce, next the contact''s delivery.'
    expected: >-
      The bounced copy becomes suppressed, the primary contact does not, and
      primary-recipient delivery leaves the message delivered.
    negative: false
    data_needs: Use only records created and owned by this suite.
    bound_to:
      - >-
        services-platform:modules/communications/tests/integration/services/dispatch/template-copies.integration.spec.js#suppresses
        the copy that bounced
      - >-
        services-platform:modules/communications/tests/integration/services/dispatch/template-copies.integration.spec.js#does
        not suppress the contact
      - >-
        services-platform:modules/communications/tests/integration/services/dispatch/template-copies.integration.spec.js#records
        the copy's bounce on the message history without marking the contact's
        message bounced
      - >-
        services-platform:modules/communications/tests/integration/services/dispatch/template-copies.integration.spec.js#marks
        the message delivered when the contact's own delivery arrives
      - TST-PFM-NOT-039
    provenance: test
    source: >-
      services-platform:modules/communications/tests/integration/services/dispatch/template-copies.integration.spec.js#suppresses
      the copy that bounced
    category: interaction
    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-019-09
    title: Copy feedback protects the copy without rewriting primary delivery
    category: interaction
    level: api
    proves:
      - BR-CAP-PFM-NOT-019-12
    exercises:
      - CAP-PFM-NOT-019
      - CAP-PFM-NOT-020
      - CAP-PFM-NOT-017
    preconditions: Use a request with a primary recipient and one eligible copy.
    steps: >-
      Send the message, report a permanent bounce for the copy and delivery for
      the primary recipient, then submit another request to each address.
    expected: >-
      History records both reports. The primary message remains delivered;
      subsequent copy mail is withheld while eligible primary mail continues.
    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-019-record-what-became-of-each-e-mail.md#frontmatter
      - >-
        rhythm-v2-docs:specs/platform/notifications/planning/capabilities/CAP-PFM-NOT-020-protect-deliverability-and-the-associations-sending-address.md#frontmatter
      - >-
        rhythm-v2-docs:specs/platform/notifications/planning/capabilities/CAP-PFM-NOT-017-ask-platform-to-send-an-association-e-mail.md#frontmatter
    interaction_with:
      - CAP-PFM-NOT-020
      - CAP-PFM-NOT-017
evidence:
  - >-
    Pinned evidence inventory 1:
    services-platform:modules/communications/tests/integration/lifecycle/contact-role-compliance-communication.integration.spec.js;
    services-platform:modules/communications/tests/e2e/messages.e2e.spec.js;
    services-platform:modules/communications/tests/integration/services/storage/communication-objects.integration.spec.js;
    services-platform:modules/communications/tests/integration/services/requests/changed-messages.integration.spec.js;
    services-platform:modules/communications/tests/integration/services/dispatch/template-copies.integration.spec.js
  - >-
    Pinned evidence inventory 2:
    services-platform:modules/communications/tests/integration/services/persistence/messages.integration.spec.js;
    services-platform:modules/communications/tests/integration/services/persistence/messages.queries.integration.spec.js;
    services-platform:modules/communications/tests/integration/services/persistence/messages.changes.queries.integration.spec.js;
    services-platform:modules/communications/tests/e2e/requests.e2e.spec.js
coverage:
  rule: 'gap: FND-PFM-NOT-080'
  transition: 'gap: FND-PFM-NOT-080'
  validation: covered
  permission: covered
  edge: covered
  failure: 'gap: FND-PFM-NOT-080'
  boundary: covered
  concurrency: covered
  invariant: covered
  interaction: covered
coverage_notes:
  rule: >-
    The specification does not isolate a general positive outcome outside the
    narrower category cases.
  transition: >-
    The lifecycle matrix records every transition as requiring independent
    verification of its guard and effects; existing scenarios remain useful
    candidates.
  failure: The suite lacks an isolated dependency failure and recovery case.
findings:
  - FND-PFM-NOT-080
transition_coverage:
  - state_model: STATE-PFM-NOT-012
    transition_index: 1
    from: unknown
    to: delivered
    trigger: Primary-recipient feedback reports delivery.
    source: >-
      services-platform:modules/communications/services/feedback/process-ses-event.ts:processSesEvent
    existing_candidates: []
    decision: 'gap: independent guard and side-effect proof has not been verified'
  - state_model: STATE-PFM-NOT-012
    transition_index: 2
    from: unknown
    to: deferred
    trigger: Feedback reports delivery delay.
    source: >-
      services-platform:modules/communications/db/communication-messages.table.ts:DELIVERY_STATE_PRECEDENCE
    existing_candidates: []
    decision: 'gap: independent guard and side-effect proof has not been verified'
  - state_model: STATE-PFM-NOT-012
    transition_index: 3
    from: delivered
    to: bounced permanent
    trigger: Feedback reports a permanent bounce after delivery.
    source: >-
      services-platform:modules/communications/services/feedback/process-ses-event.ts:processSesEvent
    existing_candidates: []
    decision: 'gap: independent guard and side-effect proof has not been verified'
  - state_model: STATE-PFM-NOT-012
    transition_index: 4
    from: delivered
    to: complained
    trigger: Feedback reports a complaint after delivery.
    source: >-
      services-platform:modules/communications/services/feedback/process-ses-event.ts:processSesEvent
    existing_candidates: []
    decision: 'gap: independent guard and side-effect proof has not been verified'
  - state_model: STATE-PFM-NOT-012
    transition_index: 5
    from: deferred
    to: delivered
    trigger: Delayed delivery later succeeds.
    source: >-
      services-platform:modules/communications/db/communication-messages.table.ts:DELIVERY_STATE_PRECEDENCE
    existing_candidates: []
    decision: 'gap: independent guard and side-effect proof has not been verified'
workflow_coverage:
  - workflow: WF-PFM-NOT-013
    capabilities:
      - CAP-PFM-NOT-019
      - CAP-PFM-NOT-020
    scenario_candidate: ATS-PFM-NOT-019-09
    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-005-08
    scenarios:
      - ATS-PFM-NOT-019-02
    coverage: specified
    source: >-
      rhythm-v2-docs:specs/platform/notifications/design/objects/DOM-PFM-NOT-005-association-communication.md
    verification_note: >-
      The candidate supplies the fixture outcome; individual binding limits
      remain applicable.
  - invariant: INV-DOM-PFM-NOT-005-10
    scenarios:
      - ATS-PFM-NOT-019-07
    coverage: specified
    source: >-
      rhythm-v2-docs:specs/platform/notifications/design/objects/DOM-PFM-NOT-005-association-communication.md
    verification_note: >-
      The candidate supplies the fixture outcome; individual binding limits
      remain applicable.
---

# Inspect communication outcomes and provider feedback: 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-019-01` | `ATS-PFM-NOT-019-01`, `ATS-PFM-NOT-019-09` |
| `BR-CAP-PFM-NOT-019-02` | `ATS-PFM-NOT-019-02` |
| `BR-CAP-PFM-NOT-019-03` | `ATS-PFM-NOT-019-03` |
| `BR-CAP-PFM-NOT-019-05` | `ATS-PFM-NOT-019-04` |
| `BR-CAP-PFM-NOT-019-07` | `ATS-PFM-NOT-019-05` |
| `BR-CAP-PFM-NOT-019-09` | `ATS-PFM-NOT-019-06` |
| `BR-CAP-PFM-NOT-019-10` | `ATS-PFM-NOT-019-07` |
| `BR-CAP-PFM-NOT-019-12` | `ATS-PFM-NOT-019-08` |

## Interactions With Related Capabilities

Use a request with a primary recipient and one eligible copy. Send the message, report a permanent bounce for the copy and delivery for the primary recipient, then submit another request to each address.

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.
