---
id: ATS-MBR-MEM-020
scope: CAP-MBR-MEM-020
product_area: membership
module: memberships
revision: 2
status: proposed
approved_by: null
approved_at: null
source_sha:
  services-membership: 5994c270e8a37a30f848e99ba5b5ee6b87c08893
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
scenarios:
  - id: ATS-MBR-MEM-020-01
    title: 'Each operation states exactly its events, and a rollback states none'
    level: api
    proves:
      - BR-CAP-MBR-MEM-020-01
      - INV-DOM-MBR-MEM-006-07
    preconditions: >-
      The well-known FAA workspace fixtures, or a workspace the test builds from
      them.
    steps:
      - do: >-
          Run each staff, affiliation, structure and sweep operation; roll one
          back; state an event outside a transaction.
        expect: >-
          Exactly the expected events with their contents; none for the
          rollback; the last refused.
        provenance: test
        source:
          - >-
            services-membership:modules/events/tests/integration/business-events.integration.spec.ts#states
            membership.term.committed and the term’s affiliation for a staff
            creation, naming the person, the program and the term
          - >-
            services-membership:modules/events/tests/integration/business-events.integration.spec.ts#states
            the old unit’s end and the new unit’s start for a transfer, each
            naming the other
          - >-
            services-membership:modules/events/tests/integration/business-events.integration.spec.ts#states
            membership.unit.reparented for a moved unit, with the old and the
            new parent
          - >-
            services-membership:modules/events/tests/integration/business-events.integration.spec.ts#leaves
            no event when the operation that stated it rolls back
          - >-
            services-membership:modules/events/tests/integration/business-events.integration.spec.ts#refuses
            to state an event outside a transaction
    expected: >-
      Exactly the expected events with their contents; none for the rollback;
      the last refused.
    negative: false
    data_needs: 'FAA programs, packages, units and members from the integration seed.'
    bound_to:
      - TST-MBR-MEM-014
    provenance: test
    source: >-
      services-membership:modules/events/tests/integration/business-events.integration.spec.ts#states
      membership.term.committed and the term’s affiliation for a staff creation,
      naming the person, the program and the term
    category: invariant
    binding_evidence:
      - suite: TST-MBR-MEM-014
        source: >-
          services-membership:modules/events/tests/integration/business-events.integration.spec.ts#states
          membership.term.committed and the term’s affiliation for a staff
          creation, naming the person, the program and the term
      - suite: TST-MBR-MEM-014
        source: >-
          services-membership:modules/events/tests/integration/business-events.integration.spec.ts#states
          the old unit’s end and the new unit’s start for a transfer, each
          naming the other
      - suite: TST-MBR-MEM-014
        source: >-
          services-membership:modules/events/tests/integration/business-events.integration.spec.ts#states
          membership.unit.reparented for a moved unit, with the old and the new
          parent
      - suite: TST-MBR-MEM-014
        source: >-
          services-membership:modules/events/tests/integration/business-events.integration.spec.ts#leaves
          no event when the operation that stated it rolls back
      - suite: TST-MBR-MEM-014
        source: >-
          services-membership:modules/events/tests/integration/business-events.integration.spec.ts#refuses
          to state an event outside a transaction
  - id: ATS-MBR-MEM-020-02
    title: Events name the membership's holder correctly
    level: unit
    proves:
      - BR-CAP-MBR-MEM-020-02
    preconditions: >-
      The well-known FAA workspace fixtures, or a workspace the test builds from
      them.
    steps:
      - do: Build events for a person's and an organization's membership.
        expect: >-
          The person by contact; the organization by organization with no
          contact.
        provenance: test
        source:
          - >-
            services-membership:modules/memberships/tests/unit/membership-business-events.spec.ts#names
            a person’s membership by its contact_id and no organization
          - >-
            services-membership:modules/memberships/tests/unit/membership-business-events.spec.ts#names
            an organization’s membership by its organization and no contact_id,
            whoever its primary contact is
          - >-
            services-membership:modules/events/tests/integration/business-events.integration.spec.ts#names
            no contact_id when the terminated membership is an organization’s
    expected: The person by contact; the organization by organization with no contact.
    negative: false
    data_needs: 'FAA programs, packages, units and members from the integration seed.'
    bound_to:
      - TST-MBR-MEM-035
      - TST-MBR-MEM-014
    provenance: test
    source: >-
      services-membership:modules/memberships/tests/unit/membership-business-events.spec.ts#names
      a person’s membership by its contact_id and no organization
    category: rule
    binding_evidence:
      - suite: TST-MBR-MEM-035
        source: >-
          services-membership:modules/memberships/tests/unit/membership-business-events.spec.ts#names
          a person’s membership by its contact_id and no organization
      - suite: TST-MBR-MEM-035
        source: >-
          services-membership:modules/memberships/tests/unit/membership-business-events.spec.ts#names
          an organization’s membership by its organization and no contact_id,
          whoever its primary contact is
      - suite: TST-MBR-MEM-014
        source: >-
          services-membership:modules/events/tests/integration/business-events.integration.spec.ts#names
          no contact_id when the terminated membership is an organization’s
  - id: ATS-MBR-MEM-020-03
    title: Terminations and lapses carry the last day of rights
    level: api
    proves:
      - BR-CAP-MBR-MEM-020-03
    preconditions: >-
      The well-known FAA workspace fixtures, or a workspace the test builds from
      them.
    steps:
      - do: Terminate a term and let the sweep find another lapsed.
        expect: >-
          Each is one termination with its last day, the first day without and
          the reason; the lapse ends on the term's own last day.
        provenance: test
        source:
          - >-
            services-membership:modules/events/tests/integration/business-events.integration.spec.ts#states
            one membership.term.terminated for a staff termination, with the
            person’s contact_id and the last day of rights
          - >-
            services-membership:modules/events/tests/integration/business-events.integration.spec.ts#states
            membership.term.terminated once when the midnight sweep finds a term
            lapsed, with the term’s last day
          - >-
            services-membership:modules/memberships/tests/unit/membership-business-events.spec.ts#gives
            a termination its last day of rights and the first day without, and
            its reason
    expected: >-
      Each is one termination with its last day, the first day without and the
      reason; the lapse ends on the term's own last day.
    negative: false
    data_needs: 'FAA programs, packages, units and members from the integration seed.'
    bound_to:
      - TST-MBR-MEM-014
      - TST-MBR-MEM-035
    provenance: test
    source: >-
      services-membership:modules/events/tests/integration/business-events.integration.spec.ts#states
      one membership.term.terminated for a staff termination, with the person’s
      contact_id and the last day of rights
    category: boundary
    binding_evidence:
      - suite: TST-MBR-MEM-014
        source: >-
          services-membership:modules/events/tests/integration/business-events.integration.spec.ts#states
          one membership.term.terminated for a staff termination, with the
          person’s contact_id and the last day of rights
      - suite: TST-MBR-MEM-014
        source: >-
          services-membership:modules/events/tests/integration/business-events.integration.spec.ts#states
          membership.term.terminated once when the midnight sweep finds a term
          lapsed, with the term’s last day
      - suite: TST-MBR-MEM-035
        source: >-
          services-membership:modules/memberships/tests/unit/membership-business-events.spec.ts#gives
          a termination its last day of rights and the first day without, and
          its reason
  - id: ATS-MBR-MEM-020-04
    title: A cancellation says whether the membership went with it
    level: api
    proves:
      - BR-CAP-MBR-MEM-020-04
    preconditions: >-
      The well-known FAA workspace fixtures, or a workspace the test builds from
      them.
    steps:
      - do: Cancel a pending first purchase and a pending renewal.
        expect: The first says the membership was removed; the second does not.
        provenance: proposed
        source: >-
          services-membership:modules/memberships/tests/unit/membership-business-events.spec.ts#says
          whether a cancellation removed the membership
    expected: The first says the membership was removed; the second does not.
    negative: false
    data_needs: 'FAA programs, packages, units and members from the integration seed.'
    bound_to: []
    provenance: proposed
    source: >-
      services-membership:modules/memberships/tests/unit/membership-business-events.spec.ts#says
      whether a cancellation removed the membership
    category: transition
    binding_gap: FND-MBR-MEM-103
  - id: ATS-MBR-MEM-020-05
    title: Event contents are strict
    level: unit
    proves:
      - BR-CAP-MBR-MEM-020-05
      - INV-DOM-MBR-MEM-006-08
    preconditions: >-
      The well-known FAA workspace fixtures, or a workspace the test builds from
      them.
    steps:
      - do: >-
          Validate a termination without its last day, an undeclared field, a
          non-local date and an overlong id.
        expect: Each is refused.
        provenance: test
        source:
          - >-
            services-membership:modules/events/tests/unit/business-event-schemas.spec.ts#refuses
            a termination that names no last day
          - >-
            services-membership:modules/events/tests/unit/business-event-schemas.spec.ts#refuses
            a field the event does not declare
          - >-
            services-membership:modules/events/tests/unit/business-event-schemas.spec.ts#refuses
            a date that is not a local date
          - >-
            services-membership:modules/events/tests/unit/business-event-schemas.spec.ts#refuses
            an id longer than an id can be
    expected: Each is refused.
    negative: true
    data_needs: 'FAA programs, packages, units and members from the integration seed.'
    bound_to:
      - TST-MBR-MEM-016
    provenance: test
    source: >-
      services-membership:modules/events/tests/unit/business-event-schemas.spec.ts#refuses
      a termination that names no last day
    category: validation
    binding_evidence:
      - suite: TST-MBR-MEM-016
        source: >-
          services-membership:modules/events/tests/unit/business-event-schemas.spec.ts#refuses
          a termination that names no last day
      - suite: TST-MBR-MEM-016
        source: >-
          services-membership:modules/events/tests/unit/business-event-schemas.spec.ts#refuses
          a field the event does not declare
      - suite: TST-MBR-MEM-016
        source: >-
          services-membership:modules/events/tests/unit/business-event-schemas.spec.ts#refuses
          a date that is not a local date
      - suite: TST-MBR-MEM-016
        source: >-
          services-membership:modules/events/tests/unit/business-event-schemas.spec.ts#refuses
          an id longer than an id can be
  - id: ATS-MBR-MEM-020-06
    title: 'Committees receives terminations alone, each with a stable identifier'
    level: api
    proves:
      - BR-CAP-MBR-MEM-020-06
    preconditions: >-
      The well-known FAA workspace fixtures, or a workspace the test builds from
      them.
    steps:
      - do: >-
          Dispatch a batch of events to Committees' fake receiver, then
          redeliver.
        expect: >-
          Only the termination is handed over, as the remote contract, with the
          event's own id; the others settle without a call.
        provenance: test
        source:
          - >-
            services-membership:modules/events/tests/integration/change-events-dispatch.integration.spec.ts#hands
            Committees the termination alone, as { version: 1, stream, events }
            with data.contact_id and data.ends_on
          - >-
            services-membership:modules/events/tests/integration/change-events-dispatch.integration.spec.ts#gives
            Committees the business event’s own id, so a redelivery names the
            same event
          - >-
            services-membership:modules/events/tests/integration/change-events-dispatch.integration.spec.ts#answers
            the events Committees does not act on without calling it
          - >-
            services-membership:tests/e2e/business-events.e2e.spec.ts#hands
            Committees the termination alone, in the remote contract, and
            settles every message
    expected: >-
      Only the termination is handed over, as the remote contract, with the
      event's own id; the others settle without a call.
    negative: false
    data_needs: 'FAA programs, packages, units and members from the integration seed.'
    bound_to:
      - TST-MBR-MEM-015
      - TST-MBR-MEM-239
    provenance: test
    source: >-
      services-membership:modules/events/tests/integration/change-events-dispatch.integration.spec.ts#hands
      Committees the termination alone, as { version: 1, stream, events } with
      data.contact_id and data.ends_on
    category: interaction
    binding_evidence:
      - suite: TST-MBR-MEM-015
        source: >-
          services-membership:modules/events/tests/integration/change-events-dispatch.integration.spec.ts#hands
          Committees the termination alone, as { version: 1, stream, events }
          with data.contact_id and data.ends_on
      - suite: TST-MBR-MEM-015
        source: >-
          services-membership:modules/events/tests/integration/change-events-dispatch.integration.spec.ts#gives
          Committees the business event’s own id, so a redelivery names the same
          event
      - suite: TST-MBR-MEM-015
        source: >-
          services-membership:modules/events/tests/integration/change-events-dispatch.integration.spec.ts#answers
          the events Committees does not act on without calling it
      - suite: TST-MBR-MEM-239
        source: >-
          services-membership:tests/e2e/business-events.e2e.spec.ts#hands
          Committees the termination alone, in the remote contract, and settles
          every message
  - id: ATS-MBR-MEM-020-07
    title: 'A failed delivery is retried, then set aside'
    level: api
    proves:
      - BR-CAP-MBR-MEM-020-07
    preconditions: >-
      The well-known FAA workspace fixtures, or a workspace the test builds from
      them.
    steps:
      - do: >-
          Committees answers failed, then succeeds; on another message it fails
          on the last attempt.
        expect: >-
          The failed attempt is reported for retry and the successful retry is
          acknowledged. A failure at the final receive remains failed for the
          queue’s configured redrive policy; the suite does not observe a
          deployed dead-letter transfer.
        provenance: test
        source:
          - >-
            services-membership:modules/events/tests/integration/change-events-dispatch.integration.spec.ts#returns
            the message to the queue when Committees answers failed, so SQS
            retries it
          - >-
            services-membership:modules/events/tests/integration/change-events-dispatch.integration.spec.ts#acknowledges
            the retried message once Committees takes it
          - >-
            services-membership:modules/events/tests/integration/change-events-dispatch.integration.spec.ts#reports
            a failure on the queue’s last receive too, which moves the message
            to the dead-letter queue
    expected: >-
      The failed attempt is reported for retry and the successful retry is
      acknowledged. A failure at the final receive remains failed for the
      queue’s configured redrive policy; the suite does not observe a deployed
      dead-letter transfer.
    negative: true
    data_needs: 'FAA programs, packages, units and members from the integration seed.'
    bound_to:
      - TST-MBR-MEM-015
    provenance: test
    source: >-
      services-membership:modules/events/tests/integration/change-events-dispatch.integration.spec.ts#returns
      the message to the queue when Committees answers failed, so SQS retries it
    category: failure
    binding_evidence:
      - suite: TST-MBR-MEM-015
        source: >-
          services-membership:modules/events/tests/integration/change-events-dispatch.integration.spec.ts#returns
          the message to the queue when Committees answers failed, so SQS
          retries it
      - suite: TST-MBR-MEM-015
        source: >-
          services-membership:modules/events/tests/integration/change-events-dispatch.integration.spec.ts#acknowledges
          the retried message once Committees takes it
      - suite: TST-MBR-MEM-015
        source: >-
          services-membership:modules/events/tests/integration/change-events-dispatch.integration.spec.ts#reports
          a failure on the queue’s last receive too, which moves the message to
          the dead-letter queue
  - id: ATS-MBR-MEM-020-08
    title: Publish the last covered day of staff termination
    category: interaction
    level: api
    proves:
      - BR-CAP-MBR-MEM-003-06
      - BR-CAP-MBR-MEM-020-03
    interacts_with:
      - CAP-MBR-MEM-003
    workflows:
      - WF-MBR-MEM-004
    relies_on:
      - ATS-MBR-MEM-003-04
      - ATS-MBR-MEM-020-03
    preconditions: A person holds a started period.
    steps:
      - do: >-
          Terminate it from a selected day and inspect the committed termination
          event; repeat the operation in a transaction that rolls back.
        expect: >-
          The committed event identifies the shortened last day; the rolled-back
          attempt publishes no event.
        provenance: proposed
        source: &ref_0
          - >-
            services-membership:modules/memberships/tests/integration/services/staff-operations.integration.spec.ts#terminates
            a term from the effective date, with the reason, and the status
            follows
          - >-
            services-membership:modules/events/tests/integration/business-events.integration.spec.ts#states
            one membership.term.terminated for a staff termination, with the
            person’s contact_id and the last day of rights
    expected: >-
      The committed event identifies the shortened last day; the rolled-back
      attempt publishes no event.
    negative: false
    data_needs: >-
      An isolated workspace with the specified membership, package, chapter and
      partner-port fixtures.
    bound_to: []
    provenance: proposed
    source: *ref_0
  - id: ATS-MBR-MEM-020-09
    title: State suspension and restoration alongside their writes
    category: interaction
    level: api
    proves:
      - BR-CAP-MBR-MEM-004-02
      - BR-CAP-MBR-MEM-004-03
      - BR-CAP-MBR-MEM-020-01
    interacts_with:
      - CAP-MBR-MEM-004
    workflows:
      - WF-MBR-MEM-005
    relies_on:
      - ATS-MBR-MEM-004-02
      - ATS-MBR-MEM-020-01
    preconditions: A principal holding is unsuspended.
    steps:
      - do: >-
          Suspend and restore it and inspect committed events; separately roll
          back a suspension.
        expect: >-
          The committed changes have their corresponding events; the rolled-back
          suspension has none.
        provenance: proposed
        source: &ref_1
          - >-
            services-membership:tests/acceptance/memberships/MBR-ACC-0174.acceptance.ts#MBR-ACC-0174:
            A membership is suspended over its terms and restored to whatever
            the terms give, once each way, with no detections while suspended
            and the history showing the suspension
          - >-
            services-membership:modules/events/tests/integration/business-events.integration.spec.ts#states
            membership.term.committed and the term’s affiliation for a staff
            creation, naming the person, the program and the term
    expected: >-
      The committed changes have their corresponding events; the rolled-back
      suspension has none.
    negative: false
    data_needs: >-
      An isolated workspace with the specified membership, package, chapter and
      partner-port fixtures.
    bound_to: []
    provenance: proposed
    source: *ref_1
  - id: ATS-MBR-MEM-020-10
    title: Publish one lapse event through repeated sweeps
    category: interaction
    level: api
    proves:
      - BR-CAP-MBR-MEM-005-05
      - BR-CAP-MBR-MEM-020-03
    interacts_with:
      - CAP-MBR-MEM-005
    workflows:
      - WF-MBR-MEM-008
    relies_on:
      - ATS-MBR-MEM-005-05
      - ATS-MBR-MEM-020-03
    preconditions: A term has passed its final grace day without renewed coverage.
    steps:
      - do: >-
          Run the status sweep after the lapse boundary, then repeat the same
          sweep.
        expect: >-
          The saved lapse detection and termination event retain the original
          last covered day and are not duplicated.
        provenance: proposed
        source: &ref_2
          - >-
            services-membership:modules/memberships/tests/integration/services/status-sweep.integration.spec.ts#records
            lapsed_detected effective the day after grace ended, even when found
            late
          - >-
            services-membership:modules/events/tests/integration/business-events.integration.spec.ts#states
            one membership.term.terminated for a staff termination, with the
            person’s contact_id and the last day of rights
    expected: >-
      The saved lapse detection and termination event retain the original last
      covered day and are not duplicated.
    negative: false
    data_needs: >-
      An isolated workspace with the specified membership, package, chapter and
      partner-port fixtures.
    bound_to: []
    provenance: proposed
    source: *ref_2
  - id: ATS-MBR-MEM-020-11
    title: State commitment for a package grant with its source
    category: interaction
    level: api
    proves:
      - BR-CAP-MBR-MEM-006-01
      - BR-CAP-MBR-MEM-020-01
    interacts_with:
      - CAP-MBR-MEM-006
    workflows:
      - WF-MBR-MEM-004
    relies_on:
      - ATS-MBR-MEM-006-01
      - ATS-MBR-MEM-020-01
    preconditions: A new person selects a granting package.
    steps:
      - do: >-
          Create the source period and inspect the committed events for both
          source and granted periods.
        expect: >-
          Both committed periods have their corresponding commitment events; a
          rolled-back save retains neither event.
        provenance: proposed
        source: &ref_3
          - >-
            services-membership:modules/memberships/tests/integration/services/staff-operations.integration.spec.ts#creates
            the granted membership and term of a package grant in the same
            transaction
          - >-
            services-membership:modules/events/tests/integration/business-events.integration.spec.ts#states
            membership.term.committed and the term’s affiliation for a staff
            creation, naming the person, the program and the term
    expected: >-
      Both committed periods have their corresponding commitment events; a
      rolled-back save retains neither event.
    negative: false
    data_needs: >-
      An isolated workspace with the specified membership, package, chapter and
      partner-port fixtures.
    bound_to: []
    provenance: proposed
    source: *ref_3
  - id: ATS-MBR-MEM-020-12
    title: Publish an affiliation end with its saved last day
    category: interaction
    level: api
    proves:
      - BR-CAP-MBR-MEM-007-08
      - BR-CAP-MBR-MEM-020-01
    interacts_with:
      - CAP-MBR-MEM-007
    workflows: []
    relies_on:
      - ATS-MBR-MEM-007-08
      - ATS-MBR-MEM-020-01
    preconditions: >-
      A permitted chapter row is active and may be ended without violating the
      minimum.
    steps:
      - do: >-
          End the row and inspect its saved last day and committed affiliation
          event.
        expect: >-
          The event identifies the row’s saved last covered day; rollback
          removes both change and event.
        provenance: proposed
        source: &ref_4
          - >-
            services-membership:modules/memberships/tests/integration/services/affiliation-withdrawal-continued.integration.spec.ts#refuses
            to withdraw the continued row, naming the affiliation that continues
            it, and keeps the row
          - >-
            services-membership:modules/events/tests/integration/business-events.integration.spec.ts#states
            membership.term.committed and the term’s affiliation for a staff
            creation, naming the person, the program and the term
    expected: >-
      The event identifies the row’s saved last covered day; rollback removes
      both change and event.
    negative: false
    data_needs: >-
      An isolated workspace with the specified membership, package, chapter and
      partner-port fixtures.
    bound_to: []
    provenance: proposed
    source: *ref_4
  - id: ATS-MBR-MEM-020-13
    title: Publish both sides of a permitted transfer
    category: interaction
    level: api
    proves:
      - BR-CAP-MBR-MEM-008-01
      - BR-CAP-MBR-MEM-020-01
    interacts_with:
      - CAP-MBR-MEM-008
    workflows: []
    relies_on:
      - ATS-MBR-MEM-008-01
      - ATS-MBR-MEM-020-01
    preconditions: A source chapter and a no-dues target permit direct transfer.
    steps:
      - do: >-
          Transfer the affiliation and inspect the committed started and ended
          events.
        expect: >-
          The two events identify their opposite units and the effective
          transfer boundary.
        provenance: proposed
        source: &ref_5
          - >-
            services-membership:modules/memberships/tests/integration/services/affiliations.integration.spec.ts#transfers
            to a chapter that sells no dues: the old row ends the day before,
            the new one continues it as primary
          - >-
            services-membership:modules/events/tests/integration/business-events.integration.spec.ts#states
            membership.term.committed and the term’s affiliation for a staff
            creation, naming the person, the program and the term
    expected: >-
      The two events identify their opposite units and the effective transfer
      boundary.
    negative: false
    data_needs: >-
      An isolated workspace with the specified membership, package, chapter and
      partner-port fixtures.
    bound_to: []
    provenance: proposed
    source: *ref_5
  - id: ATS-MBR-MEM-020-14
    title: Publish purchase commitment once per authorization
    category: interaction
    level: api
    proves:
      - BR-CAP-MBR-MEM-009-12
      - BR-CAP-MBR-MEM-020-04
    interacts_with:
      - CAP-MBR-MEM-009
    workflows:
      - WF-MBR-MEM-012
    relies_on:
      - ATS-MBR-MEM-009-12
      - ATS-MBR-MEM-020-04
    preconditions: A package purchase is reserved and awaiting fulfilment.
    steps:
      - do: >-
          Fulfil the authorization, redeliver it, and inspect committed period
          events.
        expect: >-
          The same committed records are returned and only one commitment event
          is stored.
        provenance: proposed
        source: &ref_6
          - >-
            services-membership:modules/commerce-participant/tests/integration/participant.integration.spec.ts#fulfils:
            committed with its authorization, numbered, its chapter written with
            the dues product, enrolled
          - >-
            services-membership:modules/memberships/tests/unit/membership-business-events.spec.ts#says
            whether a cancellation removed the membership
    expected: >-
      The same committed records are returned and only one commitment event is
      stored.
    negative: false
    data_needs: >-
      An isolated workspace with the specified membership, package, chapter and
      partner-port fixtures.
    bound_to: []
    provenance: proposed
    source: *ref_6
  - id: ATS-MBR-MEM-020-15
    title: Describe first-purchase removal in its cancellation event
    category: interaction
    level: api
    proves:
      - BR-CAP-MBR-MEM-010-07
      - BR-CAP-MBR-MEM-020-04
    interacts_with:
      - CAP-MBR-MEM-010
    workflows:
      - WF-MBR-MEM-014
    relies_on:
      - ATS-MBR-MEM-010-07
      - ATS-MBR-MEM-020-04
    preconditions: A first purchase has a pending term and no other coverage.
    steps:
      - do: >-
          Cancel its obligation and inspect the committed cancellation event
          before and after redelivery.
        expect: >-
          The event records that the holding is removed and redelivery creates
          no second event.
        provenance: proposed
        source: &ref_7
          - >-
            services-membership:modules/commerce-participant/tests/integration/participant.integration.spec.ts#releases
            a voided order: the pending term and the membership the claim
            created are deleted
          - >-
            services-membership:modules/memberships/tests/unit/membership-business-events.spec.ts#says
            whether a cancellation removed the membership
    expected: >-
      The event records that the holding is removed and redelivery creates no
      second event.
    negative: true
    data_needs: >-
      An isolated workspace with the specified membership, package, chapter and
      partner-port fixtures.
    bound_to: []
    provenance: proposed
    source: *ref_7
  - id: ATS-MBR-MEM-020-16
    title: State billing terminations with their last covered day
    category: interaction
    level: api
    proves:
      - BR-CAP-MBR-MEM-013-08
      - BR-CAP-MBR-MEM-013-09
      - BR-CAP-MBR-MEM-020-03
    interacts_with:
      - CAP-MBR-MEM-013
    workflows:
      - WF-MBR-MEM-004
    relies_on:
      - ATS-MBR-MEM-013-08
      - ATS-MBR-MEM-020-03
    preconditions: Lapsed candidates have planned drop actions.
    steps:
      - do: >-
          Execute the billing run, repeat execution, and inspect each
          candidate’s termination event.
        expect: >-
          Each completed drop has one event retaining the period’s last day;
          settled actions do not duplicate events.
        provenance: proposed
        source: &ref_8
          - >-
            services-membership:modules/billing/tests/integration/billing-runs.integration.spec.ts#executes
            on the jobs engine: one order per renewing candidate (Jo’s with both
            dues chapters), a notice and reminder each, one drop per lapsed term
          - >-
            services-membership:modules/events/tests/integration/business-events.integration.spec.ts#states
            one membership.term.terminated for a staff termination, with the
            person’s contact_id and the last day of rights
    expected: >-
      Each completed drop has one event retaining the period’s last day; settled
      actions do not duplicate events.
    negative: false
    data_needs: >-
      An isolated workspace with the specified membership, package, chapter and
      partner-port fixtures.
    bound_to: []
    provenance: proposed
    source: *ref_8
  - id: ATS-MBR-MEM-020-17
    title: Abort both event and renewal when enabled resolution fails
    category: interaction
    level: api
    proves:
      - BR-CAP-MBR-MEM-014-07
      - BR-CAP-MBR-MEM-020-01
    interacts_with:
      - CAP-MBR-MEM-014
    workflows:
      - WF-MBR-MEM-004
      - WF-MBR-MEM-012
      - WF-MBR-MEM-014
    relies_on:
      - ATS-MBR-MEM-014-07
      - ATS-MBR-MEM-020-01
    preconditions: >-
      An expiring member has an item, and enabled attention resolution rejects
      the renewal.
    steps:
      - do: >-
          Attempt renewal, then inspect saved periods, lifecycle and published
          business events.
        expect: >-
          The refused transaction leaves no renewal period, renewal lifecycle
          entry or commitment event.
        provenance: proposed
        source: &ref_9
          - >-
            services-membership:modules/memberships/tests/integration/services/expiring-memberships.integration.spec.ts#resolves
            the item renewed in the staff renewal’s transaction
          - >-
            services-membership:modules/events/tests/integration/business-events.integration.spec.ts#states
            membership.term.committed and the term’s affiliation for a staff
            creation, naming the person, the program and the term
    expected: >-
      The refused transaction leaves no renewal period, renewal lifecycle entry
      or commitment event.
    negative: true
    data_needs: >-
      An isolated workspace with the specified membership, package, chapter and
      partner-port fixtures.
    bound_to: []
    provenance: proposed
    source: *ref_9
evidence:
  - >-
    services-membership at 5994c270e8a37a30f848e99ba5b5ee6b87c08893: the
    capability sources and the bound test files.
  - >-
    rhythm-v2-docs:specs/membership/memberships/design/: domain objects, state
    models and workflows reviewed for coverage.
coverage:
  rule: 'gap: FND-MBR-MEM-102'
  transition: 'gap: FND-MBR-MEM-102'
  validation: covered
  permission: 'gap: FND-MBR-MEM-102'
  edge: 'gap: FND-MBR-MEM-102'
  failure: covered
  boundary: covered
  concurrency: 'gap: FND-MBR-MEM-102'
  invariant: 'gap: FND-MBR-MEM-102'
  interaction: covered
coverage_notes:
  transition: >-
    The scenarios do not enumerate every applicable object transition; the
    finding preserves the remaining transition matrix.
  permission: >-
    The finding records named permissions without a complete positive and
    negative proof.
  invariant: >-
    Existing invariant examples do not cover every applicable domain-object
    constraint.
  interaction: >-
    Every related capability and each cross-capability workflow has an explicit
    composite scenario. Unbound composites remain proposed.
---

# Tell other applications when membership changes: Capability Test Specification

## Coverage

| Rule | Scenario or Finding |
|---|---|
| `BR-CAP-MBR-MEM-020-01` | `ATS-MBR-MEM-020-01`, `ATS-MBR-MEM-020-09`, `ATS-MBR-MEM-020-11`, `ATS-MBR-MEM-020-12`, `ATS-MBR-MEM-020-13`, `ATS-MBR-MEM-020-17` |
| `BR-CAP-MBR-MEM-020-02` | `ATS-MBR-MEM-020-02` |
| `BR-CAP-MBR-MEM-020-03` | `ATS-MBR-MEM-020-03`, `ATS-MBR-MEM-020-08`, `ATS-MBR-MEM-020-10`, `ATS-MBR-MEM-020-16` |
| `BR-CAP-MBR-MEM-020-04` | `ATS-MBR-MEM-020-04`, `ATS-MBR-MEM-020-14`, `ATS-MBR-MEM-020-15` |
| `BR-CAP-MBR-MEM-020-05` | `ATS-MBR-MEM-020-05` |
| `BR-CAP-MBR-MEM-020-06` | `ATS-MBR-MEM-020-06` |
| `BR-CAP-MBR-MEM-020-07` | `ATS-MBR-MEM-020-07` |
| `BR-CAP-MBR-MEM-020-10` | no scenario: `FND-MBR-MEM-064` |

## Interactions With Related Capabilities

The interaction cases test how a shared holding, period, chapter connection or recorded change affects related work. Each case names its related capabilities and workflows in frontmatter. A composite case remains proposed until executable assertions establish every part of its expected outcome.
