diff --git a/README.md b/README.md index 4a09d45..d14751a 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,46 @@ # SlabUploader Mobile-first, self-hosted app for publishing one-of-a-one wood slabs to WooCommerce. -Field capture (offline) → deterministic measurement/pricing/normalization → publish a -single slab to the store as a pending product. +Phone capture (online) → hybrid measure/crop on device → review → WooCommerce create. Owner: Ty (Whidbey). Store: www.whidbeywoodstore.com. Deploy: .201 (Caddy, Docker). ## Status -**Planning complete — ready for implementation.** All 20 product decisions locked -(PRD "Decisions log", 2026-08-24). Build plan in `docs/IMPL-PLAN.md` (phases 0–6). +**POC requirements freeze in progress (Gate A, 2026-08-31).** Read `AGENTS.md` before +any implementation. TECH-SPEC + ARCHITECTURE beat stale PRD/README lines. Do **not** +treat older “planning complete / ready for implementation” wording as a green light +to code the obsolete server-pipeline Phase 0. + +POC bar: phone → customer-ready listing on the real store; UAT creates as **Woo draft** +with `SLAB-UAT-*` SKUs; one real SKU **Woo publish** after Ty review. Online-only for POC +(offline deferred). Build plan: `docs/IMPL-PLAN.md` (Phase 0 = hybrid Gates A+B). ## Documentation | File | What | |---|---| -| `Slab capture app PRD.txt` | Requirements + locked decisions (source of truth for "what") | -| `docs/ARCHITECTURE.md` | Services, client/server split, offline PWA, data flow | -| `docs/TECH-SPEC-PIPELINE.md` | Deterministic math + image pipeline, constants, 9 test vectors | -| `docs/DATA-MODEL.md` | SQLite schema + status state machine | -| `docs/OPENAPI.md` | Client↔server API contract (deliverable #2) | +| `AGENTS.md` | Working contract + hard rules (read first) | +| `Slab capture app PRD.txt` | Requirements + decisions (intent; superseded lines called out in AGENTS) | +| `docs/ARCHITECTURE.md` | Hybrid client/server split, data flow | +| `docs/TECH-SPEC-PIPELINE.md` | Deterministic math + image pipeline, constants, test vectors | +| `docs/DATA-MODEL.md` | SQLite schema + slab status state machine | +| `docs/OPENAPI.md` | Client↔server API contract | | `docs/CONTENT-WOO.md` | Content templates + WooCommerce payload/integration | -| `docs/PROMPTS.md` | Call 1 vision prompt + Call 2 content prompt (server files) | -| `docs/DEPLOYMENT.md` | Compose, Caddy, backup/restore, key rotation (deliverable #4) | +| `docs/PROMPTS.md` | Vision + content prompts (server files) | +| `docs/DEPLOYMENT.md` | Compose, Caddy, backup/restore, key rotation | | `docs/IMPL-PLAN.md` | Phased build plan with acceptance criteria + exit gates | -| `AGENTS.md` | Working contract + hard rules for anyone implementing | ## Quick orientation -- **Deterministic core** (measurements, pricing, normalization, Woo sync) is pure, - network-free, and unit-tested. Inference (species/character + optional content) is - the *only* scoped-LLM surface, and it's toggleable — the app fully works without it. -- **Offline-first PWA**: capture works with no network; drafts + photos persist in - IndexedDB and sync when online. +- **Hybrid:** phone owns mask/sliders/sqft/bdft/3:4 PNG; server stores, Woo, inference proxy, on-demand U2Net. +- **Deterministic core** is unit-tested and network-free. Inference is toggleable; app works with it off. +- **Online-only for POC** (offline PWA deferred). - **Two containers** (`frontend`, `fastapi`) + Caddy. Single SQLite + image volumes. -- **v1 non-goals**: no versioning, no staff/roles, no bulk publish, no analytics, - no update path (create-only, SKU-deduped). +- **v1 non-goals:** no versioning, no staff/roles, no bulk publish, no analytics, no SKU update path. ## Repo remotes -- Canonical (source of truth): Gitea `Ty_Tech/SlabUploader` on **gitea-atd** (`https://gitea.vps1.afterthedemo.com/Ty_Tech/SlabUploader`) +- Canonical SoT: Gitea `Ty_Tech/SlabUploader` on **gitea-atd** (`https://gitea.vps1.afterthedemo.com/Ty_Tech/SlabUploader`) - Optional later mirrors: `gitea-truenas`, GitHub — not SoT for this workstream ## Getting started (implementer) -1. Read `AGENTS.md` (hard rules) and `docs/IMPL-PLAN.md` (phases). -2. Start at Phase 0 — scaffold + the tested deterministic pipeline. -3. Don't skip the exit gates; Ty signs each phase. +1. Read `AGENTS.md` (hard rules) and Phase 0 in `docs/IMPL-PLAN.md`. +2. Start at Phase 0 — hybrid client TECH-SPEC modules + thin FastAPI health/settings (not a server happy-path pipeline). +3. Do not skip exit gates; Ty signs each phase via the delivery process. diff --git a/Slab capture app PRD.txt b/Slab capture app PRD.txt index 9e361c6..4d85944 100644 --- a/Slab capture app PRD.txt +++ b/Slab capture app PRD.txt @@ -16,6 +16,13 @@ publish pipeline. - Images are purged after successful publish (retained only while draft/retry). - Deploy target: .201, Docker Compose + Caddy. +## Decisions log (2026-08-31, POC freeze via CoS/PPM/Dev Manager) +- Doc contradiction freeze: TECH-SPEC + ARCHITECTURE win over stale PRD/IMPL-PLAN lines. +- AGENTS.md added; bdft = sqft × thickness_in; secrets server AES-GCM; online-only POC; + woo_create_status draft|publish (default draft); SLAB-UAT-* force Woo draft on PROD; + hybrid client happy-path; inference OFF for first claimable publish, Call 1/2 after. +- See `/workspace/slabuploader-poc-review-package.md` review package (not in repo) and AGENTS.md. + ## Decisions log (2026-08-27, Ty) 1. No calibration photos — user manually enters SKU, length, thickness. 2. First photo is the measurement photo (must be top-down, lens parallel to broad side). @@ -32,10 +39,13 @@ publish pipeline. 10. Call 1 runs automatically after calibration **only if** inference is enabled. User reviews/corrects before triggering Call 2. 11. Inference timeout >45s or non-200 → error with retry. User can fill fields manually. -12. Woo product status set to "published" by default (configurable in settings). +12. Woo create status: Settings key `woo_create_status` = draft | publish, default **draft**. + UAT on PROD uses Woo draft + SKU prefix SLAB-UAT-* (forced draft). Final POC: one real + SKU → Woo publish after Ty review. (Supersedes older "published by default" / pending smoke.) 13. Inference prompts are server-side text files, read fresh on each invocation. 14. Brand voice + GEO context injected into Call 2 prompt. -15. WooCommerce credentials (username + App Password) stored client-side in settings. +15. WooCommerce credentials stored server-side only (AES-GCM in SQLite via Settings UI). + Never in the browser. (Supersedes older client-side storage decision.) 16. Inference endpoint: configurable OpenAI-compatible API (user provides base URL, key, model). 17. Test connection verifies text completion + vision support. 18. Offline capture not required for MVP. @@ -97,7 +107,8 @@ publish pipeline. - **FR2:** User manually enters SKU (3-24 chars, [A-Z0-9-]), length (in), thickness (in). - **FR3:** App detects background color from image borders (green or black sheet). - **FR4:** First photo: BG removal - find longest dimension - compute scale - (longest_px / user-length) - compute area (sqft) - bdft = sqft * thickness / 12. + (longest_px / user-length) - compute area (sqft) - bdft = sqft * thickness_in + (TECH-SPEC authoritative; the old "/ 12" formula is wrong and must not be implemented). - **FR5:** All photos: BG removal - crop to 80% frame fill - save as transparent PNG, minimum 1600 px on smaller dimension, 3:4 portrait crop (configurable). - **FR6:** User can retake the first photo or abort and start fresh. @@ -169,7 +180,8 @@ publish pipeline. - **FR34:** Publish is blocked if the slab SKU already exists (simple dedupe, no update path). - **FR35:** Create WooCommerce product with SKU, title, description, short description, price, categories, attributes, tags, and normalized PNG images. -- **FR36:** Default product status: "published" (configurable in settings). +- **FR36:** Default Woo create status: "draft" via `woo_create_status` (draft | publish). + SLAB-UAT-* SKUs always create as Woo draft. Real inventory may use publish after Ty review. - **FR37:** Log sync results and surface errors; store WooCommerce product ID on success. ### Drafts, review, and publish @@ -199,12 +211,14 @@ publish pipeline. - SvelteKit (mobile-first) - TailwindCSS for UI - Camera API for capture and immediate preview -- No OCR, no OpenCV on client +- Hybrid client (ARCHITECTURE / TECH-SPEC): capture, sheet/sliders, mask confirm, + length axis, sqft/bdft/widths, 3:4 PNG normalization on the phone. No OCR/ruler + in POC. Offline PWA deferred. **Server (self-hosted, containerized)** -- FastAPI (Python) for deterministic image pipeline, bdft math, pricing, - background removal, crop/normalize, WooCommerce REST integration, - inference wrappers, taxonomy sync +- FastAPI (Python): draft store, on-demand U2Net ("try harder"), WooCommerce REST, + inference proxy, taxonomy sync, AES-GCM settings. Server does **not** own the + happy-path measure/crop pipeline (client is math SoT). - SQLite for v1 (slab records, pricing rules, taxonomy cache, sync history) **Inference** @@ -235,8 +249,9 @@ updated_at: timestamp 1. New slab: user stages slab and opens app. 2. Capture: take 1-5 inventory photos. 3. Enter metadata: SKU, length, thickness. -4. Calibration: server processes first photo (BG removal, area, bdft), - processes all photos (BG removal, crop, PNG). +4. Calibration: on phone — BG removal + sliders, confirm mask, confirm length axis, + enter length/thickness/SKU; client computes sqft/bdft/widths and 3:4 PNGs. + Optional server U2Net "try harder" on one photo if mask fails. 5. Inference: Call 1 auto-runs (vision), populating taxonomy/content. User reviews/corrects. Call 2 user-triggered (text), generating title/description/short description. diff --git a/docs/CONTENT-WOO.md b/docs/CONTENT-WOO.md index bf9e81b..086798c 100644 --- a/docs/CONTENT-WOO.md +++ b/docs/CONTENT-WOO.md @@ -92,7 +92,7 @@ Failure → `502 woo_error`; previous cache retained (never delete-then-fail). { "name": "{title}", "type": "simple", - "status": "pending", // test-target decision: NEVER "publish" automatically + "status": "{woo_create_status}", // draft | publish; default draft; SLAB-UAT-* forced draft "sku": "{sku}", "regular_price": "{price}", // 2 dp string, e.g. "2730.00" "description": "{description}", @@ -115,9 +115,11 @@ For reliability the server: 2. Reference those `source_url` values in `images` (≤5, per PRD 1–5 inventory photos). 3. Calibration photos are excluded (FR2). -**Status = `pending`** by default (test-target decision: test products must never go -live without human action). The Settings key `woo_publish_status` (default `pending`) -controls this; Ty flips it to `publish` only when intentionally going live. +**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 +slab lifecycle status `published` (means Woo create succeeded locally). ### 2.5 Dedupe & idempotency (FR24a) Before create: `GET /products?sku={sku}`. @@ -141,7 +143,7 @@ Before create: `GET /products?sku={sku}`. 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=pending) → woo_product_id +6. POST /products (status=woo_create_status) → woo_product_id 7. write sync_log(success), set slab published, purge images on any failure at 2–6: write sync_log(failed), slab=failed, (images retained for retry) ``` diff --git a/docs/DATA-MODEL.md b/docs/DATA-MODEL.md index fb37423..cfd961c 100644 --- a/docs/DATA-MODEL.md +++ b/docs/DATA-MODEL.md @@ -179,7 +179,7 @@ Keys: - `inference_base_url`, `inference_api_key`, `inference_model` - `inference_enabled`, `content_llm_enabled` - `brand_voice`, `geo_context` -- `publish_status` ('published' | 'draft') +- `woo_create_status` ('draft' | 'publish'), default `draft` — Woo product create status; not slab lifecycle - `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` diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index 5ea4b40..62ea0a3 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -2,6 +2,8 @@ Status: SPEC (deliverable #4) · 2026-08-24 · SlabUploader Target: TyUBUMini at 192.168.1.201. Domain: `slab.tyubumini.local` (LAN/Tailscale + +> Gate A freeze (2026-08-31): hybrid client owns measure/crop SoT. Do not scaffold a server ruler/pipeline as authoritative. Woo UAT = draft + SLAB-UAT-* on PROD. only — Decision). Inference is NOT on .201; it's a remote configurable endpoint (.202 or elsewhere). @@ -39,7 +41,7 @@ SlabUploader/ └── backend/ # FastAPI app (Phase 0) ├── app/ │ ├── main.py - │ ├── pipeline/ # deterministic core (constants.py, ruler, geometry, widths, pricing, normalize) + │ ├── (no happy-path pipeline SoT here) # hybrid: TECH-SPEC modules live in frontend/ or shared pure TS; backend = store/Woo/inference/U2Net stub │ ├── woo/ # Woo client, taxonomy, publish │ ├── inference/ # vision + content wrappers (toggleable) │ ├── prompts/ # call1.txt, call2.txt (shipped defaults) @@ -146,12 +148,12 @@ provide `python -m app.security.rekey NEW_KEY` (reads old key from env | Reset one slab | delete its photos dir + `UPDATE slabs SET status='draft'` (admin script) | ## 8. Smoke test (after every deploy) -1. `GET /api/health` → ok -2. Settings: Woo creds configured + `test-woo` ok +1. `GET /api/health` → ok (from phone over HTTPS once CA trust is proven) +2. Settings: Woo creds + `test-woo` ok; `woo_create_status=draft`; inference OFF for early gates 3. Create a slab → upload test photos (fixture set) → poll to `ready` -4. Verify bdft/price match the fixture's expected values (golden test) -5. Publish test slab → `published`, Woo product visible as **pending** in store admin -6. Delete/leave the pending test product as a staging artifact (never published) +4. Verify bdft/price match the fixture's expected values (golden test; bdft = sqft × thickness) +5. Publish test slab with SKU `SLAB-UAT-…` → slab may show local `published`; Woo product must be **draft** +6. Leave or delete the Woo draft UAT product (never customer-visible). Final live publish is a non-UAT SKU after Ty review. ## 9. Scaling path (not v1) - Postgres when SQLite write contention appears (schema already portable). diff --git a/docs/OPENAPI.md b/docs/OPENAPI.md index 53f8498..e0cc730 100644 --- a/docs/OPENAPI.md +++ b/docs/OPENAPI.md @@ -126,7 +126,7 @@ the call returns. WooCommerce is the authoritative source for `feat-*` definitio "inference_model": "", "inference_enabled": true, "content_llm_enabled": false, "brand_voice": "...", "geo_context": "...", - "publish_status": "published", + "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, @@ -139,7 +139,7 @@ Secrets (keys) are never returned. Only a `*_configured` boolean. ## Endpoints ### Health & meta -- `GET /api/v1/health` → `{ "status":"ok", "version":"...", "woo_reachable": bool }` +- `GET /api/health` → `{ "status":"ok", "version":"...", "woo_reachable": bool }` (canonical deploy/smoke path; may also be exposed as `/api/v1/health`) - `GET /api/v1/meta` → `{ "version":"...", "templates_configured": bool }` ### Slabs