---
id: ATS-COM-CAT-003
scope: CAP-COM-CAT-003
product_area: commerce
module: catalog
revision: 2
status: proposed
source_sha:
  services-commerce: c8b66a98e2490617aec38ba5c8165dd9bd0a8f9b
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
scenarios:
  - id: ATS-COM-CAT-003-01
    title: Store creation requires an existing policy
    level: api
    proves:
      - BR-CAP-COM-CAT-003-01
      - INV-DOM-COM-CAT-002-01
    preconditions: Creation requests have no valid policy selected
    steps: Submit an omitted policy and an unknown policy
    expected: Both requests fail with the policy field identified
    negative: true
    data_needs: none
    bound_to:
      - TST-COM-CAT-031
    provenance: test
    source: >-
      services-commerce:modules/configuration/tests/e2e/stores.e2e.spec.js#answers 400 to a store
      without an ordering profile
    category: validation
    binding_evidence:
      - >-
        services-commerce:modules/configuration/tests/e2e/stores.e2e.spec.js#answers 400 to a store
        without an ordering profile
      - >-
        services-commerce:modules/configuration/tests/e2e/stores.e2e.spec.js#answers 400 to an
        ordering profile the tenant does not have, naming the property
    step_evidence:
      - do: Submit an omitted policy and an unknown policy
        provenance: test
        source: >-
          services-commerce:modules/configuration/tests/e2e/stores.e2e.spec.js#answers 400 to a
          store without an ordering profile
    assertions:
      - expect: Both requests fail with the policy field identified
        provenance: test
        source: >-
          services-commerce:modules/configuration/tests/e2e/stores.e2e.spec.js#answers 400 to a
          store without an ordering profile
  - id: ATS-COM-CAT-003-02
    title: Store currency cannot be supplied independently
    level: api
    proves:
      - BR-CAP-COM-CAT-003-02
      - INV-DOM-COM-CAT-002-02
    preconditions: A store creation request is otherwise valid
    steps: Add a currency field to the request
    expected: The unsupported field is rejected
    negative: true
    data_needs: none
    bound_to:
      - TST-COM-CAT-031
    provenance: test
    source: >-
      services-commerce:modules/configuration/tests/e2e/stores.e2e.spec.js#answers 400 to a currency
      or a business unit on the body: the store carries neither since D23
    category: validation
    binding_evidence:
      - >-
        services-commerce:modules/configuration/tests/e2e/stores.e2e.spec.js#answers 400 to a
        currency or a business unit on the body: the store carries neither since D23
    step_evidence:
      - do: Add a currency field to the request
        provenance: test
        source: >-
          services-commerce:modules/configuration/tests/e2e/stores.e2e.spec.js#answers 400 to a
          currency or a business unit on the body: the store carries neither since D23
    assertions:
      - expect: The unsupported field is rejected
        provenance: test
        source: >-
          services-commerce:modules/configuration/tests/e2e/stores.e2e.spec.js#answers 400 to a
          currency or a business unit on the body: the store carries neither since D23
  - id: ATS-COM-CAT-003-03
    title: Assortment and home-category references remain valid
    level: api
    proves:
      - BR-CAP-COM-CAT-003-04
      - BR-CAP-COM-CAT-003-05
      - INV-DOM-COM-CAT-002-04
    preconditions: Books belongs to this workspace and Toys to another
    steps: 'Reference Toys, choose Apparel under only Books, and choose Books under an exclusion of Books'
    expected: Each invalid selection fails on its relevant field
    negative: true
    data_needs: Two workspaces
    bound_to:
      - TST-COM-CAT-037
    provenance: test
    source: >-
      services-commerce:modules/configuration/tests/integration/services/persistence/stores.integration.spec.js#refuses
      another tenant’s category as unknown
    category: boundary
    binding_evidence:
      - >-
        services-commerce:modules/configuration/tests/integration/services/persistence/stores.integration.spec.js#refuses
        another tenant’s category as unknown
      - >-
        services-commerce:modules/configuration/tests/integration/services/persistence/stores.integration.spec.js#refuses
        a home category outside an "only" rule’s categories, 400 naming home_category_id
      - >-
        services-commerce:modules/configuration/tests/integration/services/persistence/stores.integration.spec.js#refuses
        a home category an "all_but" rule leaves out, 400 naming home_category_id
    step_evidence:
      - do: >-
          Reference Toys, choose Apparel under only Books, and choose Books under an exclusion of
          Books
        provenance: test
        source: >-
          services-commerce:modules/configuration/tests/integration/services/persistence/stores.integration.spec.js#refuses
          another tenant’s category as unknown
    assertions:
      - expect: Each invalid selection fails on its relevant field
        provenance: test
        source: >-
          services-commerce:modules/configuration/tests/integration/services/persistence/stores.integration.spec.js#refuses
          another tenant’s category as unknown
  - id: ATS-COM-CAT-003-04
    title: Checkout references prevent store deletion
    level: api
    proves:
      - BR-CAP-COM-CAT-003-08
      - INV-DOM-COM-CAT-002-05
    preconditions: A checkout references Bookstore
    steps: Request removal of Bookstore
    expected: The refusal identifies the retained store
    negative: true
    data_needs: A checkout
    bound_to:
      - TST-COM-CAT-037
    provenance: test
    source: >-
      services-commerce:modules/configuration/tests/integration/services/persistence/stores.integration.spec.js#refuses
      deleting a store a checkout was opened in, naming the store
    category: transition
    binding_evidence:
      - >-
        services-commerce:modules/configuration/tests/integration/services/persistence/stores.integration.spec.js#refuses
        deleting a store a checkout was opened in, naming the store
    step_evidence:
      - do: Request removal of Bookstore
        provenance: test
        source: >-
          services-commerce:modules/configuration/tests/integration/services/persistence/stores.integration.spec.js#refuses
          deleting a store a checkout was opened in, naming the store
    assertions:
      - expect: The refusal identifies the retained store
        provenance: test
        source: >-
          services-commerce:modules/configuration/tests/integration/services/persistence/stores.integration.spec.js#refuses
          deleting a store a checkout was opened in, naming the store
  - id: ATS-COM-CAT-003-05
    title: Stale store replacement fails
    level: api
    proves:
      - BR-CAP-COM-CAT-003-11
      - INV-DOM-COM-CAT-002-07
    preconditions: The stored version is two
    steps: Submit a replacement based on version one
    expected: The version conflict prevents replacement
    negative: true
    data_needs: none
    bound_to:
      - TST-COM-CAT-031
    provenance: test
    source: >-
      services-commerce:modules/configuration/tests/e2e/stores.e2e.spec.js#replaces a store at the
      version read, the purchasing roles with it, and answers 409 to the stale version after
    category: concurrency
    binding_evidence:
      - >-
        services-commerce:modules/configuration/tests/e2e/stores.e2e.spec.js#replaces a store at the
        version read, the purchasing roles with it, and answers 409 to the stale version after
    step_evidence:
      - do: Submit a replacement based on version one
        provenance: test
        source: >-
          services-commerce:modules/configuration/tests/e2e/stores.e2e.spec.js#replaces a store at
          the version read, the purchasing roles with it, and answers 409 to the stale version after
    assertions:
      - expect: The version conflict prevents replacement
        provenance: test
        source: >-
          services-commerce:modules/configuration/tests/e2e/stores.e2e.spec.js#replaces a store at
          the version read, the purchasing roles with it, and answers 409 to the stale version after
  - id: ATS-COM-CAT-003-06
    title: Store writes require administrator authority
    level: api
    proves:
      - BR-CAP-COM-CAT-003-12
    preconditions: A caller holds only store read permission
    steps: 'Attempt creating, replacing and deleting a store'
    expected: All writes fail permission checks
    negative: true
    data_needs: Read-exclusively persona
    bound_to:
      - TST-COM-CAT-028
    provenance: test
    source: >-
      services-commerce:modules/configuration/tests/e2e/configuration-security.e2e.spec.js#is
      refused the creates, 403 permission_missing
    category: permission
    binding_evidence:
      - >-
        services-commerce:modules/configuration/tests/e2e/configuration-security.e2e.spec.js#is
        refused the creates, 403 permission_missing
      - >-
        services-commerce:modules/configuration/tests/e2e/configuration-security.e2e.spec.js#is
        refused the deletes, 403 permission_missing, the records still there
    step_evidence:
      - do: 'Attempt creating, replacing and deleting a store'
        provenance: test
        source: >-
          services-commerce:modules/configuration/tests/e2e/configuration-security.e2e.spec.js#is
          refused the creates, 403 permission_missing
    assertions:
      - expect: All writes fail permission checks
        provenance: test
        source: >-
          services-commerce:modules/configuration/tests/e2e/configuration-security.e2e.spec.js#is
          refused the creates, 403 permission_missing
  - id: ATS-COM-CAT-003-07
    title: Store view reflects a changed sort
    level: browser
    proves:
      - AS-JNY-COM-CAT-001-03
    preconditions: Commerce screens use mocked records
    steps: 'Create a store, open its view, change the default sort and save'
    expected: The view displays the saved sort
    negative: false
    data_needs: Console mock
    bound_to:
      - TST-COM-CAT-006
    provenance: test
    source: >-
      react-console:e2e/mock/commerce-view-edit.mock.spec.ts#create a store, land on its view, edit
      the default sort, save, and see it on the view
    category: rule
    binding_evidence:
      - >-
        react-console:e2e/mock/commerce-view-edit.mock.spec.ts#create a store, land on its view,
        edit the default sort, save, and see it on the view
    step_evidence:
      - do: 'Create a store, open its view, change the default sort and save'
        provenance: test
        source: >-
          react-console:e2e/mock/commerce-view-edit.mock.spec.ts#create a store, land on its view,
          edit the default sort, save, and see it on the view
    assertions:
      - expect: The view displays the saved sort
        provenance: test
        source: >-
          react-console:e2e/mock/commerce-view-edit.mock.spec.ts#create a store, land on its view,
          edit the default sort, save, and see it on the view
  - id: ATS-COM-CAT-003-08
    title: Store protection refuses the protected write
    category: invariant
    level: api
    proves:
      - BR-CAP-COM-CAT-003-13
    preconditions: Separate otherwise editable records use full protection and deletion-only protection.
    steps: >-
      Replace and delete the fully protected record, then replace and delete the deletion-only
      protected record; reread both.
    expected: >-
      The full-protection requests refuse; the deletion-only replacement succeeds and removal
      refuses; refused writes preserve the stored record.
    negative: true
    data_needs: Run-owned workspace and records; fixed clock where dates are exercised.
    bound_to: []
    provenance: proposed
    source:
      - 'services-commerce:modules/configuration/services/persistence/stores.ts'
    step_evidence:
      - do: >-
          Replace and delete the fully protected record, then replace and delete the deletion-only
          protected record; reread both.
        provenance: proposed
        source:
          - 'services-commerce:modules/configuration/services/persistence/stores.ts'
    assertions:
      - expect: >-
          The full-protection requests refuse; the deletion-only replacement succeeds and removal
          refuses; refused writes preserve the stored record.
        provenance: proposed
        source:
          - 'services-commerce:modules/configuration/services/persistence/stores.ts'
  - id: ATS-COM-CAT-003-09
    title: A saved unknown store handler is diagnosed during pricing
    category: rule
    level: api
    proves:
      - BR-CAP-COM-CAT-003-10
    preconditions: An active store and an unknown pricing handler reference are available.
    steps: >-
      Save the reference, read the store, then request a quote and placement using the saved
      handler.
    expected: >-
      The store retains the reference; pricing reports the unavailable handler according to its
      configured failure mode.
    negative: false
    data_needs: Run-owned workspace and records; fixed clock where dates are exercised.
    bound_to: []
    provenance: proposed
    source:
      - 'services-commerce:modules/configuration/services/persistence/stores.ts:storeColumns'
    step_evidence:
      - do: >-
          Save the reference, read the store, then request a quote and placement using the saved
          handler.
        provenance: proposed
        source:
          - 'services-commerce:modules/configuration/services/persistence/stores.ts:storeColumns'
    assertions:
      - expect: >-
          The store retains the reference; pricing reports the unavailable handler according to its
          configured failure mode.
        provenance: proposed
        source:
          - 'services-commerce:modules/configuration/services/persistence/stores.ts:storeColumns'
  - id: ATS-COM-CAT-003-10
    title: A changed assortment changes the next storefront read
    category: interaction
    level: api
    proves:
      - BR-CAP-COM-CAT-003-03
      - BR-CAP-COM-CAT-003-11
      - BR-CAP-COM-CAT-008-01
      - BR-CAP-COM-CAT-008-08
    preconditions: Books and Apparel have published products in an active store.
    steps: Browse; replace the assortment with only Books; browse and open an Apparel product page.
    expected: Only the admitted shelf remains visible and the excluded page is not found.
    negative: false
    data_needs: Run-owned workspace and records; fixed clock where dates are exercised.
    bound_to: []
    provenance: proposed
    source:
      - >-
        services-commerce:modules/catalog/tests/integration/db/catalog-tables.table.integration.spec.js#refuses
        a store category rule outside all, only and all_but
      - >-
        services-commerce:modules/configuration/tests/e2e/stores.e2e.spec.js#replaces a store at the
        version read, the purchasing roles with it, and answers 409 to the stale version after
      - >-
        services-commerce:modules/catalog/tests/unit/storefront/storefront-visibility.spec.js#removes
        a product not yet on sale, and one whose sale has ended (the end exclusive)
      - >-
        services-commerce:modules/catalog/tests/integration/services/storefront/storefront-listings.integration.spec.js#opens
        an unlisted product by its page, and is answered 404 for a draft and for a product another
        store does not sell
    exercises:
      - CAP-COM-CAT-003
      - CAP-COM-CAT-008
    workflows: []
    step_evidence:
      - do: Browse; replace the assortment with only Books; browse and open an Apparel product page.
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/tests/integration/db/catalog-tables.table.integration.spec.js#refuses
            a store category rule outside all, only and all_but
          - >-
            services-commerce:modules/configuration/tests/e2e/stores.e2e.spec.js#replaces a store at
            the version read, the purchasing roles with it, and answers 409 to the stale version
            after
          - >-
            services-commerce:modules/catalog/tests/unit/storefront/storefront-visibility.spec.js#removes
            a product not yet on sale, and one whose sale has ended (the end exclusive)
          - >-
            services-commerce:modules/catalog/tests/integration/services/storefront/storefront-listings.integration.spec.js#opens
            an unlisted product by its page, and is answered 404 for a draft and for a product
            another store does not sell
    assertions:
      - expect: Only the admitted shelf remains visible and the excluded page is not found.
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/tests/integration/db/catalog-tables.table.integration.spec.js#refuses
            a store category rule outside all, only and all_but
          - >-
            services-commerce:modules/configuration/tests/e2e/stores.e2e.spec.js#replaces a store at
            the version read, the purchasing roles with it, and answers 409 to the stale version
            after
          - >-
            services-commerce:modules/catalog/tests/unit/storefront/storefront-visibility.spec.js#removes
            a product not yet on sale, and one whose sale has ended (the end exclusive)
          - >-
            services-commerce:modules/catalog/tests/integration/services/storefront/storefront-listings.integration.spec.js#opens
            an unlisted product by its page, and is answered 404 for a draft and for a product
            another store does not sell
  - id: ATS-COM-CAT-003-11
    title: Moving store references allows a shelf to be removed
    category: interaction
    level: api
    proves:
      - BR-CAP-COM-CAT-005-05
      - BR-CAP-COM-CAT-003-05
    preconditions: Two stores retain a shelf as home or assortment.
    steps: Remove one reference and request deletion; remove the second and repeat.
    expected: Deletion remains refused after the first move and succeeds after the second.
    negative: false
    data_needs: Run-owned workspace and records; fixed clock where dates are exercised.
    bound_to: []
    provenance: proposed
    source:
      - >-
        services-commerce:modules/catalog/tests/integration/services/persistence/product-categories.integration.spec.js#is
        refused while stores name it in their assortment, naming five of them and the count
      - >-
        services-commerce:modules/configuration/tests/integration/services/persistence/stores.integration.spec.js#refuses
        a home category an "all_but" rule leaves out, 400 naming home_category_id
    exercises:
      - CAP-COM-CAT-005
      - CAP-COM-CAT-003
    workflows:
      - WF-COM-CAT-003
      - WF-COM-CAT-004
    step_evidence:
      - do: Remove one reference and request deletion; remove the second and repeat.
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/tests/integration/services/persistence/product-categories.integration.spec.js#is
            refused while stores name it in their assortment, naming five of them and the count
          - >-
            services-commerce:modules/configuration/tests/integration/services/persistence/stores.integration.spec.js#refuses
            a home category an "all_but" rule leaves out, 400 naming home_category_id
    assertions:
      - expect: Deletion remains refused after the first move and succeeds after the second.
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/tests/integration/services/persistence/product-categories.integration.spec.js#is
            refused while stores name it in their assortment, naming five of them and the count
          - >-
            services-commerce:modules/configuration/tests/integration/services/persistence/stores.integration.spec.js#refuses
            a home category an "all_but" rule leaves out, 400 naming home_category_id
  - id: ATS-COM-CAT-003-12
    title: Shared asset settlement uses each record’s current reference
    category: interaction
    level: api
    proves:
      - BR-CAP-COM-CAT-003-09
      - BR-CAP-COM-CAT-005-07
    preconditions: A store and category each have a prepared picture.
    steps: >-
      Replace both pictures, sweep detached references, restore one reference before consuming and
      inspect both owners.
    expected: Settlement retires the detached picture and retains the picture referenced again.
    negative: false
    data_needs: Run-owned workspace and records; fixed clock where dates are exercised.
    bound_to: []
    provenance: proposed
    source:
      - >-
        services-commerce:modules/catalog/tests/integration/services/assets/catalog-assets.integration.spec.js#detaches
        the store’s picture when the store is deleted
      - >-
        services-commerce:modules/catalog/tests/e2e/product-categories.e2e.spec.js#answers 400 to a
        picture the pipeline cannot read as a file, creating nothing
    exercises:
      - CAP-COM-CAT-003
      - CAP-COM-CAT-005
    workflows:
      - WF-COM-CAT-003
      - WF-COM-CAT-004
    step_evidence:
      - do: >-
          Replace both pictures, sweep detached references, restore one reference before consuming
          and inspect both owners.
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/tests/integration/services/assets/catalog-assets.integration.spec.js#detaches
            the store’s picture when the store is deleted
          - >-
            services-commerce:modules/catalog/tests/e2e/product-categories.e2e.spec.js#answers 400
            to a picture the pipeline cannot read as a file, creating nothing
    assertions:
      - expect: Settlement retires the detached picture and retains the picture referenced again.
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/tests/integration/services/assets/catalog-assets.integration.spec.js#detaches
            the store’s picture when the store is deleted
          - >-
            services-commerce:modules/catalog/tests/e2e/product-categories.e2e.spec.js#answers 400
            to a picture the pipeline cannot read as a file, creating nothing
  - id: ATS-COM-CAT-003-13
    title: Store activation is exercised across every declared move
    category: transition
    level: api
    proves:
      - BR-CAP-COM-CAT-003-08
      - BR-CAP-COM-CAT-003-11
      - BR-CAP-COM-CAT-003-13
    preconditions: >-
      Independent run-owned records start in each declared origin state; guarded variants omit one
      required condition at a time.
    steps: >-
      For each transition case, establish the origin, apply its trigger with the guard satisfied,
      read the resulting state and side effects, then repeat on a separate record with a required
      guard absent.
    expected: >-
      Every allowed case reaches its stated destination and retains its declared side effects;
      failed guards preserve the originating record except for the separately documented partial
      offer action.
    negative: true
    data_needs: Run-owned workspace and records; fixed clock where dates are exercised.
    bound_to: []
    provenance: proposed
    source:
      - >-
        rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-002-store-activation.md#STATE-COM-CAT-002
      - 'services-commerce:modules/configuration/services/persistence/stores.ts:updateStore'
      - 'services-commerce:modules/configuration/services/persistence/stores.ts'
      - >-
        services-commerce:modules/catalog/tests/integration/services/assets/catalog-assets.integration.spec.js#detaches
        the store’s picture when the store is deleted
      - >-
        services-commerce:modules/configuration/tests/integration/services/persistence/stores.integration.spec.js#refuses
        deleting a store a checkout was opened in, naming the store
    state_model: STATE-COM-CAT-002
    transition_cases:
      - from: active
        to: inactive
        trigger: A store replacement disables activity.
        guard: The store version must match and full protection must be absent.
        provenance: proposed
        source:
          - 'services-commerce:modules/configuration/services/persistence/stores.ts:updateStore'
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-002-store-activation.md#STATE-COM-CAT-002
      - from: inactive
        to: active
        trigger: A store replacement enables activity.
        guard: Update protection and the store’s current version must permit replacement.
        provenance: proposed
        source:
          - 'services-commerce:modules/configuration/services/persistence/stores.ts'
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-002-store-activation.md#STATE-COM-CAT-002
      - from: active
        to: deleted
        trigger: Staff request store removal.
        guard: Removal requires no protection and no checkout or order reference.
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/tests/integration/services/assets/catalog-assets.integration.spec.js#detaches
            the store’s picture when the store is deleted
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-002-store-activation.md#STATE-COM-CAT-002
      - from: inactive
        to: deleted
        trigger: Staff request store removal.
        guard: Removal requires no protection and no checkout or order reference.
        provenance: proposed
        source:
          - >-
            services-commerce:modules/configuration/tests/integration/services/persistence/stores.integration.spec.js#refuses
            deleting a store a checkout was opened in, naming the store
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-002-store-activation.md#STATE-COM-CAT-002
    step_evidence:
      - do: >-
          For each transition case, establish the origin, apply its trigger with the guard
          satisfied, read the resulting state and side effects, then repeat on a separate record
          with a required guard absent.
        provenance: proposed
        source:
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-002-store-activation.md#STATE-COM-CAT-002
          - 'services-commerce:modules/configuration/services/persistence/stores.ts:updateStore'
          - 'services-commerce:modules/configuration/services/persistence/stores.ts'
          - >-
            services-commerce:modules/catalog/tests/integration/services/assets/catalog-assets.integration.spec.js#detaches
            the store’s picture when the store is deleted
          - >-
            services-commerce:modules/configuration/tests/integration/services/persistence/stores.integration.spec.js#refuses
            deleting a store a checkout was opened in, naming the store
    assertions:
      - expect: >-
          Every allowed case reaches its stated destination and retains its declared side effects;
          failed guards preserve the originating record except for the separately documented partial
          offer action.
        provenance: proposed
        source:
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-002-store-activation.md#STATE-COM-CAT-002
          - 'services-commerce:modules/configuration/services/persistence/stores.ts:updateStore'
          - 'services-commerce:modules/configuration/services/persistence/stores.ts'
          - >-
            services-commerce:modules/catalog/tests/integration/services/assets/catalog-assets.integration.spec.js#detaches
            the store’s picture when the store is deleted
          - >-
            services-commerce:modules/configuration/tests/integration/services/persistence/stores.integration.spec.js#refuses
            deleting a store a checkout was opened in, naming the store
  - id: ATS-COM-CAT-003-14
    title: Managed catalog file lifecycle is exercised across every declared move
    category: transition
    level: api
    proves:
      - BR-CAP-COM-CAT-003-08
      - BR-CAP-COM-CAT-003-11
      - BR-CAP-COM-CAT-003-13
    preconditions: >-
      Independent run-owned records start in each declared origin state; guarded variants omit one
      required condition at a time.
    steps: >-
      For each transition case, establish the origin, apply its trigger with the guard satisfied,
      read the resulting state and side effects, then repeat on a separate record with a required
      guard absent.
    expected: >-
      Every allowed case reaches its stated destination and retains its declared side effects;
      failed guards preserve the originating record except for the separately documented partial
      offer action.
    negative: true
    data_needs: Run-owned workspace and records; fixed clock where dates are exercised.
    bound_to: []
    provenance: proposed
    source:
      - >-
        rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-004-managed-catalog-file.md#STATE-COM-CAT-004
      - >-
        services-commerce:modules/catalog/tests/e2e/product-categories.e2e.spec.js#answers 400 to a
        picture the pipeline cannot read as a file, creating nothing
      - >-
        services-commerce:modules/catalog/tests/integration/services/persistence/product-listings.integration.spec.js#refuses
        a gallery picture written past the pipeline (a reference the save never prepared)
      - 'services-commerce:modules/catalog/services/assets/catalog-assets.ts'
      - >-
        services-commerce:modules/catalog/tests/integration/services/persistence/product-categories.integration.spec.js#deletes
        the category with its memberships and detaches its picture; a second delete is 404
      - 'services-commerce:modules/catalog/services/assets/clean-up-catalog-assets.ts'
      - 'services-commerce:modules/catalog/services/assets/clean-up-catalog-assets.ts'
      - 'services-commerce:modules/catalog/services/assets/clean-up-one-catalog-asset.ts'
      - 'services-commerce:modules/catalog/services/assets/clean-up-one-catalog-asset.ts'
      - 'services-commerce:modules/catalog/aws/lambda/ASSET_CLEANUP_CONSUMER.ts'
    state_model: STATE-COM-CAT-004
    transition_cases:
      - from: preparing
        to: ready
        trigger: Asset copying finishes successfully.
        guard: The asset source must be readable by the pipeline.
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/tests/e2e/product-categories.e2e.spec.js#answers 400
            to a picture the pipeline cannot read as a file, creating nothing
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-004-managed-catalog-file.md#STATE-COM-CAT-004
      - from: ready
        to: attached
        trigger: The parent save commits the prepared reference.
        guard: The save must have prepared the reference it commits.
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/tests/integration/services/persistence/product-listings.integration.spec.js#refuses
            a gallery picture written past the pipeline (a reference the save never prepared)
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-004-managed-catalog-file.md#STATE-COM-CAT-004
      - from: ready
        to: abandoned
        trigger: The parent save fails after copying.
        guard: No additional condition applies.
        provenance: proposed
        source:
          - 'services-commerce:modules/catalog/services/assets/catalog-assets.ts'
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-004-managed-catalog-file.md#STATE-COM-CAT-004
      - from: attached
        to: detached
        trigger: A saved change or removal releases the reference.
        guard: A retained unchanged picture bypasses detachment and keeps its existing reference.
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/tests/integration/services/persistence/product-categories.integration.spec.js#deletes
            the category with its memberships and detaches its picture; a second delete is 404
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-004-managed-catalog-file.md#STATE-COM-CAT-004
      - from: detached
        to: discarding
        trigger: The scheduled sweep claims a due released file.
        guard: The cleanup due instant must have arrived.
        provenance: proposed
        source:
          - 'services-commerce:modules/catalog/services/assets/clean-up-catalog-assets.ts'
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-004-managed-catalog-file.md#STATE-COM-CAT-004
      - from: abandoned
        to: discarding
        trigger: The scheduled sweep claims a due abandoned copy.
        guard: The cleanup due instant must have arrived.
        provenance: proposed
        source:
          - 'services-commerce:modules/catalog/services/assets/clean-up-catalog-assets.ts'
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-004-managed-catalog-file.md#STATE-COM-CAT-004
      - from: discarding
        to: discarded
        trigger: Queue processing finishes cleanup.
        guard: The workspace-scoped owner check must find no retained reference.
        provenance: proposed
        source:
          - 'services-commerce:modules/catalog/services/assets/clean-up-one-catalog-asset.ts'
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-004-managed-catalog-file.md#STATE-COM-CAT-004
      - from: discarding
        to: attached
        trigger: Settlement finds that a saved record still retains the reference.
        guard: A saved owner record must still name the asset.
        provenance: proposed
        source:
          - 'services-commerce:modules/catalog/services/assets/clean-up-one-catalog-asset.ts'
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-004-managed-catalog-file.md#STATE-COM-CAT-004
      - from: discarding
        to: failed
        trigger: The final queue delivery cannot complete cleanup.
        guard: The receipt must be the final permitted retry after earlier failures.
        provenance: proposed
        source:
          - 'services-commerce:modules/catalog/aws/lambda/ASSET_CLEANUP_CONSUMER.ts'
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-004-managed-catalog-file.md#STATE-COM-CAT-004
    step_evidence:
      - do: >-
          For each transition case, establish the origin, apply its trigger with the guard
          satisfied, read the resulting state and side effects, then repeat on a separate record
          with a required guard absent.
        provenance: proposed
        source:
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-004-managed-catalog-file.md#STATE-COM-CAT-004
          - >-
            services-commerce:modules/catalog/tests/e2e/product-categories.e2e.spec.js#answers 400
            to a picture the pipeline cannot read as a file, creating nothing
          - >-
            services-commerce:modules/catalog/tests/integration/services/persistence/product-listings.integration.spec.js#refuses
            a gallery picture written past the pipeline (a reference the save never prepared)
          - 'services-commerce:modules/catalog/services/assets/catalog-assets.ts'
          - >-
            services-commerce:modules/catalog/tests/integration/services/persistence/product-categories.integration.spec.js#deletes
            the category with its memberships and detaches its picture; a second delete is 404
          - 'services-commerce:modules/catalog/services/assets/clean-up-catalog-assets.ts'
          - 'services-commerce:modules/catalog/services/assets/clean-up-catalog-assets.ts'
          - 'services-commerce:modules/catalog/services/assets/clean-up-one-catalog-asset.ts'
          - 'services-commerce:modules/catalog/services/assets/clean-up-one-catalog-asset.ts'
          - 'services-commerce:modules/catalog/aws/lambda/ASSET_CLEANUP_CONSUMER.ts'
    assertions:
      - expect: >-
          Every allowed case reaches its stated destination and retains its declared side effects;
          failed guards preserve the originating record except for the separately documented partial
          offer action.
        provenance: proposed
        source:
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-004-managed-catalog-file.md#STATE-COM-CAT-004
          - >-
            services-commerce:modules/catalog/tests/e2e/product-categories.e2e.spec.js#answers 400
            to a picture the pipeline cannot read as a file, creating nothing
          - >-
            services-commerce:modules/catalog/tests/integration/services/persistence/product-listings.integration.spec.js#refuses
            a gallery picture written past the pipeline (a reference the save never prepared)
          - 'services-commerce:modules/catalog/services/assets/catalog-assets.ts'
          - >-
            services-commerce:modules/catalog/tests/integration/services/persistence/product-categories.integration.spec.js#deletes
            the category with its memberships and detaches its picture; a second delete is 404
          - 'services-commerce:modules/catalog/services/assets/clean-up-catalog-assets.ts'
          - 'services-commerce:modules/catalog/services/assets/clean-up-catalog-assets.ts'
          - 'services-commerce:modules/catalog/services/assets/clean-up-one-catalog-asset.ts'
          - 'services-commerce:modules/catalog/services/assets/clean-up-one-catalog-asset.ts'
          - 'services-commerce:modules/catalog/aws/lambda/ASSET_CLEANUP_CONSUMER.ts'
  - id: ATS-COM-CAT-003-15
    title: A foreign shelf cannot enter the store assortment
    category: invariant
    level: api
    proves:
      - INV-DOM-COM-CAT-002-03
      - BR-CAP-COM-CAT-003-04
    preconditions: Books exists in one workspace and Toys only in another.
    steps: >-
      Create a store in the first workspace with Toys in its selected assortment and inspect saved
      stores afterward.
    expected: The refusal names the unknown shelf and no store is created.
    negative: true
    data_needs: Run-owned workspace and records; fixed clock where dates are exercised.
    bound_to: []
    provenance: proposed
    source:
      - >-
        services-commerce:modules/configuration/tests/integration/services/persistence/stores.integration.spec.js#refuses
        a category id the tenant does not have, 400 naming category_ids and the id, creating nothing
      - >-
        rhythm-v2-docs:specs/commerce/catalog/design/objects/DOM-COM-CAT-002-store.md#DOM-COM-CAT-002
    verification_note: >-
      This design references the invariant; executable binding awaits an assertion-by-assertion
      comparison with this complete scenario.
    step_evidence:
      - do: >-
          Create a store in the first workspace with Toys in its selected assortment and inspect
          saved stores afterward.
        provenance: proposed
        source:
          - >-
            services-commerce:modules/configuration/tests/integration/services/persistence/stores.integration.spec.js#refuses
            a category id the tenant does not have, 400 naming category_ids and the id, creating
            nothing
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/objects/DOM-COM-CAT-002-store.md#DOM-COM-CAT-002
    assertions:
      - expect: The refusal names the unknown shelf and no store is created.
        provenance: proposed
        source:
          - >-
            services-commerce:modules/configuration/tests/integration/services/persistence/stores.integration.spec.js#refuses
            a category id the tenant does not have, 400 naming category_ids and the id, creating
            nothing
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/objects/DOM-COM-CAT-002-store.md#DOM-COM-CAT-002
  - id: ATS-COM-CAT-003-16
    title: Replacing and deleting a store detaches its old pictures
    category: invariant
    level: api
    proves:
      - INV-DOM-COM-CAT-002-06
      - BR-CAP-COM-CAT-003-09
    preconditions: A store retains a privately prepared default picture and a replacement upload exists.
    steps: >-
      Replace the default picture and inspect both managed states; delete the store and inspect the
      replacement state.
    expected: >-
      The old picture is detached after replacement, the new picture is attached until deletion and
      then becomes detached.
    negative: true
    data_needs: Run-owned workspace and records; fixed clock where dates are exercised.
    bound_to: []
    provenance: proposed
    source:
      - >-
        services-commerce:modules/catalog/tests/integration/services/assets/catalog-assets.integration.spec.js#detaches
        the store’s picture when the store is deleted
      - >-
        rhythm-v2-docs:specs/commerce/catalog/design/objects/DOM-COM-CAT-002-store.md#DOM-COM-CAT-002
    verification_note: >-
      This design references the invariant; executable binding awaits an assertion-by-assertion
      comparison with this complete scenario.
    step_evidence:
      - do: >-
          Replace the default picture and inspect both managed states; delete the store and inspect
          the replacement state.
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/tests/integration/services/assets/catalog-assets.integration.spec.js#detaches
            the store’s picture when the store is deleted
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/objects/DOM-COM-CAT-002-store.md#DOM-COM-CAT-002
    assertions:
      - expect: >-
          The old picture is detached after replacement, the new picture is attached until deletion
          and then becomes detached.
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/tests/integration/services/assets/catalog-assets.integration.spec.js#detaches
            the store’s picture when the store is deleted
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/objects/DOM-COM-CAT-002-store.md#DOM-COM-CAT-002
  - id: ATS-COM-CAT-003-17
    title: Shared asset settlement respects each saved owner
    category: interaction
    level: api
    proves:
      - BR-CAP-COM-CAT-003-09
      - BR-CAP-COM-CAT-005-07
      - BR-CAP-COM-CAT-006-08
      - BR-CAP-COM-CAT-006-14
    preconditions: 'A store, shelf and listing each retain a prepared picture in one run-owned workspace.'
    steps: >-
      Replace the three pictures, sweep their detached references, restore one owner reference
      before consuming and inspect every owner and managed asset afterward.
    expected: >-
      Only detached, unreferenced pictures retire; the reference restored before settlement remains
      attached and the saved owners keep their current pictures.
    negative: false
    data_needs: 'Run-owned workspace, prepared file fixtures and independent fault injection at the handoff.'
    bound_to: []
    provenance: proposed
    source:
      - >-
        rhythm-v2-docs:specs/commerce/catalog/design/workflows/WF-COM-CAT-003-save-a-record-with-managed-pictures-or-files.md#WF-COM-CAT-003
      - >-
        rhythm-v2-docs:specs/commerce/catalog/design/workflows/WF-COM-CAT-004-sweep-and-settle-released-catalog-files.md#WF-COM-CAT-004
    exercises:
      - CAP-COM-CAT-003
      - CAP-COM-CAT-005
      - CAP-COM-CAT-006
    workflows:
      - WF-COM-CAT-003
      - WF-COM-CAT-004
    step_evidence:
      - do: >-
          Replace the three pictures, sweep their detached references, restore one owner reference
          before consuming and inspect every owner and managed asset afterward.
        provenance: proposed
        source:
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/workflows/WF-COM-CAT-003-save-a-record-with-managed-pictures-or-files.md#WF-COM-CAT-003
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/workflows/WF-COM-CAT-004-sweep-and-settle-released-catalog-files.md#WF-COM-CAT-004
    assertions:
      - expect: >-
          Only detached, unreferenced pictures retire; the reference restored before settlement
          remains attached and the saved owners keep their current pictures.
        provenance: proposed
        source:
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/workflows/WF-COM-CAT-003-save-a-record-with-managed-pictures-or-files.md#WF-COM-CAT-003
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/workflows/WF-COM-CAT-004-sweep-and-settle-released-catalog-files.md#WF-COM-CAT-004
evidence:
  - 'Pinned service inputs: test sources in scenario bindings'
  - 'Pinned console inputs: console sources in scenario bindings'
  - >-
    rhythm-v2-docs:specs/commerce/catalog/planning/capabilities/CAP-COM-CAT-003-open-a-store-that-sells-a-chosen-assortment.md#CAP-COM-CAT-003
  - >-
    rhythm-v2-docs:specs/commerce/catalog/design/workflows/WF-COM-CAT-003-save-a-record-with-managed-pictures-or-files.md#WF-COM-CAT-003
  - >-
    rhythm-v2-docs:specs/commerce/catalog/design/workflows/WF-COM-CAT-004-sweep-and-settle-released-catalog-files.md#WF-COM-CAT-004
product_intent: proposed
coverage:
  rule: covered
  transition: covered
  validation: covered
  permission: covered
  edge: 'gap: FND-COM-CAT-057'
  failure: 'gap: FND-COM-CAT-057'
  boundary: covered
  concurrency: covered
  invariant: covered
  interaction: covered
coverage_notes:
  edge: >-
    No complete edge scenario is established by this back-fill; the finding records the missing
    proof.
  failure: >-
    No complete failure scenario is established by this back-fill; the finding records the missing
    proof.
  concurrency: >-
    A stale-write example covers version conflict; overlapping operations and external effects
    require separate proof.
permission_coverage:
  - key: commerce.stores.read
    coverage: 'gap: FND-COM-CAT-057'
    reason: >-
      Existing permission examples cover selected personas, not the independent allow and deny
      matrix for every named key.
    provenance: unknown
    source:
      - 'rhythm-v2-docs:specs/commerce/catalog/FINDINGS.md#FND-COM-CAT-057'
  - key: commerce.stores.create
    coverage: 'gap: FND-COM-CAT-057'
    reason: >-
      Existing permission examples cover selected personas, not the independent allow and deny
      matrix for every named key.
    provenance: unknown
    source:
      - 'rhythm-v2-docs:specs/commerce/catalog/FINDINGS.md#FND-COM-CAT-057'
  - key: commerce.stores.update
    coverage: 'gap: FND-COM-CAT-057'
    reason: >-
      Existing permission examples cover selected personas, not the independent allow and deny
      matrix for every named key.
    provenance: unknown
    source:
      - 'rhythm-v2-docs:specs/commerce/catalog/FINDINGS.md#FND-COM-CAT-057'
  - key: commerce.stores.delete
    coverage: 'gap: FND-COM-CAT-057'
    reason: >-
      Existing permission examples cover selected personas, not the independent allow and deny
      matrix for every named key.
    provenance: unknown
    source:
      - 'rhythm-v2-docs:specs/commerce/catalog/FINDINGS.md#FND-COM-CAT-057'
  - key: commerce.stores.bulk
    coverage: 'gap: FND-COM-CAT-057'
    reason: >-
      Existing permission examples cover selected personas, not the independent allow and deny
      matrix for every named key.
    provenance: unknown
    source:
      - 'rhythm-v2-docs:specs/commerce/catalog/FINDINGS.md#FND-COM-CAT-057'
---

# Configure a store’s offered assortment: Capability Test Specification

## Coverage

| Rule | Proof Scenario or Gap |
|---|---|
| `BR-CAP-COM-CAT-003-01` | `ATS-COM-CAT-003-01` |
| `BR-CAP-COM-CAT-003-02` | `ATS-COM-CAT-003-02` |
| `BR-CAP-COM-CAT-003-04` | `ATS-COM-CAT-003-03`, `ATS-COM-CAT-003-15` |
| `BR-CAP-COM-CAT-003-05` | `ATS-COM-CAT-003-03`, `ATS-COM-CAT-003-11` |
| `BR-CAP-COM-CAT-003-08` | `ATS-COM-CAT-003-04`, `ATS-COM-CAT-003-13`, `ATS-COM-CAT-003-14` |
| `BR-CAP-COM-CAT-003-10` | `ATS-COM-CAT-003-09` |
| `BR-CAP-COM-CAT-003-11` | `ATS-COM-CAT-003-05`, `ATS-COM-CAT-003-10`, `ATS-COM-CAT-003-13`, `ATS-COM-CAT-003-14` |
| `BR-CAP-COM-CAT-003-12` | `ATS-COM-CAT-003-06` |
| `BR-CAP-COM-CAT-003-13` | `ATS-COM-CAT-003-08`, `ATS-COM-CAT-003-13`, `ATS-COM-CAT-003-14` |

## Interactions With Related Capabilities

A changed assortment changes the next storefront read. Moving store references allows a shelf to be removed. Shared asset settlement uses each record’s current reference. Shared asset settlement respects each saved owner.

Coverage describes specified proof. A binding names the existing assertions that contribute to it; proposed designs remain unexecuted.
