---
id: ATS-MBR-STR-011
scope: CAP-MBR-STR-011
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-011-01
    title: Dependent types cannot cross programs
    level: api
    proves:
      - BR-CAP-MBR-STR-011-01
      - INV-DOM-MBR-STR-003-02
    preconditions: >-
      An isolated Membership-licensed workspace contains the relevant records, with caller permissions
      selected for the case.
    steps: Create a type under a missing program and try moving a package-referenced type elsewhere.
    expected: The relevant program field is reported and existing classifications remain intact.
    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 type of a program that does not exist 400 naming program_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 moving a type a package names to another program 400 naming program_id, and
        leaves it where it was
      - TST-MBR-STR-095
    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 type of a program that does not exist 400 naming program_id
    category: invariant
  - id: ATS-MBR-STR-011-02
    title: Type overrides preserve zero and no-benefit choices
    level: api
    proves:
      - BR-CAP-MBR-STR-011-02
    preconditions: >-
      An isolated Membership-licensed workspace contains the relevant records, with caller permissions
      selected for the case.
    steps: 'Give Alumni zero grace and no benefits, then evaluate a covering and an ended period.'
    expected: The covering period is active without benefits and the ended period has no inherited grace.
    negative: false
    data_needs: The FAA example records or equivalent records prepared within the isolated workspace.
    bound_to:
      - >-
        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-0097.acceptance.ts#MBR-ACC-0097: A
        type's flow-down settings and benefits flag are configured and felt downstream: a firm's
        Corporate membership reaches its billing contacts, and a type without benefits makes its members
        active without benefits
      - TST-MBR-STR-032
      - TST-MBR-STR-037
    provenance: test
    source: >-
      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
    category: interaction
  - id: ATS-MBR-STR-011-03
    title: Flow-down needs an available target
    level: api
    proves:
      - BR-CAP-MBR-STR-011-03
      - INV-DOM-MBR-STR-003-03
    preconditions: >-
      An isolated Membership-licensed workspace contains the relevant records, with caller permissions
      selected for the case.
    steps: Enable flow-down with no target and with a missing target.
    expected: Both cases fail and identify the target field.
    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 flow-down enabled without a target type 400 naming flowdown_type_id, and writes
        no type
      - >-
        services-membership:tests/e2e/database-refusals.e2e.spec.ts#mistakes only the database catches,
        through the API › DEF-MBR-0264: a flow-down type that does not exist › refuses an unknown
        flowdown_type_id 400 naming the field and the type, and writes no type
      - TST-MBR-STR-095
    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 flow-down enabled without a target type 400 naming flowdown_type_id, and writes
      no type
    category: validation
  - id: ATS-MBR-STR-011-04
    title: Flow-down selections deduplicate at their size limit
    category: boundary
    level: api
    proves:
      - BR-CAP-MBR-STR-011-04
    preconditions: An isolated workspace contains the records and current versions needed for the case.
    steps: >-
      Submit 100 role selections including a duplicate, omit them in a later update, then submit 101
      selections.
    expected: >-
      The accepted list contains each role once; omission retains it and the oversized request is
      refused.
    negative: false
    data_needs: Separate fixture records for the accepted and refused operations.
    bound_to: []
    provenance: proposed
    source:
      - >-
        services-membership:modules/configuration/services/persistence/membership-types.ts:replaceFlowdownLists
  - id: ATS-MBR-STR-011-05
    title: Type retirement separates staff entry from product sale
    category: transition
    level: api
    proves:
      - BR-CAP-MBR-STR-011-06
    preconditions: An isolated workspace contains the records and current versions needed for the case.
    steps: >-
      Retire a type used by an active package product; compare staff term creation with purchase
      preparation.
    expected: Staff term creation refuses the type; purchase preparation still offers the active product.
    negative: false
    data_needs: Separate fixture records for the accepted and refused operations.
    bound_to: []
    provenance: proposed
    source:
      - 'react-console:acceptance/membership/journeys/configuration/MBR-ACC-0113.json'
  - id: ATS-MBR-STR-011-06
    title: Package references prevent moving a classification
    category: interaction
    level: api
    proves:
      - BR-CAP-MBR-STR-011-01
      - BR-CAP-MBR-STR-012-05
    preconditions: An isolated workspace contains the records and current versions needed for the case.
    steps: 'Create a package using a type, then try moving the type to another program.'
    expected: The program change fails and both package and type retain their original program relationship.
    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 moving a type a package names to another program 400 naming program_id, and
        leaves it where it was
      - >-
        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 package of a type of another program 400 naming membership_type_id, and
        writes no package nor unit rule
    exercises:
      - CAP-MBR-STR-011
      - CAP-MBR-STR-012
  - id: ATS-MBR-STR-011-07
    title: Changing benefits changes current interpretation
    category: interaction
    level: api
    proves:
      - BR-CAP-MBR-STR-011-02
      - BR-CAP-MBR-STR-016-04
    preconditions: An isolated workspace contains the records and current versions needed for the case.
    steps: >-
      Evaluate a covering term with benefits, disable benefits on its type and evaluate again from live
      settings.
    expected: The same period becomes active without benefits without changing its dates.
    negative: false
    data_needs: Separate fixture records for the accepted and refused operations.
    bound_to: []
    provenance: proposed
    source:
      - '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
        active without benefits when the covering term’s classification confers none
    exercises:
      - CAP-MBR-STR-011
      - CAP-MBR-STR-016
  - id: ATS-MBR-STR-011-08
    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-011
      - CAP-MBR-STR-019
evidence:
  - 'services-membership:tests/acceptance/configuration/MBR-ACC-0092.acceptance.ts'
  - 'services-membership:tests/acceptance/configuration/MBR-ACC-0097.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 moving a type a package names to another program 400 naming program_id, and leaves
    it where it was
  - '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 flow-down enabled without a target type 400 naming flowdown_type_id, and writes no
    type
  - >-
    services-membership:modules/configuration/services/persistence/membership-types.ts:replaceFlowdownLists
  - 'services-membership:modules/configuration/services/configuration/configuration-records.ts:typeContent'
  - 'react-console:acceptance/membership/journeys/configuration/MBR-ACC-0113.json'
  - >-
    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 type of a program that does not exist 400 naming program_id
  - >-
    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/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 package of a type of another program 400 naming membership_type_id, and writes no
    package nor unit rule
  - >-
    services-membership:modules/terms/tests/unit/status.spec.ts#holding status: the rows of §2 › is
    active without benefits when the covering term’s classification confers none
  - membership.configuration.read
  - membership.configuration.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: 'gap: FND-MBR-STR-064'
  transition: covered
  validation: covered
  permission: covered
  edge: 'gap: FND-MBR-STR-064'
  failure: 'gap: FND-MBR-STR-064'
  boundary: covered
  concurrency: 'gap: FND-MBR-STR-064'
  invariant: covered
  interaction: covered
coverage_notes:
  rule: >-
    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.
unproved_rules: []
coverage_obligations:
  permissions:
    - key: membership.configuration.read
      scenario_ids:
        - ATS-MBR-STR-019-02
      local_scenario_ids:
        - ATS-MBR-STR-011-08
      coverage: partial
      gap: FND-MBR-STR-064
      provenance: unknown
      source: FND-MBR-STR-064
      note: >-
        These cases establish declared authority or representative admission; exhaustive operation and
        caller combinations remain open.
    - key: membership.configuration.manage
      scenario_ids:
        - ATS-MBR-STR-019-02
      local_scenario_ids:
        - ATS-MBR-STR-011-08
      coverage: partial
      gap: FND-MBR-STR-064
      provenance: unknown
      source: FND-MBR-STR-064
      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-011-08
      coverage: partial
      gap: FND-MBR-STR-064
      provenance: unknown
      source: FND-MBR-STR-064
      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-064
          scenario_ids: []
          coverage: gap
          gap: FND-MBR-STR-064
        - from: inactive
          to: active
          trigger: Staff save activity true.
          provenance: unknown
          source: FND-MBR-STR-064
          scenario_ids: []
          coverage: gap
          gap: FND-MBR-STR-064
        - from: active
          to: deleted
          trigger: Staff delete an active record.
          provenance: unknown
          source: FND-MBR-STR-064
          scenario_ids: []
          coverage: gap
          gap: FND-MBR-STR-064
        - from: inactive
          to: deleted
          trigger: Staff delete an inactive record.
          provenance: unknown
          source: FND-MBR-STR-064
          scenario_ids: []
          coverage: gap
          gap: FND-MBR-STR-064
      gap: FND-MBR-STR-064
  related:
    - capability: CAP-MBR-STR-012
      scenario: ATS-MBR-STR-011-06
      provenance: proposed
    - capability: CAP-MBR-STR-016
      scenario: ATS-MBR-STR-011-07
      provenance: proposed
  invariants:
    - id: INV-DOM-MBR-STR-003-01
      scenario_ids: []
      coverage: gap
      gap: FND-MBR-STR-064
      provenance: unknown
      source: FND-MBR-STR-064
    - id: INV-DOM-MBR-STR-003-02
      scenario_ids:
        - ATS-MBR-STR-011-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 a type of a program that does not exist 400 naming program_id
    - id: INV-DOM-MBR-STR-003-03
      scenario_ids:
        - ATS-MBR-STR-011-03
      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 flow-down enabled without a target type 400 naming flowdown_type_id,
          and writes no type
    - id: INV-DOM-MBR-STR-003-04
      scenario_ids: []
      coverage: gap
      gap: FND-MBR-STR-064
      provenance: unknown
      source: FND-MBR-STR-064
    - id: INV-DOM-MBR-STR-003-05
      scenario_ids: []
      coverage: gap
      gap: FND-MBR-STR-064
      provenance: unknown
      source: FND-MBR-STR-064
    - id: INV-DOM-MBR-STR-003-06
      scenario_ids: []
      coverage: gap
      gap: FND-MBR-STR-064
      provenance: unknown
      source: FND-MBR-STR-064
    - id: INV-DOM-MBR-STR-003-07
      scenario_ids: []
      coverage: gap
      gap: FND-MBR-STR-064
      provenance: unknown
      source: FND-MBR-STR-064
  workflows:
    - id: WF-MBR-STR-005
      scenarios:
        - ATS-MBR-STR-011-02
        - ATS-MBR-STR-011-06
        - ATS-MBR-STR-011-07
      unproved_failure_and_concurrency: FND-MBR-STR-064
      provenance: unknown
      source: FND-MBR-STR-064
  workflow_interactions:
    - workflow: WF-MBR-STR-005
      capability: CAP-MBR-STR-018
      scenario_ids:
        - ATS-MBR-STR-018-11
      coverage: specified
      gap: null
      provenance: documented
      source:
        - ATS-MBR-STR-018-11
      note: Cross-capability examples are cited from their owning specifications.
    - workflow: WF-MBR-STR-005
      capability: CAP-MBR-STR-009
      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-010
      scenario_ids: []
      coverage: gap
      gap: FND-MBR-STR-064
      provenance: unknown
      source: FND-MBR-STR-064
      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-014
      scenario_ids: []
      coverage: gap
      gap: FND-MBR-STR-064
      provenance: unknown
      source: FND-MBR-STR-064
      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-064
      provenance: unknown
      source: FND-MBR-STR-064
      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-011-01
    scenarios:
      - ATS-MBR-STR-011-01
      - ATS-MBR-STR-011-06
    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 type of a program that does not exist 400 naming program_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 moving a type a package names to another program 400 naming program_id, and
        leaves it where it was
      - >-
        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 package of a type of another program 400 naming membership_type_id, and
        writes no package nor unit rule
  - rule: BR-CAP-MBR-STR-011-02
    scenarios:
      - ATS-MBR-STR-011-02
      - ATS-MBR-STR-011-07
    gap: null
    provenance: documented
    source:
      - >-
        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:modules/configuration/db/membership-types.table.ts:membershipTypes'
      - >-
        services-membership:modules/terms/tests/unit/status.spec.ts#holding status: the rows of §2 › is
        active without benefits when the covering term’s classification confers none
  - rule: BR-CAP-MBR-STR-011-03
    scenarios:
      - ATS-MBR-STR-011-03
    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 flow-down enabled without a target type 400 naming flowdown_type_id, and writes
        no type
  - rule: BR-CAP-MBR-STR-011-04
    scenarios:
      - ATS-MBR-STR-011-04
    gap: null
    provenance: documented
    source:
      - >-
        services-membership:modules/configuration/services/persistence/membership-types.ts:replaceFlowdownLists
  - rule: BR-CAP-MBR-STR-011-06
    scenarios:
      - ATS-MBR-STR-011-05
    gap: null
    provenance: documented
    source:
      - 'react-console:acceptance/membership/journeys/configuration/MBR-ACC-0113.json'
narrative_sources:
  - section: Coverage
    provenance: documented
    source:
      - CAP-MBR-STR-011
      - FND-MBR-STR-064
  - section: Interactions With Related Capabilities
    provenance: proposed
    source:
      - ATS-MBR-STR-011-02
      - ATS-MBR-STR-011-06
      - ATS-MBR-STR-011-07
---

# Classify memberships and configure organization flow-down: 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-011-01` | `ATS-MBR-STR-011-01`, `ATS-MBR-STR-011-06` |
| `BR-CAP-MBR-STR-011-02` | `ATS-MBR-STR-011-02`, `ATS-MBR-STR-011-07` |
| `BR-CAP-MBR-STR-011-03` | `ATS-MBR-STR-011-03` |
| `BR-CAP-MBR-STR-011-04` | `ATS-MBR-STR-011-04` |
| `BR-CAP-MBR-STR-011-06` | `ATS-MBR-STR-011-05` |

## Interactions With Related Capabilities

Package references prevent moving a classification. Create a package using a type, then try moving the type to another program.

Changing benefits changes current interpretation. Evaluate a covering term with benefits, disable benefits on its type and evaluate again from live settings.
