---
id: STATE-COM-CAT-003
title: Listing publication
object: DOM-COM-CAT-003
product_area: commerce
module: catalog
revision: 1
status: draft
design_intent: confirmed
implementation_evidence: observed
source_sha:
  services-commerce: c8b66a98e2490617aec38ba5c8165dd9bd0a8f9b
  react-console: baf6dae382d6f41dce976c7f848667ccc37e076f
states:
  - name: no listing
    meaning: No public presentation exists for this product.
  - name: draft
    meaning: 'The presentation is being prepared and cannot be purchased publicly, though staff sales remain possible.'
  - name: published
    meaning: Admitting active stores include the product in their shelf lists.
  - name: unlisted
    meaning: The product is omitted from lists but available by direct page link for public quote and purchase.
transitions:
  - 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.'
    side_effects:
      - The asset pipeline prepares files and the listing transaction attaches them.
    who: Listing administrator (create 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
  - 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.
    side_effects:
      - The save prepares and attaches its assets.
    who: Listing administrator (create permission)
    provenance: documented
    source: 'services-commerce:docs/milestones/COMM-M1b-promotions-and-codes.md#R6'
  - from: no listing
    to: unlisted
    trigger: Creation saves a presentation available only by link.
    guard: Creation authority and listing validation must pass.
    side_effects:
      - The save prepares and attaches its assets.
    who: Listing administrator (create permission)
    provenance: observed
    source: 'services-commerce:modules/catalog/services/persistence/product-listings.ts:createProductListing'
  - from: draft
    to: published
    trigger: Replacement requests publication.
    guard: 'Replacement requires update authority, the current version and no full protection.'
    side_effects:
      - The next shelf read includes the product where the assortment admits it.
    who: Listing administrator (update permission)
    provenance: test
    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
  - from: published
    to: unlisted
    trigger: Replacement requests link-only visibility.
    guard: 'Replacement requires update authority, the current version and no full protection.'
    side_effects:
      - List reads exclude the product while direct-page access remains.
    who: Listing administrator (update permission)
    provenance: test
    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
  - from: unlisted
    to: published
    trigger: Replacement restores publication.
    guard: 'Replacement requires update authority, the current version and no full protection.'
    side_effects: []
    who: Listing administrator (update permission)
    provenance: observed
    source: 'services-commerce:modules/catalog/services/persistence/product-listings.ts:updateProductListing'
  - from: published
    to: draft
    trigger: Replacement returns a published presentation to preparation.
    guard: 'Replacement requires update authority, the current version and no full protection.'
    side_effects:
      - Public presentation is withdrawn.
    who: Listing administrator (update permission)
    provenance: observed
    source: 'services-commerce:modules/catalog/services/persistence/product-listings.ts:updateProductListing'
  - 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.'
    side_effects:
      - The public direct page becomes unavailable.
    who: Listing administrator (update permission)
    provenance: observed
    source: 'services-commerce:modules/catalog/services/persistence/product-listings.ts:updateProductListing'
  - 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.'
    side_effects:
      - Direct-page access becomes available.
    who: Listing administrator (update permission)
    provenance: observed
    source: 'services-commerce:modules/catalog/services/persistence/product-listings.ts:updateProductListing'
  - from: draft
    to: no listing
    trigger: Staff remove the draft listing.
    guard: Deletion authority is required and neither protection level may block removal.
    side_effects:
      - Listing asset references are released for cleanup.
    who: Listing administrator (delete permission)
    provenance: observed
    source: 'services-commerce:modules/catalog/services/persistence/product-listings.ts:deleteProductListing'
  - from: published
    to: no listing
    trigger: Staff remove the published listing.
    guard: Deletion authority is required and neither protection level may block removal.
    side_effects:
      - Listing asset references are released for cleanup.
    who: Listing administrator (delete permission)
    provenance: observed
    source: 'services-commerce:modules/catalog/services/persistence/product-listings.ts:deleteProductListing'
  - from: unlisted
    to: no listing
    trigger: Staff remove the link-only listing.
    guard: Deletion authority is required and neither protection level may block removal.
    side_effects:
      - Listing asset references are released for cleanup.
    who: Listing administrator (delete permission)
    provenance: observed
    source: 'services-commerce:modules/catalog/services/persistence/product-listings.ts:deleteProductListing'
invalid: []
concurrency: >-
  The listing and shelf membership replacement share a versioned transaction. Asset preparation precedes that
  transaction; files prepared by a losing save await cleanup.
invariants:
  - INV-DOM-COM-CAT-003-01
  - INV-DOM-COM-CAT-003-04
  - INV-DOM-COM-CAT-003-05
evidence:
  - >-
    services-commerce at c8b66a98: modules/catalog/db/product-listings.table.ts (LISTING_STATES);
    services/persistence/product-listings.ts; services/storefront/storefront-visibility.ts;
    docs/milestones/COMM-M1-set-up-what-you-sell.md (D8, D-REC-7).
---

# Listing publication

## The Lifecycle

```mermaid
stateDiagram-v2
  [*] --> no_listing
  no_listing --> draft: first save in draft
  no_listing --> published: first save published
  no_listing --> unlisted: first save unlisted
  draft --> published
  published --> unlisted
  unlisted --> published
  published --> draft
  unlisted --> draft
  draft --> unlisted
  draft --> no_listing: delete
  published --> no_listing: delete
  unlisted --> no_listing: delete
```

## Transitions Explained

No listing: No public presentation exists for this product. Draft: The presentation is being prepared and cannot be purchased publicly, though staff sales remain possible. Published: Admitting active stores include the product in their shelf lists. Unlisted: The product is omitted from lists but available by direct page link for public quote and purchase.

## What Cannot Happen

Any listing visibility can be replaced with another permitted visibility, including saving a draft as draft. Attempting to create a second listing is a uniqueness refusal rather than a lifecycle move.

## Concurrency and Timing

The listing and shelf membership replacement share a versioned transaction. Asset preparation precedes that transaction; files prepared by a losing save await cleanup.
