---
id: ATS-MBR-STR-009
scope: CAP-MBR-STR-009
product_area: membership
module: structure
revision: 2
status: proposed
approved_by: null
approved_at: null
source_sha:
  services-membership: 5994c270e8a37a30f848e99ba5b5ee6b87c08893
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
scenarios:
  - id: ATS-MBR-STR-009-01
    title: Identity-bound programs need a category
    level: api
    proves:
      - BR-CAP-MBR-STR-009-01
      - INV-DOM-MBR-STR-003-01
    preconditions: >-
      An isolated Membership-licensed workspace contains the relevant records, with caller permissions
      selected for the case.
    steps: Attempt identity-bound program creation without its identifying unit category.
    expected: The request fails with the identifying-category field reported.
    negative: true
    data_needs: The FAA example records or equivalent records prepared within the isolated workspace.
    bound_to:
      - >-
        services-membership:tests/e2e/database-refusals.e2e.spec.ts#mistakes only the database catches,
        through the API › DEF-MBR-0086: a configuration write a check, a foreign key or an integer column
        refuses › refuses an identity-bound program without its identity unit type 400 naming
        identity_unit_type_id
      - >-
        services-membership:tests/acceptance/configuration/MBR-ACC-0120.acceptance.ts#MBR-ACC-0120:
        Scenario 11 through the route: an identity-bound Chapter Membership program names its chapter
        unit type and its owning unit, refuses the wrong structure references in plain words and the
        missing identity type raw, and Kim holds a National and an Atlanta membership side by side
      - TST-MBR-STR-095
      - TST-MBR-STR-057
    provenance: test
    source: >-
      services-membership:tests/e2e/database-refusals.e2e.spec.ts#mistakes only the database catches,
      through the API › DEF-MBR-0086: a configuration write a check, a foreign key or an integer column
      refuses › refuses an identity-bound program without its identity unit type 400 naming
      identity_unit_type_id
    category: validation
  - id: ATS-MBR-STR-009-02
    title: Program structural references are verified together
    level: api
    proves:
      - BR-CAP-MBR-STR-009-02
    preconditions: >-
      An isolated Membership-licensed workspace contains the relevant records, with caller permissions
      selected for the case.
    steps: Submit an absent identity category and an absent owning unit.
    expected: One refusal names both references and no program is created.
    negative: true
    data_needs: The FAA example records or equivalent records prepared within the isolated workspace.
    bound_to:
      - >-
        services-membership:tests/acceptance/configuration/MBR-ACC-0120.acceptance.ts#MBR-ACC-0120:
        Scenario 11 through the route: an identity-bound Chapter Membership program names its chapter
        unit type and its owning unit, refuses the wrong structure references in plain words and the
        missing identity type raw, and Kim holds a National and an Atlanta membership side by side
      - TST-MBR-STR-057
    provenance: test
    source: >-
      services-membership:tests/acceptance/configuration/MBR-ACC-0120.acceptance.ts#MBR-ACC-0120:
      Scenario 11 through the route: an identity-bound Chapter Membership program names its chapter unit
      type and its owning unit, refuses the wrong structure references in plain words and the missing
      identity type raw, and Kim holds a National and an Atlanta membership side by side
    category: validation
  - id: ATS-MBR-STR-009-03
    title: Changed grace settings are read live
    level: api
    proves:
      - BR-CAP-MBR-STR-009-05
    preconditions: >-
      An isolated Membership-licensed workspace contains the relevant records, with caller permissions
      selected for the case.
    steps: >-
      Attempt an oversized grace allowance, then change a valid allowance and evaluate an existing
      member.
    expected: The excessive value fails and the existing holding uses the new valid grace.
    negative: true
    data_needs: The FAA example records or equivalent records prepared within the isolated workspace.
    bound_to:
      - >-
        services-membership:tests/e2e/database-refusals.e2e.spec.ts#mistakes only the database catches,
        through the API › DEF-MBR-0086: a configuration write a check, a foreign key or an integer column
        refuses › refuses a grace period of 2^31 days 400 naming grace_days, beyond what the column
        stores
      - >-
        services-membership:tests/acceptance/configuration/MBR-ACC-0092.acceptance.ts#MBR-ACC-0092:
        Changing a grace period moves every existing member at once: a type override wins over the
        program, zero is not blank, and even an as-of answer for a past date follows today's setting
      - >-
        services-membership:tests/acceptance/configuration/MBR-ACC-0119.acceptance.ts#MBR-ACC-0119:
        Scenario 4 in configuration terms: Sam's grace, lapse, drop and rejoin follow the program's
        60-day grace and 60-day continuity gap and the package's 180-day reinstatement, and widening the
        continuity gap afterwards merges his two episodes into one
      - TST-MBR-STR-095
      - TST-MBR-STR-032
      - TST-MBR-STR-056
    provenance: test
    source: >-
      services-membership:tests/e2e/database-refusals.e2e.spec.ts#mistakes only the database catches,
      through the API › DEF-MBR-0086: a configuration write a check, a foreign key or an integer column
      refuses › refuses a grace period of 2^31 days 400 naming grace_days, beyond what the column stores
    category: interaction
  - id: ATS-MBR-STR-009-04
    title: Exclusivity changes affect later enrolments
    category: interaction
    level: api
    proves:
      - BR-CAP-MBR-STR-009-06
    preconditions: An isolated workspace contains the records and current versions needed for the case.
    steps: >-
      Establish two overlapping holdings, put their programs in one exclusivity group and attempt another
      overlapping enrolment; then clear the group and retry.
    expected: Earlier holdings stay intact; the grouped attempt fails and the cleared-group attempt succeeds.
    negative: false
    data_needs: Separate fixture records for the accepted and refused operations.
    bound_to: []
    provenance: proposed
    source:
      - >-
        services-membership:tests/acceptance/configuration/MBR-ACC-0093.acceptance.ts#MBR-ACC-0093:
        Putting two programs into one exclusivity group refuses new overlapping memberships from then on
        while members who already hold both keep them, and removing the group opens them again
  - id: ATS-MBR-STR-009-05
    title: Program order follows references rather than saved positions
    category: rule
    level: api
    proves:
      - BR-CAP-MBR-STR-009-11
    preconditions: An isolated workspace contains the records and current versions needed for the case.
    steps: Create two programs with opposite reference and display-position ordering and page their catalogue.
    expected: The answer follows reference order while each record retains its saved position.
    negative: false
    data_needs: Separate fixture records for the accepted and refused operations.
    bound_to: []
    provenance: proposed
    source:
      - >-
        services-membership:modules/configuration/services/persistence/configuration-store.ts:configurationStore
  - id: ATS-MBR-STR-009-06
    title: An absent program profile delegates resolution
    category: boundary
    level: api
    proves:
      - BR-CAP-MBR-STR-009-12
    preconditions: An isolated workspace contains the records and current versions needed for the case.
    steps: >-
      Read a program with an explicit ordering profile, then clear the profile and prepare the next sales
      context with application and workspace defaults.
    expected: >-
      The explicit choice is used first; the cleared choice allows the configured fallback without
      copying checkout fields into the program.
    negative: false
    data_needs: Separate fixture records for the accepted and refused operations.
    bound_to: []
    provenance: proposed
    source:
      - >-
        services-membership:docs/migrations/packages-as-products/README.md#Ordering profiles (added the
        same day, from Commerce's decision Andrew approved at about 15:45 UTC)
  - id: ATS-MBR-STR-009-07
    title: Program binding controls accepted requirement lists
    category: interaction
    level: api
    proves:
      - BR-CAP-MBR-STR-009-01
      - BR-CAP-MBR-STR-010-02
    preconditions: An isolated workspace contains the records and current versions needed for the case.
    steps: >-
      Change an unused program from affiliation binding to no binding, attempt a populated requirement
      replacement and then an empty replacement.
    expected: The populated list fails and the empty list saves at the current program version.
    negative: false
    data_needs: Separate fixture records for the accepted and refused operations.
    bound_to: []
    provenance: proposed
    source:
      - >-
        services-membership:tests/e2e/database-refusals.e2e.spec.ts#mistakes only the database catches,
        through the API › DEF-MBR-0086: a configuration write a check, a foreign key or an integer column
        refuses › refuses an identity-bound program without its identity unit type 400 naming
        identity_unit_type_id
      - >-
        services-membership:modules/configuration/services/configuration/configuration-records.ts:replaceProgramUnitTypesOf
    exercises:
      - CAP-MBR-STR-009
      - CAP-MBR-STR-010
  - id: ATS-MBR-STR-009-08
    title: A zero type override changes live grace interpretation
    category: interaction
    level: api
    proves:
      - BR-CAP-MBR-STR-009-05
      - BR-CAP-MBR-STR-011-02
    preconditions: An isolated workspace contains the records and current versions needed for the case.
    steps: >-
      Set program grace to 60 and type grace to zero, evaluate an ended holding, then clear the type
      override and evaluate again.
    expected: The first answer is lapsed and the second uses program grace.
    negative: false
    data_needs: Separate fixture records for the accepted and refused operations.
    bound_to: []
    provenance: proposed
    source:
      - >-
        services-membership:tests/e2e/database-refusals.e2e.spec.ts#mistakes only the database catches,
        through the API › DEF-MBR-0086: a configuration write a check, a foreign key or an integer column
        refuses › refuses a grace period of 2^31 days 400 naming grace_days, beyond what the column
        stores
      - 'services-membership:modules/configuration/db/membership-types.table.ts:membershipTypes'
    exercises:
      - CAP-MBR-STR-009
      - CAP-MBR-STR-011
  - id: ATS-MBR-STR-009-09
    title: Changing program grace changes an existing holding answer
    category: interaction
    level: api
    proves:
      - BR-CAP-MBR-STR-009-05
      - BR-CAP-MBR-STR-016-03
    preconditions: An isolated workspace contains the records and current versions needed for the case.
    steps: >-
      Evaluate an ended holding with 60 grace days, narrow the live program allowance and evaluate the
      same date again.
    expected: The retained term is unchanged while its standing changes from grace to lapsed.
    negative: false
    data_needs: Separate fixture records for the accepted and refused operations.
    bound_to: []
    provenance: proposed
    source:
      - >-
        services-membership:tests/e2e/database-refusals.e2e.spec.ts#mistakes only the database catches,
        through the API › DEF-MBR-0086: a configuration write a check, a foreign key or an integer column
        refuses › refuses a grace period of 2^31 days 400 naming grace_days, beyond what the column
        stores
      - >-
        services-membership:modules/terms/tests/unit/status.spec.ts#holding status: the rows of §2 › is
        in grace on the last day of grace (D = E + grace_days)
    exercises:
      - CAP-MBR-STR-009
      - CAP-MBR-STR-016
  - id: ATS-MBR-STR-009-10
    title: Workspace authority gates this capability
    category: permission
    level: api
    proves:
      - BR-CAP-MBR-STR-019-02
    preconditions: An isolated workspace contains the records and current versions needed for the case.
    steps: >-
      Exercise the capability’s exposed operations with its read-only and management callers, then repeat
      with application access removed, an unlicensed workspace and an unavailable workspace.
    expected: >-
      Each named operation requires its declared authority; admission failures occur before any record or
      calculated result is exposed.
    negative: true
    data_needs: Separate fixture records for the accepted and refused operations.
    bound_to: []
    provenance: proposed
    source:
      - >-
        services-membership:tests/unit/route-authorization.spec.ts#route authorization: every Membership
        operation names its permission › requires membership.structure.read of every structure read and
        membership.structure.manage of every change
      - >-
        services-membership:modules/configuration/aws/api/configuration.routes.ts:configurationModuleRoutes
      - >-
        services-membership:tests/e2e/terms-api.e2e.spec.ts#the terms API, end to end in process ›
        refuses a caller who holds no key in the tenant
    exercises:
      - CAP-MBR-STR-009
      - CAP-MBR-STR-019
evidence:
  - 'services-membership:tests/acceptance/configuration/MBR-ACC-0092.acceptance.ts'
  - 'services-membership:tests/acceptance/configuration/MBR-ACC-0119.acceptance.ts'
  - 'services-membership:tests/acceptance/configuration/MBR-ACC-0120.acceptance.ts'
  - 'services-membership:tests/e2e/database-refusals.e2e.spec.ts'
  - >-
    services-membership:tests/e2e/database-refusals.e2e.spec.ts#mistakes only the database catches,
    through the API › DEF-MBR-0086: a configuration write a check, a foreign key or an integer column
    refuses › refuses an identity-bound program without its identity unit type 400 naming
    identity_unit_type_id
  - >-
    services-membership:modules/configuration/services/configuration/configuration-records.ts:programResource
  - 'services-membership:modules/configuration/db/programs.table.ts:programs'
  - 'services-membership:modules/configuration/db/configuration-vocabularies.ts:NUMBER_SCOPES'
  - >-
    services-membership:tests/e2e/database-refusals.e2e.spec.ts#mistakes only the database catches,
    through the API › DEF-MBR-0086: a configuration write a check, a foreign key or an integer column
    refuses › refuses a grace period of 2^31 days 400 naming grace_days, beyond what the column stores
  - >-
    services-membership:tests/acceptance/configuration/MBR-ACC-0093.acceptance.ts#MBR-ACC-0093: Putting
    two programs into one exclusivity group refuses new overlapping memberships from then on while
    members who already hold both keep them, and removing the group opens them again
  - >-
    services-membership:tests/acceptance/configuration/MBR-ACC-0121.acceptance.ts#MBR-ACC-0121: The
    settings Commerce reads flow into the purchase plan and never into an amount: the program's
    same-order switch, ordering profile, cancellation policy and fee product, and the package's
    reinstatement audience, enrolment proration and auto-renew default each appear where the participant
    promises them, and a change is reflected on the next prepare
  - >-
    services-membership:modules/configuration/services/configuration/configuration-records.ts:programContent
  - 'react-console:acceptance/membership/inventory/business-rules.json#RUL-MBR-CFG-028'
  - >-
    services-membership:modules/configuration/services/persistence/configuration-store.ts:configurationStore
  - >-
    services-membership:docs/migrations/packages-as-products/README.md#Ordering profiles (added the same
    day, from Commerce's decision Andrew approved at about 15:45 UTC)
  - >-
    services-membership:tests/acceptance/configuration/MBR-ACC-0120.acceptance.ts#MBR-ACC-0120: Scenario
    11 through the route: an identity-bound Chapter Membership program names its chapter unit type and
    its owning unit, refuses the wrong structure references in plain words and the missing identity type
    raw, and Kim holds a National and an Atlanta membership side by side
  - >-
    services-membership:modules/configuration/services/configuration/configuration-records.ts:replaceProgramUnitTypesOf
  - 'services-membership:modules/configuration/db/membership-types.table.ts:membershipTypes'
  - >-
    services-membership:modules/terms/tests/unit/status.spec.ts#holding status: the rows of §2 › is in
    grace on the last day of grace (D = E + grace_days)
  - membership.programs.read
  - membership.programs.manage
  - membership.application.access
  - >-
    services-membership:tests/unit/route-authorization.spec.ts#route authorization: every Membership
    operation names its permission › requires membership.structure.read of every structure read and
    membership.structure.manage of every change
  - 'services-membership:modules/configuration/aws/api/configuration.routes.ts:configurationModuleRoutes'
  - >-
    services-membership:tests/e2e/terms-api.e2e.spec.ts#the terms API, end to end in process › refuses a
    caller who holds no key in the tenant
coverage:
  rule: covered
  transition: 'gap: FND-MBR-STR-062'
  validation: covered
  permission: covered
  edge: 'gap: FND-MBR-STR-062'
  failure: 'gap: FND-MBR-STR-062'
  boundary: covered
  concurrency: 'gap: FND-MBR-STR-062'
  invariant: 'gap: FND-MBR-STR-062'
  interaction: covered
coverage_notes:
  transition: >-
    No dedicated scenario establishes this category across the capability’s declared operations and
    design obligations.
  edge: >-
    No dedicated scenario establishes this category across the capability’s declared operations and
    design obligations.
  failure: >-
    No dedicated scenario establishes this category across the capability’s declared operations and
    design obligations.
  concurrency: >-
    No dedicated scenario establishes this category across the capability’s declared operations and
    design obligations.
  invariant: >-
    No dedicated scenario establishes this category across the capability’s declared operations and
    design obligations.
unproved_rules: []
coverage_obligations:
  permissions:
    - key: membership.programs.read
      scenario_ids:
        - ATS-MBR-STR-019-02
      local_scenario_ids:
        - ATS-MBR-STR-009-10
      coverage: partial
      gap: FND-MBR-STR-062
      provenance: unknown
      source: FND-MBR-STR-062
      note: >-
        These cases establish declared authority or representative admission; exhaustive operation and
        caller combinations remain open.
    - key: membership.programs.manage
      scenario_ids:
        - ATS-MBR-STR-019-02
      local_scenario_ids:
        - ATS-MBR-STR-009-10
      coverage: partial
      gap: FND-MBR-STR-062
      provenance: unknown
      source: FND-MBR-STR-062
      note: >-
        These cases establish declared authority or representative admission; exhaustive operation and
        caller combinations remain open.
    - key: membership.application.access
      scenario_ids:
        - ATS-MBR-STR-019-04
      local_scenario_ids:
        - ATS-MBR-STR-009-10
      coverage: partial
      gap: FND-MBR-STR-062
      provenance: unknown
      source: FND-MBR-STR-062
      note: >-
        These cases establish declared authority or representative admission; exhaustive operation and
        caller combinations remain open.
  state_models:
    - id: STATE-MBR-STR-006
      transitions:
        - from: active
          to: inactive
          trigger: Staff save activity false.
          provenance: unknown
          source: FND-MBR-STR-062
          scenario_ids: []
          coverage: gap
          gap: FND-MBR-STR-062
        - from: inactive
          to: active
          trigger: Staff save activity true.
          provenance: unknown
          source: FND-MBR-STR-062
          scenario_ids: []
          coverage: gap
          gap: FND-MBR-STR-062
        - from: active
          to: deleted
          trigger: Staff delete an active record.
          provenance: unknown
          source: FND-MBR-STR-062
          scenario_ids: []
          coverage: gap
          gap: FND-MBR-STR-062
        - from: inactive
          to: deleted
          trigger: Staff delete an inactive record.
          provenance: unknown
          source: FND-MBR-STR-062
          scenario_ids: []
          coverage: gap
          gap: FND-MBR-STR-062
      gap: FND-MBR-STR-062
  related:
    - capability: CAP-MBR-STR-010
      scenario: ATS-MBR-STR-009-07
      provenance: proposed
    - capability: CAP-MBR-STR-011
      scenario: ATS-MBR-STR-009-08
      provenance: proposed
    - capability: CAP-MBR-STR-016
      scenario: ATS-MBR-STR-009-09
      provenance: proposed
  invariants:
    - id: INV-DOM-MBR-STR-003-01
      scenario_ids:
        - ATS-MBR-STR-009-01
      coverage: specified
      gap: null
      provenance: documented
      source:
        - >-
          services-membership:tests/e2e/database-refusals.e2e.spec.ts#mistakes only the database catches,
          through the API › DEF-MBR-0086: a configuration write a check, a foreign key or an integer
          column refuses › refuses an identity-bound program without its identity unit type 400 naming
          identity_unit_type_id
    - id: INV-DOM-MBR-STR-003-02
      scenario_ids: []
      coverage: gap
      gap: FND-MBR-STR-062
      provenance: unknown
      source: FND-MBR-STR-062
    - id: INV-DOM-MBR-STR-003-03
      scenario_ids: []
      coverage: gap
      gap: FND-MBR-STR-062
      provenance: unknown
      source: FND-MBR-STR-062
    - id: INV-DOM-MBR-STR-003-04
      scenario_ids: []
      coverage: gap
      gap: FND-MBR-STR-062
      provenance: unknown
      source: FND-MBR-STR-062
    - id: INV-DOM-MBR-STR-003-05
      scenario_ids: []
      coverage: gap
      gap: FND-MBR-STR-062
      provenance: unknown
      source: FND-MBR-STR-062
    - id: INV-DOM-MBR-STR-003-06
      scenario_ids: []
      coverage: gap
      gap: FND-MBR-STR-062
      provenance: unknown
      source: FND-MBR-STR-062
    - id: INV-DOM-MBR-STR-003-07
      scenario_ids: []
      coverage: gap
      gap: FND-MBR-STR-062
      provenance: unknown
      source: FND-MBR-STR-062
  workflows:
    - id: WF-MBR-STR-005
      scenarios:
        - ATS-MBR-STR-009-03
        - ATS-MBR-STR-009-04
        - ATS-MBR-STR-009-07
        - ATS-MBR-STR-009-08
        - ATS-MBR-STR-009-09
      unproved_failure_and_concurrency: FND-MBR-STR-062
      provenance: unknown
      source: FND-MBR-STR-062
  workflow_interactions:
    - workflow: WF-MBR-STR-005
      capability: CAP-MBR-STR-018
      scenario_ids:
        - ATS-MBR-STR-018-10
      coverage: specified
      gap: null
      provenance: documented
      source:
        - ATS-MBR-STR-018-10
      note: Cross-capability examples are cited from their owning specifications.
    - workflow: WF-MBR-STR-005
      capability: CAP-MBR-STR-010
      scenario_ids:
        - ATS-MBR-STR-009-07
      coverage: specified
      gap: null
      provenance: documented
      source:
        - ATS-MBR-STR-009-07
      note: Cross-capability examples are cited from their owning specifications.
    - workflow: WF-MBR-STR-005
      capability: CAP-MBR-STR-011
      scenario_ids:
        - ATS-MBR-STR-009-08
      coverage: specified
      gap: null
      provenance: documented
      source:
        - ATS-MBR-STR-009-08
      note: Cross-capability examples are cited from their owning specifications.
    - workflow: WF-MBR-STR-005
      capability: CAP-MBR-STR-014
      scenario_ids: []
      coverage: gap
      gap: FND-MBR-STR-062
      provenance: unknown
      source: FND-MBR-STR-062
      note: >-
        The shared workflow names both capabilities, but a concrete interaction oracle is not established
        for this pairing.
    - workflow: WF-MBR-STR-005
      capability: CAP-MBR-STR-017
      scenario_ids: []
      coverage: gap
      gap: FND-MBR-STR-062
      provenance: unknown
      source: FND-MBR-STR-062
      note: >-
        The shared workflow names both capabilities, but a concrete interaction oracle is not established
        for this pairing.
rule_coverage:
  - rule: BR-CAP-MBR-STR-009-01
    scenarios:
      - ATS-MBR-STR-009-01
      - ATS-MBR-STR-009-07
    gap: null
    provenance: documented
    source:
      - >-
        services-membership:tests/e2e/database-refusals.e2e.spec.ts#mistakes only the database catches,
        through the API › DEF-MBR-0086: a configuration write a check, a foreign key or an integer column
        refuses › refuses an identity-bound program without its identity unit type 400 naming
        identity_unit_type_id
      - >-
        services-membership:tests/e2e/database-refusals.e2e.spec.ts#mistakes only the database catches,
        through the API › DEF-MBR-0086: a configuration write a check, a foreign key or an integer column
        refuses › refuses an identity-bound program without its identity unit type 400 naming
        identity_unit_type_id
      - >-
        services-membership:modules/configuration/services/configuration/configuration-records.ts:replaceProgramUnitTypesOf
  - rule: BR-CAP-MBR-STR-009-02
    scenarios:
      - ATS-MBR-STR-009-02
    gap: null
    provenance: documented
    source:
      - >-
        services-membership:tests/acceptance/configuration/MBR-ACC-0120.acceptance.ts#MBR-ACC-0120:
        Scenario 11 through the route: an identity-bound Chapter Membership program names its chapter
        unit type and its owning unit, refuses the wrong structure references in plain words and the
        missing identity type raw, and Kim holds a National and an Atlanta membership side by side
  - rule: BR-CAP-MBR-STR-009-05
    scenarios:
      - ATS-MBR-STR-009-03
      - ATS-MBR-STR-009-08
      - ATS-MBR-STR-009-09
    gap: null
    provenance: documented
    source:
      - >-
        services-membership:tests/e2e/database-refusals.e2e.spec.ts#mistakes only the database catches,
        through the API › DEF-MBR-0086: a configuration write a check, a foreign key or an integer column
        refuses › refuses a grace period of 2^31 days 400 naming grace_days, beyond what the column
        stores
      - >-
        services-membership:tests/e2e/database-refusals.e2e.spec.ts#mistakes only the database catches,
        through the API › DEF-MBR-0086: a configuration write a check, a foreign key or an integer column
        refuses › refuses a grace period of 2^31 days 400 naming grace_days, beyond what the column
        stores
      - 'services-membership:modules/configuration/db/membership-types.table.ts:membershipTypes'
      - >-
        services-membership:tests/e2e/database-refusals.e2e.spec.ts#mistakes only the database catches,
        through the API › DEF-MBR-0086: a configuration write a check, a foreign key or an integer column
        refuses › refuses a grace period of 2^31 days 400 naming grace_days, beyond what the column
        stores
      - >-
        services-membership:modules/terms/tests/unit/status.spec.ts#holding status: the rows of §2 › is
        in grace on the last day of grace (D = E + grace_days)
  - rule: BR-CAP-MBR-STR-009-06
    scenarios:
      - ATS-MBR-STR-009-04
    gap: null
    provenance: documented
    source:
      - >-
        services-membership:tests/acceptance/configuration/MBR-ACC-0093.acceptance.ts#MBR-ACC-0093:
        Putting two programs into one exclusivity group refuses new overlapping memberships from then on
        while members who already hold both keep them, and removing the group opens them again
  - rule: BR-CAP-MBR-STR-009-11
    scenarios:
      - ATS-MBR-STR-009-05
    gap: null
    provenance: documented
    source:
      - >-
        services-membership:modules/configuration/services/persistence/configuration-store.ts:configurationStore
  - rule: BR-CAP-MBR-STR-009-12
    scenarios:
      - ATS-MBR-STR-009-06
    gap: null
    provenance: documented
    source:
      - >-
        services-membership:docs/migrations/packages-as-products/README.md#Ordering profiles (added the
        same day, from Commerce's decision Andrew approved at about 15:45 UTC)
narrative_sources:
  - section: Coverage
    provenance: documented
    source:
      - CAP-MBR-STR-009
      - FND-MBR-STR-062
  - section: Interactions With Related Capabilities
    provenance: proposed
    source:
      - ATS-MBR-STR-009-03
      - ATS-MBR-STR-009-04
      - ATS-MBR-STR-009-07
      - ATS-MBR-STR-009-08
      - ATS-MBR-STR-009-09
---

# Establish programs and their membership context: Capability Test Specification

## Coverage

The coverage map distinguishes specified examples from missing proof. A covered category means an expected case is specified; executable bindings and their run results remain separate evidence. Each required rule has a proof link or an explicit coverage finding in the structured rule ledger.

| Required Rule | Scenario or Finding |
|---|---|
| `BR-CAP-MBR-STR-009-01` | `ATS-MBR-STR-009-01`, `ATS-MBR-STR-009-07` |
| `BR-CAP-MBR-STR-009-02` | `ATS-MBR-STR-009-02` |
| `BR-CAP-MBR-STR-009-05` | `ATS-MBR-STR-009-03`, `ATS-MBR-STR-009-08`, `ATS-MBR-STR-009-09` |
| `BR-CAP-MBR-STR-009-06` | `ATS-MBR-STR-009-04` |
| `BR-CAP-MBR-STR-009-11` | `ATS-MBR-STR-009-05` |
| `BR-CAP-MBR-STR-009-12` | `ATS-MBR-STR-009-06` |

## Interactions With Related Capabilities

Program binding controls accepted requirement lists. Change an unused program from affiliation binding to no binding, attempt a populated requirement replacement and then an empty replacement.

A zero type override changes live grace interpretation. Set program grace to 60 and type grace to zero, evaluate an ended holding, then clear the type override and evaluate again.

Changing program grace changes an existing holding answer. Evaluate an ended holding with 60 grace days, narrow the live program allowance and evaluate the same date again.
