# Milestone 3 — Create Builds on Milestones 1–2 (`docs/CAPTURE.md`, `docs/PHOTOS.md`). Destination and field names also live in `docs/PRODUCT.md`. **Scope source of truth:** this file and the Ty conversations that wrote it. Task and milestone breakdowns on `main` (including older PRODUCT / IMPL-PLAN / CONTENT-WOO splits) are **obsolete for M3**. Do not cross-reference `main` for what M3 includes once this branch is the working set. ## Goal Phone path from Photos-ready device draft → store-synced **Woo attributes** filled and reviewed → WooCommerce product create (practice SKUs as draft) → clear device draft. M3 also wires the **OpenAI-compatible inference endpoint** as a server dependency so inference ops can run when enabled. Create must still work with inference off. ## Woo attributes Locked name for the synced taxonomy package. WooCommerce is the source of truth; the app caches ids and never invents the lists. ### Categories (product categories, leaf-only) | Field | Cardinality | Notes | |---|---|---| | Species | exactly 1 required | Synced species leaves only; no hardcoded / fallback set | | Wood category | 1+ required | Parents are grouping only; picks are leaves | ### Attributes (exactly five locked Woo attributes) | Attribute | Cardinality | Notes | |---|---|---| | Edge Type | 1 required | Term id from sync | | Figure | 1+ required | Term ids from sync | | Grade | 1+ required | Term ids from sync | | Thickness | 1 required | Measured thickness rounded **up** into the store band | | Moisture | 1 required | Default kiln-dried; user sets/keeps; **never inferred** | v1: missing attributes are skipped, not created. Species, character, length, and free-text thickness are **not** product attributes. ### Tags | Tag prefix | Cardinality | Notes | |---|---|---| | `fig-*` | 1+ required | Other Woo tags ignored | | `feat-*` | 0+ optional | Character / inclusions / voids / checks map onto existing feat tags only | ### Listing facts beside taxonomy - User: SKU, length, thickness (and app-computed widths / sqft / bdft from M1/M2) - Price: propose `bdft × species $/bdft` from pricing rules; user may override; missing rate → empty price - Copy: title / short title / description from templates (optional Generate text when inference is on; not required for ready) ## Sync (Woo) - FastAPI is the sole Woo client. Credentials never in the browser. - Auth: WordPress **application password** for a dedicated low-privilege WP user (not Woo `ck_`/`cs_` consumer keys). - Store URL: `WOO_BASE_URL` from `deploy/.env` / compose (`https://` only). Settings shows it read-only. Not a SQLite settings row. - Woo username + application password: Settings UI → AES-GCM in SQLite (`SLAB_AES_KEY` env). - Taxonomy sync pulls, in order: categories → tags (`fig-*` / `feat-*` only) → attributes + terms (five locked). - Sync failure keeps the previous cache (never delete-then-fail). Stale picks show “no longer valid”; never silently swap. ## Inference dependency - OpenAI-compatible endpoint: `inference_base_url`, API key, model in **Settings** (encrypted), not compose env. - Server proxies all inference calls. - Feature-flagged. **Create / ready path must pass with inference off** (manual taxonomy + deterministic numbers + Woo draft). - Vision may prefill taxonomy at ≥0.7 confidence when on; typed values always win; below threshold → leave empty. ## Ready set (submission ready) Review does not continue until the full ready set is filled. Species + one figure is not enough. Required: - ≥1 inventory PNG (from M2) - SKU, length, thickness; widths / sqft / bdft present - Species, ≥1 wood category, edge type, ≥1 figure, ≥1 grade, thickness band, moisture, ≥1 `fig-*`, price - `feat-*` optional (0+) ## Create - `POST` product to Woo with categories, attributes, tags, price, copy, inventory PNG images. - Duplicate SKU (any store status) blocks create; no update path in this version. - `SLAB-UAT-*` always creates as Woo **draft**, even if Settings say publish. - Device draft ≠ Woo draft. After successful create, clear the device draft. - Default create status from Settings `woo_create_status` (`draft` \| `publish`, default `draft`), subject to the UAT force-draft rule. ## Acceptance (done when) Product state induced: overnight UAT app on ubudual5060 `http://100.64.0.9:10881` (or the current M3 deploy of this branch), with Woo credentials and `WOO_BASE_URL` pointed at the intended store. Evidence from that state: 1. Taxonomy sync via application password retrieves Woo attributes into the app cache; pickers offer only synced ids. 2. A Photos-ready slab can be filled to the full ready set using those synced lists (inference off is a required path). 3. Create with `SLAB-UAT-*` yields a WooCommerce **draft** product whose categories / attributes / tags / price / images match the reviewed ready set. 4. Duplicate SKU is blocked; device draft clears after success. 5. Inference Settings accept an OpenAI-compatible base URL / key / model; create still succeeds with inference disabled. ## Dishonest proxies (do not count as pass) - Hardcoded or fixture taxonomy lists treated as “synced” - Mock Woo client or in-process fake that never hit the real store - Claiming ready / create green from unit tests alone without a real draft in the store - Consumer-key auth instead of application password - Inference “wired” only as dead config with no server proxy path, or create that requires inference on - Touching `:10880` (Ty Node/SvelteKit) or dumping work onto `m2-ds` / doc-simplification branches ## Out of scope (unless Ty expands M3 in chat) - Full Assist UX beyond the OpenAI endpoint dependency and optional prefill rules above - Creating new Woo attributes / terms / tags from the app - Update-in-place of an existing store SKU - Bulk create