docs: factual CONTENT-WOO/DATA-MODEL fixes + hybrid Phases 3-5

CONTENT-WOO: PNG media, fig-*/feat-* tags, five locked attributes,
AGENTS-cited Woo safety. DATA-MODEL/OPENAPI drop server sheet prefs
and PWA wording. ARCHITECTURE status reflects locked hybrid topology.
IMPL Phases 3-5 rewritten for hybrid (all-photos@1024, confidence 0.7,
no offline-era paths).
This commit is contained in:
2026-09-01 05:28:03 +00:00
parent 369d404c29
commit 3ac5e36dc3
6 changed files with 100 additions and 86 deletions
+6 -3
View File
@@ -1,9 +1,12 @@
# Architecture
Status: SPEC · 2026-08-27 · SlabUploader
Locked so far: topology C (hybrid), swimlane, what-runs-where. Remaining sections TBD.
Status: SPEC · hybrid topology locked at Gate A freeze (2026-08-31) · SlabUploader
Companion: PRD (root), TECH-SPEC-PIPELINE.md.
Normative for hybrid split, swimlane, what-runs-where, secrets **why**, and status
transitions. Ops depth lives in DEPLOYMENT. Schema enums live in DATA-MODEL.
Agent-facing hard rules live in AGENTS.md (verbatim locks stay there).
Companion: TECH-SPEC-PIPELINE.md. PRD is historical intent only (see AGENTS precedence).
## 1. Topology (hybrid)
+24 -20
View File
@@ -75,15 +75,17 @@ Rebuilds the cache table (delete + insert, single transaction). Returns counts.
Failure → `502 woo_error`; previous cache retained (never delete-then-fail).
### 2.3 Auto-assign taxonomy (FR26)
- **Category**: species value is matched to a Woo category by name (case-insensitive,
trimmed). Match → that category id. No match → the slab's `category_override`
(user picks in review) or empty (Woo required-field validation will surface it).
- **Tags**: `[character]` plus optional `[species]` if present as a tag.
Call 1 feat-* observations are mapped to Woo feat-* tags (e.g., Crack/Check,
Resin Ready). WooCommerce is authoritative for tag definitions.
- **Attributes**: species/character/length/thickness as product attributes if the
corresponding Woo attributes exist in the cache (by name). Missing attributes are
skipped, not created (v1: no attribute creation).
- **Categories**: species leaf category (1 required) plus wood-category leaf ids (1+).
Match by name against the synced Woo category cache (case-insensitive, trimmed).
No match → user override in review, or empty (Woo required-field validation surfaces it).
- **Tags**: `fig-*` only for figure tags (1+ required) and `feat-*` only for feature tags
(0+). Call 1 free-text observations (character / inclusions / voids / checks) map to
existing Woo `feat-*` tags. Do not invent generic character or species product tags.
WooCommerce is authoritative for tag definitions.
- **Attributes**: the five locked Woo attributes only — Edge Type, Figure, Grade,
Thickness, Moisture — using term ids from the cache. Do not publish species, character,
length, or free-text thickness as product attributes. Missing attributes are skipped,
not created (v1: no attribute creation).
- Every assignment is overridable in the review screen (FR26).
### 2.4 Product create payload (FR27)
@@ -98,7 +100,7 @@ Failure → `502 woo_error`; previous cache retained (never delete-then-fail).
"description": "{description}",
"short_description": "{short_title}",
"categories": [ { "id": <species category id> } ],
"tags": [ { "id": <char tag id> }, … ],
"tags": [ { "id": <fig-* tag id> }, { "id": <feat-* tag id> }, … ],
"images": [
{ "src": "<data URL or pre-uploaded attachment URL>", "alt": "{species} {character} slab" }
],
@@ -110,15 +112,17 @@ Failure → `502 woo_error`; previous cache retained (never delete-then-fail).
```
**Image upload strategy** (Woo v3): `images[].src` accepts a URL or a data URL.
For reliability the server:
1. For each customer-facing (inventory, normalized) photo in `seq` order:
`POST /media` with the JPEG bytes → get attachment `source_url`.
2. Reference those `source_url` values in `images` (≤5, per PRD 1–5 inventory photos).
3. Calibration photos are excluded (FR2).
1. For each customer-facing inventory photo in `seq` order (processed transparent PNGs
from the client TECH-SPEC path): `POST /media` with the **PNG bytes** → get attachment
`source_url`. Woo accepts `image/*`; keep alpha. Do not re-encode to JPEG and drop alpha.
2. Reference those `source_url` values in `images` (≤5 inventory photos).
3. Only `kind=inventory` photos. Calibration photos do not exist in this design.
**Status** comes from Settings key **`woo_create_status`** (`draft` | `publish`, default
**`draft`**). UAT on PROD uses Woo **draft** plus SKU prefix `SLAB-UAT-*` (hard rule:
`SLAB-UAT-*` always creates as Woo draft even if Settings say publish). Flip to
`publish` only for intentional live inventory after Ty review. Do not conflate with
**`draft`**). Woo **safety policy** (UAT draft default, `SLAB-UAT-*` force draft even when
Settings say publish, no edit-same-SKU, purge originals + processed images after successful
publish) is owned by **AGENTS.md §5**. This file owns payload shape and publish sequence
only. Do not diverge from AGENTS on force-draft. Do not conflate Woo create status with
slab lifecycle status `published` (means Woo create succeeded locally).
### 2.5 Dedupe & idempotency (FR24a)
@@ -142,9 +146,9 @@ Before create: `GET /products?sku={sku}`.
2. dedupe by SKU (GET /products?sku=) → 409 duplicate_sku if exists
3. (re)sync taxonomy if cache older than 1h → best-effort; use existing cache on fail
4. resolve category/tags/attributes (auto-assign + overrides)
5. upload normalized inventory images → media → source_urls
6. POST /products (status=woo_create_status) → woo_product_id
7. write sync_log(success), set slab published, purge images
5. upload processed PNG inventory images → media → source_urls
6. POST /products (status per AGENTS §5 + woo_create_status) → woo_product_id
7. write sync_log(success), set slab published, purge originals + processed images
on any failure at 2–6: write sync_log(failed), slab=failed, (images retained for retry)
```
+10 -4
View File
@@ -4,7 +4,7 @@ Status: SPEC · 2026-08-27 · SlabUploader
Storage: single SQLite file. Schema portable to Postgres.
Conventions:
- IDs: client-generated UUIDv4 for slabs so the PWA can reference a slab before it exists server-side.
- IDs: client-generated UUIDv4 for slabs so the SvelteKit client can reference a slab before it exists server-side.
- Timestamps: UTC ISO-8601 TEXT.
- Soft state via status column; no hard deletes for slabs (audit).
- Money: float64 USD, rounded half-up to cents at write.
@@ -174,20 +174,26 @@ authoritative source for tag definitions; this table caches synced Woo tags.
| `value_enc` | BLOB | AES-GCM ciphertext |
| `updated_at` | TEXT | |
Keys:
Keys (server settings; secrets encrypted at rest):
- `woo_base_url`, `woo_consumer_key`, `woo_consumer_secret`
- `inference_base_url`, `inference_api_key`, `inference_model`
- `inference_enabled`, `content_llm_enabled`
- `brand_voice`, `geo_context`
- `woo_create_status` ('draft' | 'publish'), default `draft` — Woo product create status; not slab lifecycle
- `woo_create_status` ('draft' | 'publish'), default `draft` — Woo product create status key only; safety policy lives in AGENTS.md §5
- `aspect_ratio` ('3:4'), `output_px` (1600), `output_px_min` (1600), `fill_target` (0.80)
- `user_sensitivity`, `user_edge_offset`, `user_feather`, `user_sheet_mode`
Sheet/slider prefs (`sheet_mode`, sensitivity, edge offset, feather) are **not** server
settings keys in POC. Client localStorage is source of truth (AGENTS §4 / ARCHITECTURE §5).
Optional server sync of those prefs is out of scope for POC.
**Prompt files** — not in the settings table. Stored as text files in
`/data/prompts/` (server volume). Read fresh on each invocation. Default
prompts shipped with the app; owner edits directly. A reset-to-default script
restores originals.
This table is the ciphertext **key inventory** and schema only. The never-browser /
AES-GCM / `SLAB_AES_KEY` env policy lives in AGENTS.md §4; flow lives in ARCHITECTURE §5.
AES master key is an env var (`SLAB_AES_KEY`, 32 bytes base64). Never in the DB.
Each `value_enc` = `nonce(12) || ciphertext`.
+57 -55
View File
@@ -1,11 +1,9 @@
# Implementation Plan
Status: SPEC · Gate A freeze 2026-08-31 · hybrid rewrite pass 2026-09-01 · SlabUploader
Status: SPEC · Gate A freeze 2026-08-31 · hybrid rewrite complete 2026-09-01 · SlabUploader
Phase 0 and the Gate C / Phase 1–2 / Phase 6 sections below are authoritative for hybrid
topology. Phases 3–5 still need a full hybrid rewrite in a later docs commit; until then
implement only Phase 0 plus sections marked rewritten here. Do not implement deferred
stubs (offline PWA, OCR, ruler) or any server happy-path measure/crop as SoT.
Phases 0–6 below are hybrid-aligned. Phase 2 remains a deferred stub only. Do not
implement offline PWA, OCR, ruler, or any server happy-path measure/crop as SoT.
The build plan a professional (or a fresh Hermes session) executes against. Each phase
has: goal, deliverables, acceptance criteria (testable), and exit gate. Ordering is by
@@ -149,50 +147,57 @@ No airplane-mode acceptance. No OCR or ruler exit gate in POC.
---
## Phase 3 — Frontend: enrich, review, publish UI
## Phase 3 — Frontend: enrich, review, publish UI (rewritten)
> **Partial hybrid rewrite pending (docs commit 3).** Goal and intent below stay valid.
> Do not reintroduce offline capture, server measure/crop, or a `processing` status.
> Prefer AGENTS + ARCHITECTURE + OPENAPI over any leftover offline-era wording.
Goal: the enriched review screen and publish action, wired to Phase 1 API.
Goal: online review + publish UI on top of Gate C calibrated drafts. Hybrid only.
Client already owns mask/numbers/PNGs. This phase does not re-measure on the server.
Deliverables:
- Review screen: all fields editable (dimensions, species/character with confidence
+ manual override, price with recommendation, content with "Generate text" when
inference is on, client-processed PNGs).
- Species/character inference UI (calls server; shows confidence; manual confirm below
threshold). Call 1 uses all inventory photos @1024 when inference is enabled.
- Publish button (online-only), status polling, success/failure states, error detail.
- Settings UI: Woo creds + test, inference endpoint + test (vision check),
pricing rules editor, brand voice/GEO. Prompts are server files, not UI settings.
- Review screen after calibrated upload: all fields editable (length, thickness, SKU,
widths/sqft/bdft shown as client-computed with manual override paths, species and
wood categories, five attributes, fig-*/feat-* tags, price with species $/bdft
recommendation, title/short/description).
- Show client-processed transparent PNGs (and originals while retained). No server
re-crop UI as SoT.
- Inference UI (only meaningful after Gate C and when `inference_enabled`): Call 1
results with confidence; manual confirm required below threshold **0.7**
(configurable). "Generate text" triggers Call 2; never auto.
- Publish button (online-only), poll `publishing` → `published` | `failed`, surface
error detail from sync_log.
- Settings UI: Woo creds + test, inference endpoint + test (vision check), pricing
rules editor, brand voice/GEO, `woo_create_status`. Sheet/slider prefs stay in
client localStorage. Prompts are server files, not UI settings.
- Secrets never appear in GET settings responses (masked `*_configured` only).
Acceptance:
- Full round-trip in browser: capture → upload → review → edit → publish → local
`published` (against a Woo sandbox/mock in tests; against real store in Phase 6).
- Inference disabled → review screen fully functional with manual species/character.
- Every field is editable and the override persists (asserted in a test).
- Browser round-trip: online capture → mask/axis confirm → calibrated upload →
review → edit → publish → local `published` (Woo mock in tests; real store Phase 6).
- Inference OFF → full manual taxonomy + content path works; no inference network calls.
- Field overrides persist across poll/refresh (asserted).
- No offline/IndexedDB requirement. No OCR/ruler UI.
Exit gate: Ty publishes a test slab to the real store as a **Woo draft** (`SLAB-UAT-*`) product.
Exit gate: Ty publishes a test slab to the real store as a **Woo draft** (`SLAB-UAT-*`).
(Gate A lock: not pending/published for UAT.)
---
## Phase 4 — WooCommerce integration (real)
## Phase 4 — WooCommerce integration (rewritten)
> **Partial hybrid rewrite pending (docs commit 3).** Payload and safety locks already
> match AGENTS. Keep `woo_create_status` + `SLAB-UAT-*` force-draft. No server happy-path
> image pipeline as SoT.
Goal: production Woo sync, taxonomy, dedupe, logging (FR24–28).
Goal: production Woo sync, taxonomy, dedupe, logging. Browser never calls Woo.
Media bytes are client-processed **PNGs**. Safety policy from AGENTS §5.
Deliverables:
- Woo client (httpx, Basic auth, timeouts), taxonomy sync, auto-assign, media upload,
product create via `woo_create_status` (default **draft**; `SLAB-UAT-*` forces draft),
dedupe, sync_log.
- Woo client (httpx, Basic auth, timeouts), taxonomy sync into cache tables, auto-assign
per CONTENT-WOO §2.3 (categories + five attributes + fig-*/feat-* tags only).
- Media upload of processed PNG inventory photos; product create via `woo_create_status`
(default **draft**). If SKU matches `SLAB-UAT-*`, force Woo **draft** even when
Settings say publish.
- Dedupe by SKU before create; `409 duplicate_sku`; no edit-same-SKU path in MVP.
- `sync_log` with payload_hash; on success set local `published`, purge originals +
processed images on server.
- `POST /api/admin/taxonomy/sync` against the real store.
- Integration tests against a Woo mock (respx) for success + error + duplicate paths.
- Partial-failure handling (orphaned media noted, per CONTENT-WOO §2.6).
@@ -200,8 +205,8 @@ Deliverables:
Acceptance:
- Real UAT publish: product appears in store admin as **draft** under `SLAB-UAT-*`,
correct SKU/price/images/category/tags; `woo_product_id` stored; `sync_log` written
with payload_hash.
correct SKU/price/PNG images/category/tags/attributes; `woo_product_id` stored;
`sync_log` written with payload_hash.
- Duplicate SKU publish → 409, no second product created.
- Woo error (bad creds / 500) → slab `failed` with detail; retry path works.
- All Woo integration tests green (mock + one live smoke).
@@ -210,33 +215,30 @@ Exit gate: Ty verifies a real **Woo draft** `SLAB-UAT-*` product in the store is
---
## Phase 5 — Inference (vision + optional content)
## Phase 5 — Inference (vision + optional content) (rewritten)
> **Partial hybrid rewrite pending (docs commit 3).** Sequencing lock stands: inference
> OFF until Gate C is green; Call 1/2 still inside POC after Gate C. Call 1 = all photos
> @1024 (not top-down-only). Confidence threshold 0.7 (configurable) lives here.
Goal: scoped, toggleable inference with guardrails. Starts only after Gate C is green.
Goal: scoped, toggleable inference **after Gate C is green**, still inside POC.
Does not gate publish. Full suite must pass with inference OFF.
Deliverables:
- Vision wrapper: species/character (and related taxonomy) from **all inventory photos**
downscaled to 1024 via the configurable OpenAI-compatible endpoint; model constrained
to Woo taxonomy cache; returns confidence (threshold **0.7**, configurable); logs
request/response.
- Content wrapper: Call 2 reads `prompts/call2.txt`; LLM writes prose only,
dimensions injected by deterministic templates. See `docs/PROMPTS.md`.
- `test-inference` endpoint (vision capability check).
- All inference code behind feature flags; suite passes with everything OFF.
- Call 1 (vision) proxy: **all inventory photos** downscaled to 1024 + Woo taxonomy
snapshot + user metadata; OpenAI-compatible endpoint; model constrained to cached
taxonomy; confidence threshold **0.7** (configurable; relocate from older PRD FR22a).
Auto-run on calibrated create only if `inference_enabled`. Not top-down-only.
- Call 2 (text) proxy: never auto. User taps Generate text. LLM prose only; templates
inject deterministic numbers. See `docs/PROMPTS.md`.
- `test-inference` endpoint (vision capability check) before Call 2 is allowed.
- Feature flags; `inference_log` audit rows; prompt files read fresh each call.
- No numeric/dimension invention by the LLM. LoRA deferred.
Acceptance:
- With a local vision model configured: species/character suggestion appears with
confidence; low confidence → manual confirm required.
- With inference OFF: full app works, tests green (explicit test asserts no network
calls to the inference endpoint).
- Inference payloads stored for audit (asserted present after a call).
- Call 1 prompt file edits survive server restart (read fresh each invocation).
- Local vision model configured: taxonomy suggestions appear with confidence; below
0.7 → manual confirm required.
- Inference OFF: app works end-to-end; tests assert zero calls to the inference endpoint.
- Audit payloads present after a call; prompt file edits apply without server restart.
- Gate C path remains green with inference OFF.
Exit gate: Ty runs species detection on 3 real slabs; results acceptable or manually
corrected (that is the design).
+2 -3
View File
@@ -128,11 +128,10 @@ the call returns. WooCommerce is the authoritative source for `feat-*` definitio
"brand_voice": "...", "geo_context": "...",
"woo_create_status": "draft",
"aspect_ratio": "3:4", "output_px": 1600, "output_px_min": 1600,
"fill_target": 0.80,
"user_sensitivity": 0.5, "user_edge_offset": 0, "user_feather": 1,
"user_sheet_mode": "auto" }
"fill_target": 0.80 }
```
Secrets (keys) are never returned. Only a `*_configured` boolean.
Sheet/slider prefs are client localStorage only in POC (not in SettingsView).
---