Files
SlabUploader/.audit/archive/2026-09-18-reconciliation-decisions.md
Ty 01aa4c52c3 docs(phase2): reconcile requirements and migrate capabilities/contracts
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/.
2026-09-18 21:02:06 -07:00

14 KiB
Raw Permalink Blame History

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.