---
id: ATS-COM-CAT-006
scope: CAP-COM-CAT-006
product_area: commerce
module: catalog
revision: 2
status: proposed
source_sha:
  services-commerce: c8b66a98e2490617aec38ba5c8165dd9bd0a8f9b
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
scenarios:
  - id: ATS-COM-CAT-006-01
    title: Listing identities and slugs prevent duplicate publication records
    level: api
    proves:
      - BR-CAP-COM-CAT-006-01
      - INV-DOM-COM-CAT-003-01
    preconditions: A listing already occupies a product and slug
    steps: 'Repeat the product, reuse the slug for another product and submit a malformed slug'
    expected: All invalid creations fail
    negative: true
    data_needs: none
    bound_to:
      - TST-COM-CAT-015
      - TST-COM-CAT-021
    provenance: test
    source: >-
      services-commerce:modules/catalog/tests/e2e/product-listings.e2e.spec.js#answers 400 to a
      second listing of the same product
    category: validation
    binding_evidence:
      - >-
        services-commerce:modules/catalog/tests/e2e/product-listings.e2e.spec.js#answers 400 to a
        second listing of the same product
      - >-
        services-commerce:modules/catalog/tests/e2e/product-listings.e2e.spec.js#answers 400 to a
        slug that is not one
      - >-
        services-commerce:modules/catalog/tests/integration/services/persistence/product-listings.integration.spec.js#refuses
        a second listing of the product (400) and another listing under the taken slug (400)
    step_evidence:
      - do: 'Repeat the product, reuse the slug for another product and submit a malformed slug'
        provenance: test
        source: >-
          services-commerce:modules/catalog/tests/e2e/product-listings.e2e.spec.js#answers 400 to a
          second listing of the same product
    assertions:
      - expect: All invalid creations fail
        provenance: test
        source: >-
          services-commerce:modules/catalog/tests/e2e/product-listings.e2e.spec.js#answers 400 to a
          second listing of the same product
  - id: ATS-COM-CAT-006-02
    title: Listing state controls list and page visibility
    level: unit
    proves:
      - BR-CAP-COM-CAT-006-02
    preconditions: 'Fixtures include draft, published and unlisted listings'
    steps: Read storefront lists and individual pages
    expected: Lists retain published items; pages also retain unlisted items; drafts remain excluded
    negative: false
    data_needs: none
    bound_to:
      - TST-COM-CAT-027
    provenance: test
    source: >-
      services-commerce:modules/catalog/tests/unit/storefront/storefront-visibility.spec.js#shows
      published listings alone on the list, and unlisted ones too on a product page
    category: transition
    binding_evidence:
      - >-
        services-commerce:modules/catalog/tests/unit/storefront/storefront-visibility.spec.js#shows
        published listings alone on the list, and unlisted ones too on a product page
      - >-
        services-commerce:modules/catalog/tests/unit/storefront/storefront-visibility.spec.js#removes
        a draft everywhere, an unlisted listing from the list only, and a published one nowhere
    step_evidence:
      - do: Read storefront lists and individual pages
        provenance: test
        source: >-
          services-commerce:modules/catalog/tests/unit/storefront/storefront-visibility.spec.js#shows
          published listings alone on the list, and unlisted ones too on a product page
    assertions:
      - expect: Lists retain published items; pages also retain unlisted items; drafts remain excluded
        provenance: test
        source: >-
          services-commerce:modules/catalog/tests/unit/storefront/storefront-visibility.spec.js#shows
          published listings alone on the list, and unlisted ones too on a product page
  - id: ATS-COM-CAT-006-03
    title: Unlisted products remain publicly quotable
    level: api
    proves:
      - BR-CAP-COM-CAT-006-03
    preconditions: An unlisted product belongs to the store assortment
    steps: Request its public quote
    expected: The quote returns a price
    negative: false
    data_needs: none
    bound_to:
      - TST-COM-CAT-057
    provenance: test
    source: >-
      services-commerce:modules/pricing/tests/e2e/price-quote-public-sale.e2e.spec.js#prices an
      unlisted product, which the product page opens by its link
    category: interaction
    binding_evidence:
      - >-
        services-commerce:modules/pricing/tests/e2e/price-quote-public-sale.e2e.spec.js#prices an
        unlisted product, which the product page opens by its link
    step_evidence:
      - do: Request its public quote
        provenance: test
        source: >-
          services-commerce:modules/pricing/tests/e2e/price-quote-public-sale.e2e.spec.js#prices an
          unlisted product, which the product page opens by its link
    assertions:
      - expect: The quote returns a price
        provenance: test
        source: >-
          services-commerce:modules/pricing/tests/e2e/price-quote-public-sale.e2e.spec.js#prices an
          unlisted product, which the product page opens by its link
  - id: ATS-COM-CAT-006-04
    title: Digital-file and grant bounds reject invalid settings
    level: api
    proves:
      - BR-CAP-COM-CAT-006-04
      - BR-CAP-COM-CAT-006-05
      - INV-DOM-COM-CAT-003-04
    preconditions: A physical item and digital item are available
    steps: Attach a file to the physical item and give the digital item an excessive window or zero limit
    expected: Each request identifies its invalid setting
    negative: true
    data_needs: none
    bound_to:
      - TST-COM-CAT-015
      - TST-COM-CAT-025
    provenance: test
    source: >-
      services-commerce:modules/catalog/tests/e2e/product-listings.e2e.spec.js#answers 400 to files
      on a product that is not digital merchandise
    category: boundary
    binding_evidence:
      - >-
        services-commerce:modules/catalog/tests/e2e/product-listings.e2e.spec.js#answers 400 to
        files on a product that is not digital merchandise
      - >-
        services-commerce:modules/catalog/tests/unit/api/product-listing-download-rules.spec.js#refuses
        a window of 4000 days, naming the field and the bound
      - >-
        services-commerce:modules/catalog/tests/unit/api/product-listing-download-rules.spec.js#still
        refuses a window of zero
    step_evidence:
      - do: >-
          Attach a file to the physical item and give the digital item an excessive window or zero
          limit
        provenance: test
        source: >-
          services-commerce:modules/catalog/tests/e2e/product-listings.e2e.spec.js#answers 400 to
          files on a product that is not digital merchandise
    assertions:
      - expect: Each request identifies its invalid setting
        provenance: test
        source: >-
          services-commerce:modules/catalog/tests/e2e/product-listings.e2e.spec.js#answers 400 to
          files on a product that is not digital merchandise
  - id: ATS-COM-CAT-006-05
    title: Listing presentation rejects invalid collections and dates
    level: unit
    proves:
      - BR-CAP-COM-CAT-006-06
      - BR-CAP-COM-CAT-006-07
      - INV-DOM-COM-CAT-003-12
    preconditions: Listing validation accepts a complete input
    steps: 'Supply nine images, a self-related product and reversed Featured dates'
    expected: Validation reports each problem
    negative: true
    data_needs: none
    bound_to:
      - TST-COM-CAT-009
      - TST-COM-CAT-021
    provenance: test
    source: >-
      react-console:lib/commerce/catalog/__tests__/rules.test.ts#refuses more than eight gallery
      images or related products
    category: validation
    binding_evidence:
      - >-
        react-console:lib/commerce/catalog/__tests__/rules.test.ts#refuses more than eight gallery
        images or related products
      - >-
        react-console:lib/commerce/catalog/__tests__/rules.test.ts#refuses a featured window that
        ends before it starts
      - >-
        services-commerce:modules/catalog/tests/integration/services/persistence/product-listings.integration.spec.js#refuses
        files on a product that is not digital merchandise (none, or physical), and a product that
        goes with itself (400)
    step_evidence:
      - do: 'Supply nine images, a self-related product and reversed Featured dates'
        provenance: test
        source: >-
          react-console:lib/commerce/catalog/__tests__/rules.test.ts#refuses more than eight gallery
          images or related products
    assertions:
      - expect: Validation reports each problem
        provenance: test
        source: >-
          react-console:lib/commerce/catalog/__tests__/rules.test.ts#refuses more than eight gallery
          images or related products
  - id: ATS-COM-CAT-006-06
    title: Listing replacement reuses and detaches prepared images
    level: api
    proves:
      - BR-CAP-COM-CAT-006-08
      - INV-DOM-COM-CAT-003-05
    preconditions: The saved gallery contains two images
    steps: 'Retain one image, replace the other and separately attempt an unprepared reference'
    expected: >-
      The retained identity survives, the replacement detaches its predecessor and the unprepared
      reference fails
    negative: true
    data_needs: Asset pipeline stand-in
    bound_to:
      - TST-COM-CAT-021
    provenance: test
    source: >-
      services-commerce:modules/catalog/tests/integration/services/persistence/product-listings.integration.spec.js#keeps
      an unchanged picture with its id (no copy), replaces the other (the old one detached), and
      refuses a stale version
    category: invariant
    binding_evidence:
      - >-
        services-commerce:modules/catalog/tests/integration/services/persistence/product-listings.integration.spec.js#keeps
        an unchanged picture with its id (no copy), replaces the other (the old one detached), and
        refuses a stale version
      - >-
        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)
    step_evidence:
      - do: 'Retain one image, replace the other and separately attempt an unprepared reference'
        provenance: test
        source: >-
          services-commerce:modules/catalog/tests/integration/services/persistence/product-listings.integration.spec.js#keeps
          an unchanged picture with its id (no copy), replaces the other (the old one detached), and
          refuses a stale version
    assertions:
      - expect: >-
          The retained identity survives, the replacement detaches its predecessor and the
          unprepared reference fails
        provenance: test
        source: >-
          services-commerce:modules/catalog/tests/integration/services/persistence/product-listings.integration.spec.js#keeps
          an unchanged picture with its id (no copy), replaces the other (the old one detached), and
          refuses a stale version
  - id: ATS-COM-CAT-006-07
    title: Listing edit authority follows initial or existing state
    level: unit
    proves:
      - BR-CAP-COM-CAT-006-11
    preconditions: 'Fixtures distinguish create, update and read-only permissions'
    steps: Inspect storefront actions for listed and unlisted products
    expected: Edit and Publish appear only with the applicable permission
    negative: true
    data_needs: none
    bound_to:
      - TST-COM-CAT-004
    provenance: test
    source: >-
      react-console:components/commerce/__tests__/permission-gating.test.tsx#asks a product not on
      the shelf yet for the create key, since its first save is a create
    category: permission
    binding_evidence:
      - >-
        react-console:components/commerce/__tests__/permission-gating.test.tsx#asks a product not on
        the shelf yet for the create key, since its first save is a create
      - >-
        react-console:components/commerce/__tests__/permission-gating.test.tsx#offers a read-only
        person no edit and no publish, listed or not
    step_evidence:
      - do: Inspect storefront actions for listed and unlisted products
        provenance: test
        source: >-
          react-console:components/commerce/__tests__/permission-gating.test.tsx#asks a product not
          on the shelf yet for the create key, since its first save is a create
    assertions:
      - expect: Edit and Publish appear only with the applicable permission
        provenance: test
        source: >-
          react-console:components/commerce/__tests__/permission-gating.test.tsx#asks a product not
          on the shelf yet for the create key, since its first save is a create
  - id: ATS-COM-CAT-006-08
    title: Listing replacement rejects stale or mismatched records
    level: api
    proves:
      - BR-CAP-COM-CAT-006-12
    preconditions: The listing has advanced to version two
    steps: Submit version one and separately name a different product
    expected: Both requests fail without replacing the listing
    negative: true
    data_needs: none
    bound_to:
      - TST-COM-CAT-015
    provenance: test
    source: >-
      services-commerce:modules/catalog/tests/e2e/product-listings.e2e.spec.js#replaces at the
      version read, a gallery picture kept with its id and one replaced; the stale version is 409; a
      mismatched product_id 400
    category: concurrency
    binding_evidence:
      - >-
        services-commerce:modules/catalog/tests/e2e/product-listings.e2e.spec.js#replaces at the
        version read, a gallery picture kept with its id and one replaced; the stale version is 409;
        a mismatched product_id 400
    step_evidence:
      - do: Submit version one and separately name a different product
        provenance: test
        source: >-
          services-commerce:modules/catalog/tests/e2e/product-listings.e2e.spec.js#replaces at the
          version read, a gallery picture kept with its id and one replaced; the stale version is
          409; a mismatched product_id 400
    assertions:
      - expect: Both requests fail without replacing the listing
        provenance: test
        source: >-
          services-commerce:modules/catalog/tests/e2e/product-listings.e2e.spec.js#replaces at the
          version read, a gallery picture kept with its id and one replaced; the stale version is
          409; a mismatched product_id 400
  - id: ATS-COM-CAT-006-09
    title: Merchandise view reflects saved storefront pricing
    level: browser
    proves:
      - AS-JNY-COM-CAT-001-05
    preconditions: The browser uses mocked Commerce data
    steps: Create an item with storefront information and later change its price
    expected: The item view presents the revised price
    negative: false
    data_needs: Console mock
    bound_to:
      - TST-COM-CAT-007
    provenance: test
    source: >-
      react-console:e2e/mock/merchandise.mock.spec.ts#a new item is created with its storefront,
      shown on its view, and a price change comes back to the view
    category: interaction
    binding_evidence:
      - >-
        react-console:e2e/mock/merchandise.mock.spec.ts#a new item is created with its storefront,
        shown on its view, and a price change comes back to the view
    step_evidence:
      - do: Create an item with storefront information and later change its price
        provenance: test
        source: >-
          react-console:e2e/mock/merchandise.mock.spec.ts#a new item is created with its storefront,
          shown on its view, and a price change comes back to the view
    assertions:
      - expect: The item view presents the revised price
        provenance: test
        source: >-
          react-console:e2e/mock/merchandise.mock.spec.ts#a new item is created with its storefront,
          shown on its view, and a price change comes back to the view
  - id: ATS-COM-CAT-006-10
    title: Listing protection refuses the protected write
    category: invariant
    level: api
    proves:
      - BR-CAP-COM-CAT-006-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/catalog/services/persistence/product-listings.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/catalog/services/persistence/product-listings.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/catalog/services/persistence/product-listings.ts'
  - id: ATS-COM-CAT-006-11
    title: Asset cleanup preserves references and records terminal failures
    category: failure
    level: api
    proves:
      - BR-CAP-COM-CAT-006-14
    preconditions: 'Detached, abandoned and retained assets are due for cleanup.'
    steps: >-
      Sweep the assets; inspect the queue before consuming; settle messages, restoring one parent
      reference before settlement; fail another through its last retry.
    expected: >-
      Sweeping changes no stored object; settlement keeps the restored reference, retires detached
      data, deletes abandoned data and records terminal failure.
    negative: false
    data_needs: Run-owned workspace and records; fixed clock where dates are exercised.
    bound_to: []
    provenance: proposed
    source:
      - 'services-commerce:modules/catalog/services/assets/clean-up-one-catalog-asset.ts:1'
    step_evidence:
      - do: >-
          Sweep the assets; inspect the queue before consuming; settle messages, restoring one
          parent reference before settlement; fail another through its last retry.
        provenance: proposed
        source:
          - 'services-commerce:modules/catalog/services/assets/clean-up-one-catalog-asset.ts:1'
    assertions:
      - expect: >-
          Sweeping changes no stored object; settlement keeps the restored reference, retires
          detached data, deletes abandoned data and records terminal failure.
        provenance: proposed
        source:
          - 'services-commerce:modules/catalog/services/assets/clean-up-one-catalog-asset.ts:1'
  - id: ATS-COM-CAT-006-12
    title: Moving a listing between shelves changes membership order
    category: interaction
    level: api
    proves:
      - BR-CAP-COM-CAT-005-04
      - BR-CAP-COM-CAT-006-02
    preconditions: Two shelves and a published listing exist.
    steps: 'Replace the listing’s shelf selection, then read both memberships.'
    expected: The old membership disappears and the new membership is appended in the new shelf.
    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#refuses
        a list naming one product twice (400) and an unknown category (404)
      - >-
        services-commerce:modules/catalog/tests/unit/storefront/storefront-visibility.spec.js#removes
        a draft everywhere, an unlisted listing from the list only, and a published one nowhere
    exercises:
      - CAP-COM-CAT-005
      - CAP-COM-CAT-006
    workflows:
      - WF-COM-CAT-003
      - WF-COM-CAT-004
    step_evidence:
      - do: 'Replace the listing’s shelf selection, then read both memberships.'
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/tests/integration/services/persistence/product-categories.integration.spec.js#refuses
            a list naming one product twice (400) and an unknown category (404)
          - >-
            services-commerce:modules/catalog/tests/unit/storefront/storefront-visibility.spec.js#removes
            a draft everywhere, an unlisted listing from the list only, and a published one nowhere
    assertions:
      - expect: The old membership disappears and the new membership is appended in the new shelf.
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/tests/integration/services/persistence/product-categories.integration.spec.js#refuses
            a list naming one product twice (400) and an unknown category (404)
          - >-
            services-commerce:modules/catalog/tests/unit/storefront/storefront-visibility.spec.js#removes
            a draft everywhere, an unlisted listing from the list only, and a published one nowhere
  - id: ATS-COM-CAT-006-13
    title: Publication changes list and direct-page visibility
    category: interaction
    level: api
    proves:
      - BR-CAP-COM-CAT-006-02
      - BR-CAP-COM-CAT-008-01
      - BR-CAP-COM-CAT-008-08
    preconditions: A store admits a listing’s shelf.
    steps: >-
      Move the listing from draft to published to unlisted to draft, reading list and page after
      each save.
    expected: The observed list and page results match each declared publication transition.
    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/unit/storefront/storefront-visibility.spec.js#removes
        a draft everywhere, an unlisted listing from the list only, and a published one nowhere
      - >-
        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-006
      - CAP-COM-CAT-008
    workflows: []
    step_evidence:
      - do: >-
          Move the listing from draft to published to unlisted to draft, reading list and page after
          each save.
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/tests/unit/storefront/storefront-visibility.spec.js#removes
            a draft everywhere, an unlisted listing from the list only, and a published one nowhere
          - >-
            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: The observed list and page results match each declared publication transition.
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/tests/unit/storefront/storefront-visibility.spec.js#removes
            a draft everywhere, an unlisted listing from the list only, and a published one nowhere
          - >-
            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-006-14
    title: A file replacement changes which download can be requested
    category: interaction
    level: api
    proves:
      - BR-CAP-COM-CAT-006-04
      - BR-CAP-COM-CAT-006-05
      - BR-CAP-COM-CAT-009-02
      - BR-CAP-COM-CAT-009-05
    preconditions: Digital merchandise has a retained file and an owner grant.
    steps: >-
      Download the retained file; replace it with another privately prepared file; request the
      detached file and the replacement.
    expected: >-
      The detached file is not found and the retained replacement is available under the grant’s
      remaining limits.
    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/e2e/product-listings.e2e.spec.js#answers 400 to
        files on a product that is not digital merchandise
      - >-
        services-commerce:modules/catalog/tests/unit/api/product-listing-download-rules.spec.js#refuses
        a window of 4000 days, naming the field and the bound
      - >-
        services-commerce:modules/catalog/tests/e2e/downloads.e2e.spec.js#gives the grant’s owner a
        signed link and consumes one use per Idempotency-Key (a retry costs nothing), until the
        grant is exhausted (409)
      - >-
        services-commerce:modules/catalog/tests/e2e/downloads.e2e.spec.js#answers 404 for a grant of
        another kind, an unknown grant, and a file the product does not have
    exercises:
      - CAP-COM-CAT-006
      - CAP-COM-CAT-009
    workflows: []
    step_evidence:
      - do: >-
          Download the retained file; replace it with another privately prepared file; request the
          detached file and the replacement.
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/tests/e2e/product-listings.e2e.spec.js#answers 400 to
            files on a product that is not digital merchandise
          - >-
            services-commerce:modules/catalog/tests/unit/api/product-listing-download-rules.spec.js#refuses
            a window of 4000 days, naming the field and the bound
          - >-
            services-commerce:modules/catalog/tests/e2e/downloads.e2e.spec.js#gives the grant’s
            owner a signed link and consumes one use per Idempotency-Key (a retry costs nothing),
            until the grant is exhausted (409)
          - >-
            services-commerce:modules/catalog/tests/e2e/downloads.e2e.spec.js#answers 404 for a
            grant of another kind, an unknown grant, and a file the product does not have
    assertions:
      - expect: >-
          The detached file is not found and the retained replacement is available under the grant’s
          remaining limits.
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/tests/e2e/product-listings.e2e.spec.js#answers 400 to
            files on a product that is not digital merchandise
          - >-
            services-commerce:modules/catalog/tests/unit/api/product-listing-download-rules.spec.js#refuses
            a window of 4000 days, naming the field and the bound
          - >-
            services-commerce:modules/catalog/tests/e2e/downloads.e2e.spec.js#gives the grant’s
            owner a signed link and consumes one use per Idempotency-Key (a retry costs nothing),
            until the grant is exhausted (409)
          - >-
            services-commerce:modules/catalog/tests/e2e/downloads.e2e.spec.js#answers 404 for a
            grant of another kind, an unknown grant, and a file the product does not have
  - id: ATS-COM-CAT-006-15
    title: An unlisted page retains public quoting while a draft does not
    category: interaction
    level: api
    proves:
      - BR-CAP-COM-CAT-006-03
      - BR-CAP-COM-CAT-013-07
    preconditions: The same product stays in the store assortment.
    steps: Save unlisted and quote by its direct page; save draft and quote again as a visitor.
    expected: The unlisted quote returns the product amount and the draft quote is refused.
    negative: false
    data_needs: Run-owned workspace and records; fixed clock where dates are exercised.
    bound_to: []
    provenance: proposed
    source:
      - >-
        services-commerce:modules/pricing/tests/e2e/price-quote-public-sale.e2e.spec.js#prices an
        unlisted product, which the product page opens by its link
      - >-
        services-commerce:modules/pricing/tests/e2e/price-quote-public-sale.e2e.spec.js#prices the
        draft and the out-of-assortment product for staff holding commerce.orders.create: Standard
        6000 and 15000
    exercises:
      - CAP-COM-CAT-006
      - CAP-COM-CAT-013
    workflows: []
    step_evidence:
      - do: Save unlisted and quote by its direct page; save draft and quote again as a visitor.
        provenance: proposed
        source:
          - >-
            services-commerce:modules/pricing/tests/e2e/price-quote-public-sale.e2e.spec.js#prices
            an unlisted product, which the product page opens by its link
          - >-
            services-commerce:modules/pricing/tests/e2e/price-quote-public-sale.e2e.spec.js#prices
            the draft and the out-of-assortment product for staff holding commerce.orders.create:
            Standard 6000 and 15000
    assertions:
      - expect: The unlisted quote returns the product amount and the draft quote is refused.
        provenance: proposed
        source:
          - >-
            services-commerce:modules/pricing/tests/e2e/price-quote-public-sale.e2e.spec.js#prices
            an unlisted product, which the product page opens by its link
          - >-
            services-commerce:modules/pricing/tests/e2e/price-quote-public-sale.e2e.spec.js#prices
            the draft and the out-of-assortment product for staff holding commerce.orders.create:
            Standard 6000 and 15000
  - id: ATS-COM-CAT-006-16
    title: The combined item screen saves product and presentation separately
    category: interaction
    level: api
    proves:
      - BR-CAP-COM-CAT-007-13
      - BR-CAP-COM-CAT-006-10
    preconditions: The console edits merchandise and a listing for one product.
    steps: >-
      Save all three requests successfully; repeat with the listing request refused; reopen the
      item.
    expected: >-
      A successful save presents the changed item; a later refusal can leave earlier product and
      merchandise changes saved.
    negative: false
    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/workflows/WF-COM-CAT-005-save-a-merchandise-item-from-the-console-in.md#WF-COM-CAT-005
      - 'react-console:lib/commerce/merchandise/live.ts:createLiveMerchandiseAdapter'
    exercises:
      - CAP-COM-CAT-007
      - CAP-COM-CAT-006
    workflows:
      - WF-COM-CAT-005
    step_evidence:
      - do: >-
          Save all three requests successfully; repeat with the listing request refused; reopen the
          item.
        provenance: proposed
        source:
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/workflows/WF-COM-CAT-005-save-a-merchandise-item-from-the-console-in.md#WF-COM-CAT-005
          - 'react-console:lib/commerce/merchandise/live.ts:createLiveMerchandiseAdapter'
    assertions:
      - expect: >-
          A successful save presents the changed item; a later refusal can leave earlier product and
          merchandise changes saved.
        provenance: proposed
        source:
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/workflows/WF-COM-CAT-005-save-a-merchandise-item-from-the-console-in.md#WF-COM-CAT-005
          - 'react-console:lib/commerce/merchandise/live.ts:createLiveMerchandiseAdapter'
    findings:
      - FND-COM-CAT-029
  - id: ATS-COM-CAT-006-17
    title: Listing publication is exercised across every declared move
    category: transition
    level: api
    proves:
      - BR-CAP-COM-CAT-006-02
      - BR-CAP-COM-CAT-006-08
      - BR-CAP-COM-CAT-006-11
      - BR-CAP-COM-CAT-006-12
      - BR-CAP-COM-CAT-006-13
      - BR-CAP-COM-CAT-006-14
    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-003-listing-publication.md#STATE-COM-CAT-003
      - >-
        react-console:components/commerce/__tests__/permission-gating.test.tsx#asks a product not on
        the shelf yet for the create key, since its first save is a create
      - 'services-commerce:docs/milestones/COMM-M1b-promotions-and-codes.md#R6'
      - >-
        services-commerce:modules/catalog/services/persistence/product-listings.ts:createProductListing
      - >-
        services-commerce:modules/catalog/tests/unit/storefront/storefront-visibility.spec.js#removes
        a draft everywhere, an unlisted listing from the list only, and a published one nowhere
      - >-
        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
      - >-
        services-commerce:modules/catalog/services/persistence/product-listings.ts:updateProductListing
      - >-
        services-commerce:modules/catalog/services/persistence/product-listings.ts:updateProductListing
      - >-
        services-commerce:modules/catalog/services/persistence/product-listings.ts:updateProductListing
      - >-
        services-commerce:modules/catalog/services/persistence/product-listings.ts:updateProductListing
      - >-
        services-commerce:modules/catalog/services/persistence/product-listings.ts:deleteProductListing
      - >-
        services-commerce:modules/catalog/services/persistence/product-listings.ts:deleteProductListing
      - >-
        services-commerce:modules/catalog/services/persistence/product-listings.ts:deleteProductListing
    state_model: STATE-COM-CAT-003
    transition_cases:
      - from: no listing
        to: draft
        trigger: Creation saves a draft presentation.
        guard: >-
          Creation authority, a valid distinct slug and digital delivery for attached files are
          required.
        provenance: proposed
        source:
          - >-
            react-console:components/commerce/__tests__/permission-gating.test.tsx#asks a product
            not on the shelf yet for the create key, since its first save is a create
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-003-listing-publication.md#STATE-COM-CAT-003
      - from: no listing
        to: published
        trigger: 'Creation saves a published presentation, as the new-merchandise console default does.'
        guard: Creation authority and listing validation must pass.
        provenance: proposed
        source:
          - 'services-commerce:docs/milestones/COMM-M1b-promotions-and-codes.md#R6'
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-003-listing-publication.md#STATE-COM-CAT-003
      - from: no listing
        to: unlisted
        trigger: Creation saves a presentation available only by link.
        guard: Creation authority and listing validation must pass.
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/services/persistence/product-listings.ts:createProductListing
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-003-listing-publication.md#STATE-COM-CAT-003
      - from: draft
        to: published
        trigger: Replacement requests publication.
        guard: 'Replacement requires update authority, the current version and no full protection.'
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/tests/unit/storefront/storefront-visibility.spec.js#removes
            a draft everywhere, an unlisted listing from the list only, and a published one nowhere
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-003-listing-publication.md#STATE-COM-CAT-003
      - from: published
        to: unlisted
        trigger: Replacement requests link-only visibility.
        guard: 'Replacement requires update authority, the current version and no full protection.'
        provenance: proposed
        source:
          - >-
            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
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-003-listing-publication.md#STATE-COM-CAT-003
      - from: unlisted
        to: published
        trigger: Replacement restores publication.
        guard: 'Replacement requires update authority, the current version and no full protection.'
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/services/persistence/product-listings.ts:updateProductListing
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-003-listing-publication.md#STATE-COM-CAT-003
      - from: published
        to: draft
        trigger: Replacement returns a published presentation to preparation.
        guard: 'Replacement requires update authority, the current version and no full protection.'
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/services/persistence/product-listings.ts:updateProductListing
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-003-listing-publication.md#STATE-COM-CAT-003
      - from: unlisted
        to: draft
        trigger: Replacement removes public access from a link-only presentation.
        guard: 'Replacement requires update authority, the current version and no full protection.'
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/services/persistence/product-listings.ts:updateProductListing
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-003-listing-publication.md#STATE-COM-CAT-003
      - from: draft
        to: unlisted
        trigger: Replacement makes a prepared presentation accessible by link.
        guard: 'Replacement requires update authority, the current version and no full protection.'
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/services/persistence/product-listings.ts:updateProductListing
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-003-listing-publication.md#STATE-COM-CAT-003
      - from: draft
        to: no listing
        trigger: Staff remove the draft listing.
        guard: Deletion authority is required and neither protection level may block removal.
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/services/persistence/product-listings.ts:deleteProductListing
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-003-listing-publication.md#STATE-COM-CAT-003
      - from: published
        to: no listing
        trigger: Staff remove the published listing.
        guard: Deletion authority is required and neither protection level may block removal.
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/services/persistence/product-listings.ts:deleteProductListing
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-003-listing-publication.md#STATE-COM-CAT-003
      - from: unlisted
        to: no listing
        trigger: Staff remove the link-only listing.
        guard: Deletion authority is required and neither protection level may block removal.
        provenance: proposed
        source:
          - >-
            services-commerce:modules/catalog/services/persistence/product-listings.ts:deleteProductListing
          - >-
            rhythm-v2-docs:specs/commerce/catalog/design/state-models/STATE-COM-CAT-003-listing-publication.md#STATE-COM-CAT-003
    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-003-listing-publication.md#STATE-COM-CAT-003
          - >-
            react-console:components/commerce/__tests__/permission-gating.test.tsx#asks a product
            not on the shelf yet for the create key, since its first save is a create
          - 'services-commerce:docs/milestones/COMM-M1b-promotions-and-codes.md#R6'
          - >-
            services-commerce:modules/catalog/services/persistence/product-listings.ts:createProductListing
          - >-
            services-commerce:modules/catalog/tests/unit/storefront/storefront-visibility.spec.js#removes
            a draft everywhere, an unlisted listing from the list only, and a published one nowhere
          - >-
            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
          - >-
            services-commerce:modules/catalog/services/persistence/product-listings.ts:updateProductListing
          - >-
            services-commerce:modules/catalog/services/persistence/product-listings.ts:updateProductListing
          - >-
            services-commerce:modules/catalog/services/persistence/product-listings.ts:updateProductListing
          - >-
            services-commerce:modules/catalog/services/persistence/product-listings.ts:updateProductListing
          - >-
            services-commerce:modules/catalog/services/persistence/product-listings.ts:deleteProductListing
          - >-
            services-commerce:modules/catalog/services/persistence/product-listings.ts:deleteProductListing
          - >-
            services-commerce:modules/catalog/services/persistence/product-listings.ts:deleteProductListing
    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-003-listing-publication.md#STATE-COM-CAT-003
          - >-
            react-console:components/commerce/__tests__/permission-gating.test.tsx#asks a product
            not on the shelf yet for the create key, since its first save is a create
          - 'services-commerce:docs/milestones/COMM-M1b-promotions-and-codes.md#R6'
          - >-
            services-commerce:modules/catalog/services/persistence/product-listings.ts:createProductListing
          - >-
            services-commerce:modules/catalog/tests/unit/storefront/storefront-visibility.spec.js#removes
            a draft everywhere, an unlisted listing from the list only, and a published one nowhere
          - >-
            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
          - >-
            services-commerce:modules/catalog/services/persistence/product-listings.ts:updateProductListing
          - >-
            services-commerce:modules/catalog/services/persistence/product-listings.ts:updateProductListing
          - >-
            services-commerce:modules/catalog/services/persistence/product-listings.ts:updateProductListing
          - >-
            services-commerce:modules/catalog/services/persistence/product-listings.ts:updateProductListing
          - >-
            services-commerce:modules/catalog/services/persistence/product-listings.ts:deleteProductListing
          - >-
            services-commerce:modules/catalog/services/persistence/product-listings.ts:deleteProductListing
          - >-
            services-commerce:modules/catalog/services/persistence/product-listings.ts:deleteProductListing
  - id: ATS-COM-CAT-006-18
    title: Managed catalog file lifecycle is exercised across every declared move
    category: transition
    level: api
    proves:
      - BR-CAP-COM-CAT-006-02
      - BR-CAP-COM-CAT-006-08
      - BR-CAP-COM-CAT-006-11
      - BR-CAP-COM-CAT-006-12
      - BR-CAP-COM-CAT-006-13
      - BR-CAP-COM-CAT-006-14
    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-006-19
    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-006-present-a-product-in-the-store-and-publish-it.md#CAP-COM-CAT-006
  - >-
    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
  - >-
    rhythm-v2-docs:specs/commerce/catalog/design/workflows/WF-COM-CAT-005-save-a-merchandise-item-from-the-console-in.md#WF-COM-CAT-005
product_intent: proposed
coverage:
  rule: 'gap: FND-COM-CAT-060'
  transition: covered
  validation: covered
  permission: covered
  edge: 'gap: FND-COM-CAT-060'
  failure: covered
  boundary: covered
  concurrency: covered
  invariant: covered
  interaction: covered
coverage_notes:
  rule: >-
    No complete rule scenario is established by this back-fill; the finding records the missing
    proof.
  edge: >-
    No complete edge 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.product-listings.read
    coverage: 'gap: FND-COM-CAT-060'
    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-060'
  - key: commerce.product-listings.create
    coverage: 'gap: FND-COM-CAT-060'
    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-060'
  - key: commerce.product-listings.update
    coverage: 'gap: FND-COM-CAT-060'
    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-060'
  - key: commerce.product-listings.delete
    coverage: 'gap: FND-COM-CAT-060'
    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-060'
  - key: commerce.product-listings.bulk
    coverage: 'gap: FND-COM-CAT-060'
    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-060'
---

# Prepare and publish product presentations: Capability Test Specification

## Coverage

| Rule | Proof Scenario or Gap |
|---|---|
| `BR-CAP-COM-CAT-006-01` | `ATS-COM-CAT-006-01` |
| `BR-CAP-COM-CAT-006-02` | `ATS-COM-CAT-006-02`, `ATS-COM-CAT-006-12`, `ATS-COM-CAT-006-13`, `ATS-COM-CAT-006-17`, `ATS-COM-CAT-006-18` |
| `BR-CAP-COM-CAT-006-03` | `ATS-COM-CAT-006-03`, `ATS-COM-CAT-006-15` |
| `BR-CAP-COM-CAT-006-04` | `ATS-COM-CAT-006-04`, `ATS-COM-CAT-006-14` |
| `BR-CAP-COM-CAT-006-05` | `ATS-COM-CAT-006-04`, `ATS-COM-CAT-006-14` |
| `BR-CAP-COM-CAT-006-06` | `ATS-COM-CAT-006-05` |
| `BR-CAP-COM-CAT-006-07` | `ATS-COM-CAT-006-05` |
| `BR-CAP-COM-CAT-006-08` | `ATS-COM-CAT-006-06`, `ATS-COM-CAT-006-17`, `ATS-COM-CAT-006-18`, `ATS-COM-CAT-006-19` |
| `BR-CAP-COM-CAT-006-11` | `ATS-COM-CAT-006-07`, `ATS-COM-CAT-006-17`, `ATS-COM-CAT-006-18` |
| `BR-CAP-COM-CAT-006-12` | `ATS-COM-CAT-006-08`, `ATS-COM-CAT-006-17`, `ATS-COM-CAT-006-18` |
| `BR-CAP-COM-CAT-006-13` | `ATS-COM-CAT-006-10`, `ATS-COM-CAT-006-17`, `ATS-COM-CAT-006-18` |
| `BR-CAP-COM-CAT-006-14` | `ATS-COM-CAT-006-11`, `ATS-COM-CAT-006-17`, `ATS-COM-CAT-006-18`, `ATS-COM-CAT-006-19` |

## Interactions With Related Capabilities

Unlisted products remain publicly quotable. Merchandise view reflects saved storefront pricing. Moving a listing between shelves changes membership order. Publication changes list and direct-page visibility. A file replacement changes which download can be requested. An unlisted page retains public quoting while a draft does not. The combined item screen saves product and presentation separately. 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.
