Kestrel Fastening Systems KESTREL FASTENING SYSTEMS

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.