---
id: ATS-PFM-TNL-015
scope: CAP-PFM-TNL-015
product_area: platform
module: tenant-lifecycle
revision: 3
status: proposed
approved_by: null
approved_at: null
source_sha:
  services-platform: ad3f6a6582843008ed0f5a17b131878d15829af1
  services-crm: a8d1364f1dd44b723bb5f9acba12f983e9ec8d69
  services-commerce: c8b66a98e2490617aec38ba5c8165dd9bd0a8f9b
  services-committees: 36129c24daeaadcdb339218c38cdeaff9861359d
  services-membership: 5994c270e8a37a30f848e99ba5b5ee6b87c08893
  services-submissions: 64236c19be89579ef5897aad7de3925ed3ce03bf
  services-websites: 5fc6823c69d5e6f208c21dba8c0eb3132f8bdc4c
  services-marketing: d071a159fe9782bb71ff246d5c67d5bbfe07fbfa
  rhythm-v2-docs: d318a948692343e2f0a7ecc2e11c50db179a8c95
scenarios:
  - id: ATS-PFM-TNL-015-01
    title: Reminder timing follows the chosen wait
    level: unit
    proves:
      - BR-CAP-PFM-TNL-015-01
    preconditions: 'The fixture covers long, boundary and short waiting periods.'
    steps: Calculate request notice obligations.
    expected: Seven-day reminders require at least eight days and final reminders become immediate below two days.
    negative: false
    data_needs: >-
      Use the run-owned records and caller contexts created by the cited tests; use their provider and clock
      seams only where declared.
    bound_to:
      - >-
        services-platform:modules/tenants/tests/unit/services/deletion/waiting-period.spec.js#owes the notice
        at request, the seven-day reminder and the one-day reminder, in that order
      - >-
        services-platform:modules/tenants/tests/unit/services/deletion/waiting-period.spec.js#is owed at
        exactly eight days, the shortest period with room for it
      - >-
        services-platform:modules/tenants/tests/unit/services/deletion/waiting-period.spec.js#is skipped just
        below eight days, where it would fall before the request itself
      - >-
        services-platform:modules/tenants/tests/unit/services/deletion/waiting-period.spec.js#is due at once
        just below two days, so a short period still has the notice destruction waits for
      - >-
        services-platform:modules/tenants/tests/unit/services/deletion/waiting-period.spec.js#is due at once
        for a one-day period, the never-active tenant
      - >-
        services-platform:modules/tenants/tests/integration/services/deletion/request-deletion.integration.spec.js#makes
        both of those notices due at once, or a tenant with a one-day period could never be deleted
    provenance: test
    source: >-
      services-platform:modules/tenants/tests/unit/services/deletion/waiting-period.spec.js#owes the notice at
      request, the seven-day reminder and the one-day reminder, in that order
    category: boundary
    action_evidence:
      provenance: test
      source: >-
        services-platform:modules/tenants/tests/unit/services/deletion/waiting-period.spec.js#owes the notice
        at request, the seven-day reminder and the one-day reminder, in that order
    assertion_evidence:
      provenance: test
      source: >-
        services-platform:modules/tenants/tests/unit/services/deletion/waiting-period.spec.js#owes the notice
        at request, the seven-day reminder and the one-day reminder, in that order
    binding_note: >-
      The original test-title references contribute separate assertions from several files. No suite receives
      an implements link for the complete combined scenario; a full executable mapping remains
      FND-PFM-TNL-084.
    binding_gap: FND-PFM-TNL-084
  - id: ATS-PFM-TNL-015-02
    title: State changes create notice obligations
    level: api
    proves:
      - BR-CAP-PFM-TNL-015-02
    preconditions: 'Fixtures exercise hold, release, execution, completion and failure.'
    steps: Inspect notice obligations after each event.
    expected: Each event retains the corresponding notice kind.
    negative: false
    data_needs: >-
      Use the run-owned records and caller contexts created by the cited tests; use their provider and clock
      seams only where declared.
    bound_to:
      - >-
        services-platform:modules/tenants/tests/integration/services/deletion/deletion-holds.integration.spec.js#owes
        a notice, because a schedule that changes silently is a schedule nobody can trust
      - >-
        services-platform:modules/tenants/tests/integration/services/deletion/deletion-holds.integration.spec.js#owes
        a released notice
      - >-
        services-platform:modules/tenants/tests/integration/services/deletion/deletion-holds.integration.spec.js#owes
        a cancelled notice
      - >-
        services-platform:modules/tenants/tests/integration/services/deletion/begin-destruction.integration.spec.js#owes
        an executing notice, so somebody is told the point of no return was passed
      - >-
        services-platform:modules/tenants/tests/integration/services/deletion/tenant-purge.integration.spec.js#tells
        the people the plan owes a last notice to
      - >-
        services-platform:modules/tenants/tests/integration/services/deletion/tenant-purge-recovery.integration.spec.js#tells
        the people a deletion that failed was not completed
    provenance: test
    source: >-
      services-platform:modules/tenants/tests/integration/services/deletion/deletion-holds.integration.spec.js#owes
      a notice, because a schedule that changes silently is a schedule nobody can trust
    category: transition
    action_evidence:
      provenance: test
      source: >-
        services-platform:modules/tenants/tests/integration/services/deletion/deletion-holds.integration.spec.js#owes
        a notice, because a schedule that changes silently is a schedule nobody can trust
    assertion_evidence:
      provenance: test
      source: >-
        services-platform:modules/tenants/tests/integration/services/deletion/deletion-holds.integration.spec.js#owes
        a notice, because a schedule that changes silently is a schedule nobody can trust
    binding_note: >-
      The original test-title references contribute separate assertions from several files. No suite receives
      an implements link for the complete combined scenario; a full executable mapping remains
      FND-PFM-TNL-084.
    binding_gap: FND-PFM-TNL-084
  - id: ATS-PFM-TNL-015-03
    title: Recipients remain internal
    level: api
    proves:
      - BR-CAP-PFM-TNL-015-03
    preconditions: The request advances through its waiting period.
    steps: Inspect captured recipients for each notice.
    expected: 'Recipients are the staff list and requester, with no customer-facing addition.'
    negative: false
    data_needs: >-
      Use the run-owned records and caller contexts created by the cited tests; use their provider and clock
      seams only where declared.
    bound_to:
      - >-
        services-platform:modules/tenants/tests/integration/services/deletion/tenant-deletion-schedule.integration.spec.js#tells
        the staff list and the requester, and nobody else
      - TST-PFM-TNL-064
    provenance: test
    source: >-
      services-platform:modules/tenants/tests/integration/services/deletion/tenant-deletion-schedule.integration.spec.js#tells
      the staff list and the requester, and nobody else
    category: permission
    action_evidence:
      provenance: test
      source: >-
        services-platform:modules/tenants/tests/integration/services/deletion/tenant-deletion-schedule.integration.spec.js#tells
        the staff list and the requester, and nobody else
    assertion_evidence:
      provenance: test
      source: >-
        services-platform:modules/tenants/tests/integration/services/deletion/tenant-deletion-schedule.integration.spec.js#tells
        the staff list and the requester, and nobody else
    binding_note: >-
      All cited checks for this scenario occur in the registered file. The link establishes inspected
      executable source rather than a passing run.
  - id: ATS-PFM-TNL-015-04
    title: Notice slots deduplicate recorded obligations
    level: api
    proves:
      - BR-CAP-PFM-TNL-015-04
    preconditions: A thirty-day request is scheduled.
    steps: Run its notice schedule and append an existing slot again.
    expected: Notices occur at their due times and repeated slot insertion adds no duplicate record.
    negative: false
    data_needs: >-
      Use the run-owned records and caller contexts created by the cited tests; use their provider and clock
      seams only where declared.
    bound_to:
      - >-
        services-platform:modules/tenants/tests/integration/services/deletion/tenant-deletion-schedule.integration.spec.js#sends
        the requested notice on the day of the request
      - >-
        services-platform:modules/tenants/tests/integration/services/deletion/tenant-deletion-schedule.integration.spec.js#sends
        both reminders, each on its own day, as the waiting period passes
      - >-
        services-platform:modules/tenants/tests/integration/services/deletion/tenant-deletion-schedule.integration.spec.js#slept
        until each notice was due rather than polling, and never for a fixed duration
      - >-
        services-platform:modules/tenants/tests/integration/services/persistence/tenant-deletion-requests.integration.spec.js#ignores
        a notice whose schedule slot is already taken, so a re-run sends nothing twice
    provenance: test
    source: >-
      services-platform:modules/tenants/tests/integration/services/deletion/tenant-deletion-schedule.integration.spec.js#sends
      the requested notice on the day of the request
    category: invariant
    action_evidence:
      provenance: test
      source: >-
        services-platform:modules/tenants/tests/integration/services/deletion/tenant-deletion-schedule.integration.spec.js#sends
        the requested notice on the day of the request
    assertion_evidence:
      provenance: test
      source: >-
        services-platform:modules/tenants/tests/integration/services/deletion/tenant-deletion-schedule.integration.spec.js#sends
        the requested notice on the day of the request
    binding_note: >-
      The original test-title references contribute separate assertions from several files. No suite receives
      an implements link for the complete combined scenario; a full executable mapping remains
      FND-PFM-TNL-084.
    binding_gap: FND-PFM-TNL-084
  - id: ATS-PFM-TNL-015-05
    title: Failed gating notices prevent destruction
    level: api
    proves:
      - BR-CAP-PFM-TNL-015-05
    preconditions: A required notice has failed and another fixture is environment-suppressed.
    steps: Advance schedules beyond eligibility.
    expected: 'Failure continues waiting with the notice identified, while suppression satisfies the gate.'
    negative: true
    data_needs: >-
      Use the run-owned records and caller contexts created by the cited tests; use their provider and clock
      seams only where declared.
    bound_to:
      - >-
        services-platform:modules/tenants/tests/integration/services/deletion/tenant-deletion-schedule.integration.spec.js#holds
        the deletion indefinitely when a notice could not be delivered, rather than destroying the tenant
        unannounced
      - >-
        services-platform:modules/tenants/tests/integration/services/deletion/begin-destruction.integration.spec.js#waits
        while a notice destruction waits for has not been sent
      - >-
        services-platform:modules/tenants/tests/integration/services/deletion/begin-destruction.integration.spec.js#names
        the notices it is waiting for
      - >-
        services-platform:modules/tenants/tests/integration/services/persistence/tenant-deletion-requests.integration.spec.js#counts
        a suppressed notice as sent, which is the development and test path
      - >-
        services-platform:modules/tenants/tests/integration/services/persistence/tenant-deletion-requests.integration.spec.js#still
        lists a failed notice, so a send that never worked never lets a tenant be deleted
      - >-
        services-platform:modules/tenants/tests/integration/services/persistence/tenant-deletion-requests.integration.spec.js#ignores
        a seven-day reminder that was never sent: only two kinds gate destruction
    provenance: test
    source: >-
      services-platform:modules/tenants/tests/integration/services/deletion/tenant-deletion-schedule.integration.spec.js#holds
      the deletion indefinitely when a notice could not be delivered, rather than destroying the tenant
      unannounced
    category: failure
    action_evidence:
      provenance: test
      source: >-
        services-platform:modules/tenants/tests/integration/services/deletion/tenant-deletion-schedule.integration.spec.js#holds
        the deletion indefinitely when a notice could not be delivered, rather than destroying the tenant
        unannounced
    assertion_evidence:
      provenance: test
      source: >-
        services-platform:modules/tenants/tests/integration/services/deletion/tenant-deletion-schedule.integration.spec.js#holds
        the deletion indefinitely when a notice could not be delivered, rather than destroying the tenant
        unannounced
    binding_note: >-
      The original test-title references contribute separate assertions from several files. No suite receives
      an implements link for the complete combined scenario; a full executable mapping remains
      FND-PFM-TNL-084.
    binding_gap: FND-PFM-TNL-084
  - id: ATS-PFM-TNL-015-06
    title: Gating delivery controls the irreversible handoff
    category: interaction
    level: api
    proves:
      - BR-CAP-PFM-TNL-015-05
    interacts_with:
      - CAP-PFM-TNL-016
      - CAP-PFM-TNL-013
      - CAP-PFM-TNL-014
    preconditions: An eligible scheduled deletion has a failed final-reminder notice.
    steps: >-
      Run the destruction check, record delivery or policy suppression for the obligation and run the check
      again.
    expected: >-
      The first check keeps the decision reversible; a satisfied gate allows the remaining safeguards to
      decide whether purge may begin.
    negative: true
    data_needs: >-
      Create a separate run-owned workspace for each decision branch and an unaffected comparison workspace.
      Use a controlled clock, paused task or injected fault at the boundary named in the scenario.
    bound_to: []
    provenance: proposed
    source: >-
      rhythm-v2-docs:specs/platform/tenant-lifecycle/planning/capabilities/CAP-PFM-TNL-015-tell-rhythm-staff-about-every-step-of-a-deletion.md;
      rhythm-v2-docs:specs/platform/tenant-lifecycle/planning/capabilities/CAP-PFM-TNL-016-begin-destruction-only-when-every-safeguard-still-holds.md;
      rhythm-v2-docs:specs/platform/tenant-lifecycle/planning/capabilities/CAP-PFM-TNL-013-ask-for-a-workspace-to-be-deleted-after-a-waiting-period.md;
      rhythm-v2-docs:specs/platform/tenant-lifecycle/planning/capabilities/CAP-PFM-TNL-014-hold-release-or-call-off-a-deletion-before-destruction.md
    action_evidence:
      provenance: proposed
      source: >-
        rhythm-v2-docs:specs/platform/tenant-lifecycle/planning/capabilities/CAP-PFM-TNL-015-tell-rhythm-staff-about-every-step-of-a-deletion.md;
        rhythm-v2-docs:specs/platform/tenant-lifecycle/planning/capabilities/CAP-PFM-TNL-016-begin-destruction-only-when-every-safeguard-still-holds.md;
        rhythm-v2-docs:specs/platform/tenant-lifecycle/planning/capabilities/CAP-PFM-TNL-013-ask-for-a-workspace-to-be-deleted-after-a-waiting-period.md;
        rhythm-v2-docs:specs/platform/tenant-lifecycle/planning/capabilities/CAP-PFM-TNL-014-hold-release-or-call-off-a-deletion-before-destruction.md
    assertion_evidence:
      provenance: proposed
      source: >-
        rhythm-v2-docs:specs/platform/tenant-lifecycle/planning/capabilities/CAP-PFM-TNL-015-tell-rhythm-staff-about-every-step-of-a-deletion.md;
        rhythm-v2-docs:specs/platform/tenant-lifecycle/planning/capabilities/CAP-PFM-TNL-016-begin-destruction-only-when-every-safeguard-still-holds.md;
        rhythm-v2-docs:specs/platform/tenant-lifecycle/planning/capabilities/CAP-PFM-TNL-013-ask-for-a-workspace-to-be-deleted-after-a-waiting-period.md;
        rhythm-v2-docs:specs/platform/tenant-lifecycle/planning/capabilities/CAP-PFM-TNL-014-hold-release-or-call-off-a-deletion-before-destruction.md
evidence:
  - >-
    services-platform:modules/tenants/tests/unit/services/deletion/waiting-period.spec.js#owes the notice at
    request, the seven-day reminder and the one-day reminder, in that order
  - >-
    services-platform:modules/tenants/tests/integration/services/deletion/deletion-holds.integration.spec.js#owes
    a notice, because a schedule that changes silently is a schedule nobody can trust
  - >-
    services-platform:modules/tenants/tests/integration/services/deletion/tenant-deletion-schedule.integration.spec.js#tells
    the staff list and the requester, and nobody else
  - >-
    services-platform:modules/tenants/tests/integration/services/deletion/tenant-deletion-schedule.integration.spec.js#sends
    the requested notice on the day of the request
  - >-
    services-platform:modules/tenants/tests/integration/services/deletion/tenant-deletion-schedule.integration.spec.js#holds
    the deletion indefinitely when a notice could not be delivered, rather than destroying the tenant
    unannounced
  - >-
    rhythm-v2-docs:specs/platform/tenant-lifecycle/planning/capabilities/CAP-PFM-TNL-015-tell-rhythm-staff-about-every-step-of-a-deletion.md;
    rhythm-v2-docs:specs/platform/tenant-lifecycle/planning/capabilities/CAP-PFM-TNL-016-begin-destruction-only-when-every-safeguard-still-holds.md;
    rhythm-v2-docs:specs/platform/tenant-lifecycle/planning/capabilities/CAP-PFM-TNL-013-ask-for-a-workspace-to-be-deleted-after-a-waiting-period.md;
    rhythm-v2-docs:specs/platform/tenant-lifecycle/planning/capabilities/CAP-PFM-TNL-014-hold-release-or-call-off-a-deletion-before-destruction.md
coverage:
  rule: 'gap: FND-PFM-TNL-080'
  transition: 'gap: FND-PFM-TNL-080'
  validation: 'gap: FND-PFM-TNL-080'
  permission: covered
  edge: 'gap: FND-PFM-TNL-080'
  failure: covered
  boundary: covered
  concurrency: 'gap: FND-PFM-TNL-080'
  invariant: covered
  interaction: 'gap: FND-PFM-TNL-080'
coverage_notes:
  transition: >-
    The listed scenarios sample lifecycle changes. Complete transition, guard and side-effect proof is still
    owed for the state models named in the coverage finding.
  interaction: >-
    The added interaction is proposed. Related capabilities not included in that scenario remain named in the
    coverage finding.
transition_obligations:
  - state_model: STATE-PFM-TNL-005
    transition_n: 1
    coverage: 'gap: FND-PFM-TNL-080'
    provenance: observed
    source: 'services-platform:modules/tenants/services/deletion/notices/send-notices.ts:sendDueTenantDeletionNotices'
  - state_model: STATE-PFM-TNL-005
    transition_n: 2
    coverage: 'gap: FND-PFM-TNL-080'
    provenance: observed
    source: 'services-platform:modules/tenants/services/deletion/notices/send-notices.ts:sendDueTenantDeletionNotices'
  - state_model: STATE-PFM-TNL-005
    transition_n: 3
    coverage: 'gap: FND-PFM-TNL-080'
    provenance: observed
    source: 'services-platform:modules/tenants/services/deletion/notices/send-notices.ts:sendDueTenantDeletionNotices'
  - state_model: STATE-PFM-TNL-005
    transition_n: 4
    coverage: 'gap: FND-PFM-TNL-080'
    provenance: observed
    source: >-
      services-platform:modules/tenants/services/deletion/tenant-deletion-schedule-operations.ts:tenantDeletionScheduleOperations
  - state_model: STATE-PFM-TNL-005
    transition_n: 5
    coverage: 'gap: FND-PFM-TNL-080'
    provenance: inferred
    source: 'services-platform:modules/tenants/services/deletion/notices/send-notices.ts'
permission_obligations: []
evidence_note: >-
  The test-specific convention lists an Evidence body section, but the corpus checker rejects it. Sources and
  verification limits remain in frontmatter so this artifact conforms without changing the checker.
---
# Keep deletion decisions visible to staff: Capability Test Specification

## Coverage

The category map separates defined scenarios from missing proof. A covered category has a scenario; a binding records inspected test assertions and carries no execution result. State-model completeness and untested interactions remain explicit gaps.

| Rule | Proved by |
|---|---|
| `BR-CAP-PFM-TNL-015-01` | `ATS-PFM-TNL-015-01` |
| `BR-CAP-PFM-TNL-015-02` | `ATS-PFM-TNL-015-02` |
| `BR-CAP-PFM-TNL-015-03` | `ATS-PFM-TNL-015-03` |
| `BR-CAP-PFM-TNL-015-04` | `ATS-PFM-TNL-015-04` |
| `BR-CAP-PFM-TNL-015-05` | `ATS-PFM-TNL-015-05`, `ATS-PFM-TNL-015-06` |
| `BR-CAP-PFM-TNL-015-07` | No scenario: `FND-PFM-TNL-080` |

## Interactions With Related Capabilities

Gating delivery controls the irreversible handoff. The combined scenario sets up the stated boundary, changes the upstream condition and observes its effect on the dependent outcome. Its steps and assertions are proposed; component tests do not establish this combined execution.

| Related capability | Combined coverage |
|---|---|
| `CAP-PFM-TNL-014` | `ATS-PFM-TNL-015-06` (proposed) |
| `CAP-PFM-TNL-016` | `ATS-PFM-TNL-015-06` (proposed) |
| `CAP-PFM-TNL-017` | Gap: `FND-PFM-TNL-080` |
