chore(openspec): archive the mock-migration waves and green the spec set
Archive the three completed changes behind replace-mock-api-wave-1/2/3. Each merge applied cleanly to the main specs: - catalog gains the Public product browse requirement (subtree filtering and price sort) - frontend-mall picks up the per-domain adapter, the pinned home page, the discovery-page changes, the live auth panels and the live transaction flows - cart's Server-side cart requirement now documents the shop and stock carried by every line Also replace the TBD Purpose placeholder in all eleven specs with a one-line description of what each capability covers. Those placeholders predate this work and were the only reason `openspec validate --all --strict` reported 0 passed / 11 failed; it now reports 11 passed / 0 failed.
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
# catalog Specification
|
||||
|
||||
## Purpose
|
||||
TBD - created by archiving change catalog-i18n-currency. Update Purpose after archive.
|
||||
The public product and category catalogue: localized content, publish lifecycle, shop isolation, SKU pricing and public browse.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: Localized product content
|
||||
Product and category names/descriptions SHALL be stored as JSONB maps keyed by locale (`en`, `zh`). The API MUST return the full map; clients pick the display locale.
|
||||
|
||||
@@ -37,3 +39,21 @@ Each SKU SHALL carry `price_minor` (integer minor units) and an ISO `currency` c
|
||||
- **WHEN** upserting a SKU with stock < 0
|
||||
- **THEN** the API returns 400
|
||||
|
||||
### Requirement: Public product browse
|
||||
Public `GET /api/products` SHALL return only `published` products whose shop is active, and SHALL remain readable without authentication. When `category_id` is supplied, the filter SHALL match that category **and every category beneath it**, so requesting a parent category returns products assigned to its child and grandchild categories. The listing SHALL accept an optional `sort` of `price` together with an `order` of `asc` or `desc`, ordering by each product's lowest active SKU price; any other `sort` value SHALL be rejected with a 400 `ApiError` rather than silently ignored. An unsorted listing SHALL order newest first. Paging SHALL keep returning `page` and `per_page` alongside the filtered `total`.
|
||||
|
||||
#### Scenario: parent category includes descendant products
|
||||
- **WHEN** a shopper requests products for a category that has child categories holding published products
|
||||
- **THEN** the response contains the products assigned to those descendant categories, not only those assigned directly to the requested category
|
||||
|
||||
#### Scenario: sort by lowest active SKU price
|
||||
- **WHEN** a shopper requests the product list with `sort=price` and `order=asc`
|
||||
- **THEN** products come back ordered by their lowest active SKU price ascending
|
||||
|
||||
#### Scenario: unsupported sort is rejected
|
||||
- **WHEN** a client requests a `sort` value that is not `price`
|
||||
- **THEN** the API responds 400 with an `ApiError` body instead of ignoring the parameter
|
||||
|
||||
#### Scenario: unpublished products never appear
|
||||
- **WHEN** any public listing or filter is applied
|
||||
- **THEN** products that are not `published`, or whose shop is not active, are absent from both `items` and `total`
|
||||
|
||||
Reference in New Issue
Block a user