Catalog
What renders a product list and a product detail page on this baseline, and the single missing artefact that makes both render empty.
What the baseline guarantees
Layers: surface-swift 1.5.1 (the pages), base 3.2.1 (the shop), feature-* (the 8 products).
Shipped storefront catalogue surfaces (each is a Swift-v2_App paragraph with
moduleSystemName `eCom_ProductCatalog`):
Shop replace/_content/Swift 2/Shop — grid-row-1, 1Column, container
Grid, paragraph Swift-v2_App bound to
IndexQuery /Files/System/Repositories/ProductsFrontend/Products.query
FacetGroups /Files/System/Repositories/ProductsFrontend/Products.facets
PageSize 12, ProductListTemplate ProductListRenderGrid.cshtml,
ProductTemplate ProductDetailRenderGrid.cshtml,
QueryConditions default IsVariant=False,
UseGroupSortInGroupContext 0
Shop/Product List, Shop/Product Details the PLP/PDP compositions
Product Components/ Product Info (right side), Product List, Product List Card
Desktop Header search, Express Buy, Kit Configurator — all bind the same repository path.
Shop page URL provider: ShopUrlDataProvider, IncludeFrom=ContextShop,
Include=GroupsProductsVariants, GroupPath=Tree, ProductPath=Tree.
THE GAP, and it is deliberate: **no layer in this Distribution ships the `ProductsFrontend`
repository.** base.contract.json `repositories.provisionedByGate` is `false`. The files
(Products.index / Products.query / Products.facets) arrive with the HOST's Swift 2.4.0 design
package under wwwroot/Files/System/Repositories/ProductsFrontend/. On a host without them,
every PLP answers **HTTP 200 with an in-page Lucene "numHits must be > 0" error, never a
500** — so a status-code assert passes on a dead catalogue. Gate the PLP on a product count
and `dw-error == 0`, never on HTTP status (base.contract.json repositories.note).
Not present by design: zero product images (ShopImageFolder points at /Images/Products, which
holds nothing), zero variants, zero assortments, zero feeds. Which skill covers it
dw-commerce-catalog (dynamo: true) — references/catalog-publishing.md (619 lines), references/listing-and-stock.md. dw-search-indexing (dynamo: true) — references/query-authoring.md and references/query-expressions.md own the .query the PLP binds.
Which MCP tools brand it
create_products -> assign_products_to_group -> patch_products_safe -> import_product_images_from_urls / upload_product_images -> set_product_primary_image -> save_prices -> create_or_update_product_queries -> build_product_index -> wait_for_product_index -> verify_product_visibility Assortment scoping, when the customer needs it — the ordered recipe at dw-commerce-b2b/SKILL.md:89-99: save_assortments (active) -> assign_products_to_assortment / assign_groups_to_assortment -> assign_shops_to_assortment -> assign_permissions_to_assortment -> build_assortments (get_assortments_for_build confirms nothing is pending) -> wire the storefront (the catalog page's index query must filter on AssortmentIDs) -> check_assortment_product_access for a representative user + product
Verify
Storefront: GET <site-root>/shop — assert 200 AND a non-zero product-card count AND zero occurrences of `dw-error` and of `numHits must be > 0`. Rendered-rows rule (dw-commerce-catalog/SKILL.md:241): assert rendered product rows EQUAL the listing's own header count. A header count that exceeds the rows it renders is the standing signal that the index counts something the entity fetch drops. MCP: get_products_by_query against the ProductsFrontend query returns > 0, and verify_product_visibility on one known ProductId reports it visible in SHOP1. File-level: read_file on /Files/System/Repositories/ProductsFrontend/Products.query must succeed. If it 404s, the host's Swift design-package overlay never landed and no amount of index rebuilding will fix the PLP.