01aa4c52c3
Surface-matrix sweep of every source doc; added nine missed behaviors (METADATA-115, REVIEW-50, APP-55/60/65, SETTINGS-45 mode, Generate-text availability, defaults-off). 153 requirements, 0 coverage gaps. Migrated capabilities to docs/capabilities/ and contracts to docs/contracts/; added WOO-API, API, DATA-MODEL, PROMPTS, UX, TEST-SPEC. Populated MILESTONES; branch-neutral trunk docs; renamed umbrella term to Woo Slab Taxonomy. Archived reconciliation working records under .audit/archive/.
212 lines
14 KiB
Markdown
212 lines
14 KiB
Markdown
# Requirements reconciliation — decision record
|
||
|
||
- Date: 2026-09-18
|
||
- Branch: `docs/clean-main`
|
||
- Participants: Ty + opencode
|
||
- Status: working record. Gets distilled into `docs/REQUIREMENTS.md`, then retired.
|
||
|
||
This captures the decisions made while consolidating the scattered requirements, so
|
||
context is not lost between sessions. Loose ends are collected in section 16.
|
||
|
||
## 0. Resume here (for a new session)
|
||
|
||
**SUPERSEDED 2026-09-18. Read `.audit/2026-09-18-session-handoff.md` instead.**
|
||
|
||
This section is kept only so the history below still reads correctly. The current state,
|
||
locked rulings, gate output, and next steps all live in the handoff. Everything below this
|
||
point is the decision log from the original reconciliation, not the current resume point.
|
||
|
||
## 1. Why this exists
|
||
|
||
Requirements are scattered across layers of docs (old `main` specs, current branch docs,
|
||
`archive/`), with duplicates and contradictions. Goal: one clean `main` holding all shipped
|
||
code plus one reconciled requirement set, with M3 scoped as a subset of it. Coding is paused
|
||
until that exists.
|
||
|
||
## 2. Git state established
|
||
|
||
1. `pre-simplification` tag created at `13bfa792bb3ed968b333675c85bcec0c451e2635` (old main tip; retains the deleted specs: ARCHITECTURE, IMPL-PLAN, OPENAPI, DATA-MODEL, TEST-SPEC, UX, UX-DESIGN, TECH-SPEC-PIPELINE, DEPLOYMENT, PROMPTS, Build-release-standard).
|
||
2. `main` fast-forwarded to `d754cac` (the M1>M2>M3 code line). `main` now holds all shipped code plus current docs.
|
||
3. Local `m3-ds` created, tracking `origin/m3-ds`.
|
||
4. Phase 1 scaffold committed as `b42e3cb` on branch `docs/clean-main`.
|
||
5. Nothing pushed. `origin/main` still at the old tip until we decide to push.
|
||
|
||
## 3. Process rules adopted
|
||
|
||
1. Coding is paused until the requirement set is reconciled.
|
||
2. Requirements live only in `docs/REQUIREMENTS.md`, one row per requirement, with stable IDs.
|
||
3. Milestones are expressed as explicit subsets of requirement IDs (`docs/MILESTONES.md`).
|
||
4. Conflict resolution rule: current/later docs plus the code that actually runs beat the tag and `archive/`. "Typically later docs prevail."
|
||
5. The structure is enforced by `tools/check_requirements.py` (registered docs, unique IDs, traceability).
|
||
6. Verification discipline: no "done" without raw evidence; CI is the only mechanical gate.
|
||
|
||
## 4. Namespaces (owner jobs, not tech layers)
|
||
|
||
1. `CAPTURE` — ecommerce-ready pics: intake, pick calibration photo, BG removal, crop, axis, length/width, calculations, inventory PNG, dimension overlay, lineup.
|
||
2. `METADATA` — building the listing: attributes and tags, title, short/deep description, the two inference calls.
|
||
3. `REVIEW` — confirm and correct: confidence display, field gating, tweaks, per-slab price suggestion and override, ready gate.
|
||
4. `PUBLISH` — WooCommerce create, draft/live, duplicate SKU, cleanup.
|
||
5. `SETTINGS` — configured before the session: Woo connection, inference config, species `$/bdft` rates, brand/geo.
|
||
6. `APP` — foundation: device draft, slab status, resume, server seams, secret handling.
|
||
7. `OPS` — foundation: deploy, hosts, release, UAT.
|
||
8. `QUALITY` — foundation: test obligations, vectors, anti-metrics.
|
||
|
||
## 5. Capture decisions
|
||
|
||
1. Mask controls are exactly two: `Sheet` (auto/green/black) and `Sensitivity` (0–100). Edge offset and Feather are dropped. The Sheet toggle itself is a candidate for removal (auto-detect stays).
|
||
2. Sensitivity changes are debounced 2000 ms; a Sheet change runs an immediate pass.
|
||
3. The owner uploads all photos of the slab, then selects one as the calibration photo. That photo drives every downstream step, is duplicated with dimensions added, and becomes the first showcase image.
|
||
4. Crop is 90% fill, shorter side at least 800 px when the source allows (shipped values beat the old 80%/1600).
|
||
5. Length and thickness are the only user-set numbers. Widths, sqft, and bdft are computed from the approved mask and are immutable in production (no typed override).
|
||
6. Widths are sampled every 6 inches along the approved axis, dropping the first and last 6 inches.
|
||
7. Thickness band uses the round-up model: measured `1"` lands in the `1–1.5"` store band.
|
||
8. Woo image order is locked: clean calibration PNG, then dimension-overlay copy, then remaining batch photos in the order added. Minimum two images per listing; no upper cap.
|
||
|
||
## 6. Ready set (submission-ready)
|
||
|
||
Required:
|
||
|
||
1. At least two images — clean first pic plus its dimension-overlay copy.
|
||
2. `sku` matching `[A-Z0-9-]{3,24}`.
|
||
3. `length_in` and `thickness_in`.
|
||
4. Widths / sqft / bdft (computed, immutable).
|
||
5. `categories` — exactly one species leaf.
|
||
6. `categories` — at least one wood-category leaf.
|
||
7. `attributes[Edge Type]` — one term.
|
||
8. `attributes[Figure]` — at least one term.
|
||
9. `attributes[Grade]` — exactly one term.
|
||
10. `attributes[Thickness]` — one term (round-up band).
|
||
11. `attributes[Moisture]` — one term, default kiln-dried.
|
||
12. `regular_price`.
|
||
13. `name`, `short_description`, `description`.
|
||
|
||
Optional: all Woo product tags (figure family and features family).
|
||
|
||
Woo schema field mapping lives in section 6 of this record and will become the `METADATA` requirements.
|
||
|
||
Note: title / short description / description are required on the product. Because deterministic templates are the inference-off fallback, they are guaranteed non-empty at create.
|
||
|
||
## 7. Taxonomy and categories
|
||
|
||
1. Categories sync from Woo; the product is assigned to leaf categories. Parents are never assigned but matter in context: the site shows them (breadcrumbs/filters) and Call 2 receives the parent path as context.
|
||
2. Species is exactly one synced leaf. Wood category is one or more synced leaves.
|
||
3. In review, the owner designates a primary wood category and any secondary ones. Default primary is the first category selected. How the owner corrects it, and how they stay aware of it, is a UX problem deferred to design.
|
||
4. Primary drives the title. Primary plus secondary drives the description prose.
|
||
5. The primary leaf selects a specialized Call 2 prompt. This is the "common pattern."
|
||
6. Wood-category leaves: Knife blanks, Specialty cuts, Dimensional wood, Live edge wood, Bowl blanks, logs and caps, Pen blanks, Pepper mill blanks. All are settled prose/prompt work items except `Specialty cuts`, which is parked.
|
||
7. All Woo product tags are optional. Tags come from the synced Woo set; no hardcoded names or prefixes. Wrinkle: one figure tag is bare `fig` (Figured), so a `fig-*` glob would drop it — the filter must be data-driven.
|
||
8. Current tags. Features: `feat-mincr` (Crack/Check minor), `feat-resin` (Resin Ready). Figure: `fig` (Figured), `fig-brdseye`, `fig-chatoy`, `fig-crotch`, `fig-curl`, `fig-fback`, `fig-flame`, `fig-hfig`, `fig-marbled`, `fig-quilted`, `fig-splt`.
|
||
|
||
## 8. Locked attributes
|
||
|
||
1. `attributes[Edge Type]` — 1 term.
|
||
2. `attributes[Figure]` — at least 1 term; includes `plain` for basic slabs; never left empty.
|
||
3. `attributes[Grade]` — exactly 1 term.
|
||
4. `attributes[Thickness]` — 1 term, round-up band.
|
||
5. `attributes[Moisture]` — 1 term, default kiln-dried, never inferred.
|
||
6. Missing attributes or terms are skipped, never created. v1 does not invent taxonomy.
|
||
|
||
## 9. Text generation (Call 2)
|
||
|
||
1. Call 2 produces `name`, `short_description`, and `description`.
|
||
2. The template is a consistency/style guide in the prompt; the model has creative license. It is not assembled deterministically in code.
|
||
3. With inference off, the deterministic template is the fallback that fills those three fields.
|
||
4. Call 2 receives all metadata confirmed or changed in review — not the raw Call 1 suggestions.
|
||
5. Dimensions are supplied as a literal fact block (species, figure term, length, thickness, average width, width min–max, board feet). The model uses those exact strings and invents no numbers.
|
||
6. There is no `L × W × T` rectangle assumption; slabs are irregular and width varies.
|
||
7. The dimension-overlay image is not sent to the model.
|
||
8. A post-check for number drift is parked as `later`; if it is ever added it is plain deterministic comparison, not an AI call.
|
||
9. The title separator is a hyphen: ` - `.
|
||
|
||
## 10. Inference transport
|
||
|
||
1. Calls are asynchronous: the request returns `202`, the client polls and shows phase feedback.
|
||
2. Phases: uploading, analyzing, still working. Polling is the heartbeat that distinguishes dead connectivity from a working model.
|
||
3. Tunable defaults: ack 15 s, upload-stall 20 s, overall inference deadline 120 s after ack, poll every 2 s backing off to 10 s.
|
||
4. On timeout or failure, fail gracefully with a plain message and a Retry action. Retry is offered only if `test-inference` previously passed. A retry replaces any in-flight job (idempotent).
|
||
5. Inference input is the clean processed image (masked, cropped), composited on soft white, no alpha, downscaled to at most 1024 px long edge, JPEG quality 80.
|
||
6. Only the pixel cap is exposed in Settings. JPEG quality 80 lives in a server config file.
|
||
7. Call 1 and Call 2 both receive the images. Call 2 additionally receives the literal fact block.
|
||
8. Provider threads are dropped; always full resend to the provider.
|
||
9. Inference never blocks create and never auto-publishes.
|
||
|
||
## 11. Prompt management
|
||
|
||
1. Prompts are stored in SQLite as immutable revisions, seeded from bundled repo defaults on first run.
|
||
2. Saving appends a revision; an active pointer selects which revision is live.
|
||
3. Reset to default re-activates the bundled default; reset to a revision re-activates that revision.
|
||
4. Each inference call records the revision id/hash it used.
|
||
5. A runtime debug flag gates both the Advanced settings UI and the prompt-write endpoints. Single build, not two.
|
||
6. This is post-M3 (`later`).
|
||
|
||
## 12. Pricing
|
||
|
||
1. A flat `$/bdft` rate per species is configured in Settings, matched to a synced species leaf. No tiers in v1.
|
||
2. The per-slab suggestion is `bdft × rate`; the owner may override and the override sticks. No rule means an empty price the owner types.
|
||
3. The price field and its suggestion live in the review screen.
|
||
|
||
## 13. Publish
|
||
|
||
1. Create sequence is the documented one: validate the ready set, look up the SKU (stop if it exists), refresh taxonomy if older than one hour, resolve categories/tags/attributes, upload inventory PNGs, `POST /products`, then finalize.
|
||
2. On success the local slab row is deleted. WooCommerce is the only source of truth; `sync_log` is retained; photos are purged; the device draft is cleared.
|
||
3. `SLAB-UAT-*` always creates a Woo draft.
|
||
4. A duplicate SKU blocks create; there is no update path.
|
||
5. A stale value on submit is a field-level error telling the owner to pick from the current list.
|
||
|
||
## 14. Settings
|
||
|
||
1. Compose env: `WOO_BASE_URL` (https only), `SLAB_AES_KEY`.
|
||
2. Encrypted in SQLite: `woo_wp_username`, `woo_app_password`, `inference_base_url`, `inference_api_key`.
|
||
3. Plain settings: `inference_model`, `inference_enabled`, `content_llm_enabled`, `woo_create_status` (default draft), `brand_voice`, `geo_context`.
|
||
4. Derived: `taxonomy_last_sync_at`, `taxonomy_anchor`.
|
||
5. Auth is a WordPress application password for a dedicated low-privilege user; not consumer keys, not the login password.
|
||
|
||
## 15. Scope mapping
|
||
|
||
Shipped: M1 Capture, M2 Photos, capture knob prefs.
|
||
|
||
M3: store-connection and AI-connection settings; taxonomy sync; the five attributes; Call 1; Call 2; title/description generation; review screen with field gating; pricing; publish; local draft cleanup.
|
||
|
||
Later: prompt-management UI, U2Net, offline capture, OCR/ruler, batch create, number-drift check, Specialty cuts prose, primary-category correction UX.
|
||
|
||
## 16. Open / parked items
|
||
|
||
1. `attributes` element shape on `POST /products` is open — needs a live Whidbey product sample or the official Woo REST fields. Do not invent.
|
||
2. Server persistence approach is open — full OpenAPI slab CRUD vs a thinner FastAPI path.
|
||
3. `Specialty cuts` leaf prose is parked.
|
||
4. Primary-category correction and awareness is a UX problem, deferred.
|
||
5. Number-drift check is `later`.
|
||
6. Prompt management is post-M3.
|
||
7. The QUALITY/OPS extraction re-run is still pending (the first pass was truncated).
|
||
8. "Later docs prevail" is the rule, but the tag/archive still hold material not yet reconciled (e.g. U2Net, browser floor, TES vectors, deploy details).
|
||
|
||
## 17. Work items
|
||
|
||
1. ~~Re-run the QUALITY/OPS extraction.~~ Done 2026-09-18 (TEST-SPEC ~110 rows; plan/deploy/release ~120 rows).
|
||
2. Write `docs/REQUIREMENTS.md` namespace by namespace, starting with `CAPTURE`.
|
||
3. Draft the per-leaf title/prose work once the `METADATA` section is being written.
|
||
4. Migrate capability/contract docs into their homes, make docs branch-neutral, rebuild the structure check, wire CI.
|
||
|
||
## 18. Rulings deferred to a final cleanup pass
|
||
|
||
These surfaced during the QUALITY/OPS extraction. They do not block writing `REQUIREMENTS.md`; park them and reconcile in the cleanup pass.
|
||
|
||
1. Test directory layout: old TEST-SPEC mandates Vitest and `frontend/src/__tests__/`; shipped code uses the Node test runner (`node --import tsx --test`) and `frontend/tests/`. Contradiction.
|
||
2. Test coverage target (~95% for deterministic modules) — adopt or drop.
|
||
3. "The Test Bar" role and who authors reports versus Ty's sign-off.
|
||
4. `docs/checks/check-structure.sh` gate timing and criteria — now replaced by `tools/check_requirements.py`.
|
||
5. Deploy ports: proposal `18080/18443` versus UAT `:10881`, and `APP_HOSTNAME` binding.
|
||
6. Backup retention "7 daily + 4 weekly" has no verification test.
|
||
7. `SLAB_AES_KEY_OLD` rekey env placement (host vs compose).
|
||
8. Smoke step 5: local `published` versus Woo `draft` mismatch — intended or not.
|
||
9. Release process: `Build-release-standard` points at GitHub while the repo is Gitea.
|
||
10. Taxonomy sync endpoint path divergence across docs.
|
||
11. Ownership of `deploy/INVENTORY.md` across Gate B vs Phase 6.
|
||
12. Scope labels: do shipped/M3/later map to phases or to release milestones.
|
||
13. `C1`/`C2` (Call 1 / Call 2) namespace ownership ambiguity.
|
||
14. Whether `UAT-PRACTICE.md` change control is a gate.
|
||
15. Definition of "real fixtures" acceptance for TV-3, TV-6, TV-7, TV-10.
|
||
16. `INF-1` ownership: SETTINGS toggle versus QUALITY suite obligation.
|
||
17. Server-side API surface (`revision_conflict`, validation module, slab CRUD) belongs to the open server-persistence decision.
|
||
18. `output_px_min` "never below 1600" from old DEPLOYMENT/IMPL-PLAN versus shipped 800 floor.
|