docs(m3): rewrite CONTENT-WOO for clarity

The file repeated requirement IDs on nearly every line, carried defensive
negations, mixed 'description' with 'long description', and left a cross
reference and the payload shape incomplete.

- Cite requirement IDs once in the Requirements impact table.
- Name the seven leaves as the synced WooCommerce wood-category leaves.
- Keep the length ft/in split with the short-description pattern it serves.
- Use 'description' only, matching the Woo field.
- Complete the cross references with section pointers.
- Follow the Woo v3 product shape and add an example submission whose
  taxonomy values are placeholders, since the synced cache supplies them.
This commit is contained in:
2026-09-19 13:15:38 -07:00
parent 5a210c47b0
commit 2d12cbcfbd
+68 -66
View File
@@ -1,66 +1,48 @@
# Listing copy and payload intent # Listing copy and payload intent
Status: SPEC · 2026-09-18. Status: SPEC · 2026-09-18.
Owns how listing copy is produced and the intent of the create payload. The wire shape is Owns how listing copy is produced and the intent of the create payload.
`docs/contracts/WOO-API.md`; the create sequence is `docs/contracts/API.md`; prompts are
`docs/contracts/PROMPTS.md`.
## Requirements impact ## Requirements impact
| Requirement | Impact on this design | | Requirement | Impact on this design |
|---|---| |---|---|
| REVIEW-10, REVIEW-15, REVIEW-40, REVIEW-50 | required prose, primary category, editability, limits | | METADATA-5, METADATA-10, METADATA-15, METADATA-30, METADATA-55, METADATA-60, METADATA-65, METADATA-66, METADATA-70, METADATA-75, METADATA-80, METADATA-85, METADATA-90, METADATA-95, METADATA-110 | taxonomy, copy production, Call 2 inputs, inference-off path, primary category, dimension fact block, logging, non-blocking |
| METADATA-55, METADATA-60, METADATA-65, METADATA-66 | Call 2, inputs, dimension safety, logging | | REVIEW-10, REVIEW-15, REVIEW-40, REVIEW-50 | prose fields, primary category, editability, field limits |
| METADATA-70, METADATA-75, METADATA-80, METADATA-85, METADATA-90, METADATA-95, METADATA-110 | deterministic copy, inference-off path, pattern selection, fact block, overlay exclusion, control | | PUBLISH-5, PUBLISH-10, PUBLISH-15, PUBLISH-20, PUBLISH-25, PUBLISH-30, PUBLISH-31, PUBLISH-40, PUBLISH-45, PUBLISH-50, PUBLISH-55, PUBLISH-60 | payload fields, draft rule, duplicate SKU, images, published meaning, quantity |
| PUBLISH-5, PUBLISH-10, PUBLISH-15, PUBLISH-20, PUBLISH-25, PUBLISH-30, PUBLISH-31, PUBLISH-40, PUBLISH-45, PUBLISH-50, PUBLISH-55, PUBLISH-60 | payload and create outcomes | | SETTINGS-30, SETTINGS-65 | brand voice, geo context, create status |
| SETTINGS-30, SETTINGS-65 | brand voice and geo context; create status |
| METADATA-10, METADATA-15, METADATA-30 | categories, attributes, tags on the payload |
## 1. Copy sources The wire shape is `docs/contracts/WOO-API.md`; the create sequence is `docs/contracts/API.md`; prompts are `docs/contracts/PROMPTS.md`.
Three fields, three sources (`REVIEW-10`): ## 1. The three copy fields
1. **name** — assembled deterministically by the server from the primary wood category's title The product carries `name`, `short_description`, and `description` (`docs/contracts/WOO-API.md` §3).
pattern and the dimension fact block (`METADATA-80`). The model does not write it.
2. **short_description** — assembled deterministically as a spec line from the primary wood
category's short-description pattern (`METADATA-80`). The model does not write it.
3. **description** — written by Call 2 when it runs (`METADATA-55`), from the inputs in
`METADATA-60`; with inference off the server produces it (`METADATA-70`).
The primary wood category selects the title pattern, the short-description pattern, and the Call 2 `name` and `short_description` are built by the server from the primary wood category's patterns and the dimension fact block. `description` is written by Call 2, or by the server when inference is off (`METADATA-80`, `METADATA-70`).
prompt (`METADATA-85`, `REVIEW-15`). The owner can edit any field until create (`REVIEW-40`), and
every field respects the limits in `docs/contracts/WOO-API.md` §9 (`REVIEW-50`).
The model writes the long description only, from the fact block; it never invents a dimension The primary wood category is the one the owner declares in review. It selects the title pattern, the short-description pattern, and the Call 2 prompt (`METADATA-85`, `REVIEW-15`).
(`METADATA-65`, `METADATA-90`). The dimension-overlay image is never sent (`METADATA-95`). The
Generate text control appears only when content generation is enabled and the endpoint is The owner can edit all three fields until create. Length limits are in `docs/contracts/WOO-API.md` §9 (`REVIEW-50`).
reachable (`METADATA-110`); a failed generation never blocks create (`METADATA-75`). Every
generation is logged (`METADATA-66`).
## 2. Deterministic title and short description ## 2. Deterministic title and short description
Both patterns are assembled by the server and use only synced taxonomy terms and capture facts The patterns interpolate the dimension fact block with `{field}` placeholders. They read only the synced taxonomy terms and the capture facts (`METADATA-80`, `METADATA-90`).
(`METADATA-80`). Placeholders use `{field}`; every number comes from the deterministic pipeline,
never the model (`METADATA-90`).
The shape follows the convention a working mill store uses: descriptor-led, dimension-light titles, The form is the convention a working mill store uses: descriptor-led, dimension-light titles, with the measurements carried in the short description. See "Provenance" below.
with the measurements carried separately. See "Provenance" below.
### 2.1 Title ### 2.1 Title
Slot order:
``` ```
[Figure] {species} [Live Edge] {noun} [(dims)] [Figure] {species} [Live Edge] {noun} [(dims)]
``` ```
1. `Figure` leads as a Title-Case adjective and is omitted when the Figure term is `plain`. 1. `Figure` leads as a Title-Case adjective.
2. Grade is not used in the title. 2. Grade is not a title slot.
3. `Live Edge` appears only when the Edge Type term is Live Edge. 3. `Live Edge` appears for leaves where the edge type is part of the product form.
4. There is no separator between slots. 4. Slots run together with no separator.
5. Dimensions ride in the title only for the turning leaves, as a trailing `(L x W x T)`. 5. Turning leaves carry `(L x W x T)` at the end; slab, lumber, and cap leaves carry no dimensions.
Per-leaf patterns: The seven leaves below are the WooCommerce wood-category leaf terms. They come from the synced taxonomy only (`METADATA-5`, `METADATA-10`); parents group and are not assigned.
| Leaf | Pattern | Example | | Leaf | Pattern | Example |
|---|---|---| |---|---|---|
@@ -74,16 +56,18 @@ Per-leaf patterns:
`Specialty cuts` has no M3 pattern. `Specialty cuts` has no M3 pattern.
`length_ft = floor(length_in / 12)`, `length_in_remainder = length_in mod 12` (1 dp). An unset Figure drops its slot; the title starts at `{species}`.
### 2.2 Short description ### 2.2 Short description
One spec line, so the measurements appear even when the title omits them: One spec line, carrying the measurements the title omits:
``` ```
[Figure] {species} {noun}, {length} x {width} x {thickness}, {bdft} bdft [Figure] {species} {noun}, {length} x {width} x {thickness}, {bdft} bdft
``` ```
The line expresses length in feet and inches, so it splits `length_in`: `{length_ft}ft {length_in_remainder}in`, where `length_ft = floor(length_in / 12)` and `length_in_remainder = length_in mod 12`, to 1 dp. The title uses the same split for its length slot.
| Leaf | Example | | Leaf | Example |
|---|---| |---|---|
| Live edge wood | `Figured Black Walnut Live Edge Slab, 9ft 6in x 24.3in x 1.75in, 18.4 bdft` | | Live edge wood | `Figured Black Walnut Live Edge Slab, 9ft 6in x 24.3in x 1.75in, 18.4 bdft` |
@@ -92,36 +76,54 @@ One spec line, so the measurements appear even when the title omits them:
### 2.3 Description ### 2.3 Description
The long description is the model's prose when Call 2 runs (`METADATA-55`); with inference off the Call 2 writes the `description` using the specialized prompt selected by the primary wood category (`METADATA-85`). The prompt receives the processed photos, the review-confirmed metadata, the primary and secondary wood categories, the parent-category path, the dimension fact block, and brand/geo context (`METADATA-60`). With inference off the server produces the `description` (`METADATA-70`). The field carries HTML, as WooCommerce expects on `description` (`docs/contracts/WOO-API.md` §3).
server produces it (`METADATA-70`). The per-leaf prose templates that guide Call 2 are authored
with that slice and are not defined here.
### 2.4 Provenance ### 2.4 Provenance
The title and short-description form was modeled on the live product titles of Cook Woods The title and short-description form was modeled on the live product titles of Cook Woods (cookwoods.com), captured 2026-09-19. Borrowed: descriptor-led titles; dimension-free titles for slabs, lumber, and caps; parenthetical dimensions only for turning stock; no separator; title case. Not borrowed: their size-class nouns (`Mini Slab`, `Table`, `Micro Lumber`, `Long`) and pack counts (`5 Pack`), which have no synced taxonomy term, and their grade adjectives.
(cookwoods.com), captured 2026-09-19. Borrowed: descriptor-led titles; dimension-free titles for
slabs, lumber, and caps; parenthetical dimensions only for turning stock; no separator; title case.
Not borrowed: their size-class nouns (`Mini Slab`, `Table`, `Micro Lumber`, `Long`) and pack counts
(`5 Pack`), which have no synced taxonomy term, and their grade adjectives, which are not used in
our titles.
## 3. Payload intent ## 3. Payload intent
The exact wire shape is `docs/contracts/WOO-API.md`. Intent: The exact wire shape is `docs/contracts/WOO-API.md` §3–§5. Intent:
- Only non-null fields are sent (`PUBLISH-55`). 1. Only non-null fields are sent (`PUBLISH-55`).
- The payload carries an explicit `slug`: the sanitized product name plus the lowercased SKU. 2. The payload carries an explicit `slug`: the sanitized product name plus the lowercased SKU (`PUBLISH-55`).
Dimensions never appear in the slug (`PUBLISH-55`). 3. `status` comes from `woo_create_status` (default draft); a `SLAB-UAT-*` SKU creates a draft (`PUBLISH-10`, `SETTINGS-65`).
- `status` comes from `woo_create_status` (default draft); `SLAB-UAT-*` always draft 4. Categories are the species leaf plus each wood-category leaf; attributes are the five locked attributes by synced term; tags are the fig-* and feat-* families (`METADATA-10`, `METADATA-15`, `METADATA-30`).
(`SETTINGS-65`, `PUBLISH-10`). 5. Images are the processed transparent PNGs in photos-ready order, with no cap (`PUBLISH-40`, `PUBLISH-45`).
- Categories are the species leaf plus each wood-category leaf; attributes are the five locked 6. `sold_individually` is true; each product is one slab (`PUBLISH-60`).
attributes by synced term; tags are fig-* and feat-* (`METADATA-10`, `METADATA-15`, 7. Create checks the SKU first and stops on a duplicate; a stale value is a field-level error (`PUBLISH-15`, `PUBLISH-20`).
`METADATA-30`). Missing attributes or terms are skipped, never created (`METADATA-20`). 8. Success purges photos and deletes the local row; failure retains photos and writes `sync_log`, including the orphaned-media cleanup note (`PUBLISH-25`, `PUBLISH-30`, `PUBLISH-31`). Published means the create succeeded, not that the listing is live (`PUBLISH-50`).
- Images are the processed transparent PNGs in photos-ready order, with no cap (`PUBLISH-40`,
`PUBLISH-45`). ### 3.1 Example submission
- `sold_individually` is true; each product is one slab (`PUBLISH-60`).
- Create checks the SKU first and blocks on a duplicate (`PUBLISH-15`); a stale value is a The structure below is the WooCommerce product shape this app sends. Woo ids and term names come from the synced taxonomy at runtime; the values shown are illustrative.
field-level error (`PUBLISH-20`). Success purges photos and deletes the local row
(`PUBLISH-25`); failure retains photos and logs (`PUBLISH-30`), including the orphaned-media ```json
cleanup note (`PUBLISH-31`). Published means the create succeeded, not that it is live {
(`PUBLISH-50`). "name": "Figured Black Walnut Live Edge Slab",
"type": "simple",
"status": "draft",
"sku": "<SKU>",
"slug": "figured-black-walnut-live-edge-slab-<lowercased sku>",
"regular_price": "<bdft x species rate, 2 dp>",
"description": "<p>Prose written by Call 2.</p>",
"short_description": "Figured Black Walnut Live Edge Slab, 9ft 6in x 24.3in x 1.75in, 18.4 bdft",
"categories": [{ "id": "<species leaf id>" }, { "id": "<wood-category leaf id>" }],
"tags": [{ "id": "<fig-* tag id>" }, { "id": "<feat-* tag id>" }],
"images": [{ "id": "<attachment id>" }],
"attributes": [
{ "id": "<Edge Type id>", "position": 0, "visible": true, "variation": false, "options": ["<edge term>"] },
{ "id": "<Figure id>", "position": 1, "visible": true, "variation": false, "options": ["<figure term>"] },
{ "id": "<Grade id>", "position": 2, "visible": true, "variation": false, "options": ["<grade term>"] },
{ "id": "<Thickness id>", "position": 3, "visible": true, "variation": false, "options": ["<thickness term>"] },
{ "id": "<Moisture id>", "position": 4, "visible": true, "variation": false, "options": ["<moisture term>"] }
],
"virtual": false,
"downloadable": false,
"catalog_visibility": "visible",
"sold_individually": true
}
```
Only non-null fields are sent, so a listing without `feat-*` tags omits them from `tags`, and each `attributes` element carries a single `options` value.