docs: Gate A freeze — AGENTS.md + hybrid Phase 0

Lock hybrid topology, Woo draft+SLAB-UAT-* UAT firebreak, client-authoritative
TECH-SPEC math, and sequenced inference after Gate C. Supersede obsolete
server-pipeline Phase 0 and flag Phase 2 offline/OCR/ruler as out of POC scope.
This commit is contained in:
Dev Manager
2026-09-01 03:59:07 +00:00
parent a94d81d8ce
commit 457711bd78
2 changed files with 201 additions and 21 deletions
+148
View File
@@ -0,0 +1,148 @@
# AGENTS.md — SlabUploader
Working contract for coding agents. Read before writing code.
Status: **Gate A freeze** · signed 2026-08-31 PT · SoT: Gitea `Ty_Tech/SlabUploader` on **gitea-atd** (not mirrors).
---
## 0. Doc precedence (hard)
When docs disagree, use this order until stale docs are rewritten:
1. **`docs/TECH-SPEC-PIPELINE.md`** — deterministic math, image prep, constants, test vectors
2. **`docs/ARCHITECTURE.md`** — hybrid topology, what-runs-where, secrets, status machine
3. This **`AGENTS.md`** — hard rules for agents
4. **`docs/DATA-MODEL.md`**, **`docs/OPENAPI.md`**, **`docs/CONTENT-WOO.md`**, **`docs/PROMPTS.md`**, **`docs/DEPLOYMENT.md`**
5. **`docs/IMPL-PLAN.md`** — Phase 0 hybrid rewrite is authoritative; Phases 1+ TBD rewrite — **do not implement old Phase 0–2** (server happy-path pipeline, offline PWA, OCR, ruler)
6. **PRD / README** — product intent only; do **not** implement lines that contradict TECH-SPEC or ARCHITECTURE
Known stale / do-not-implement without rewrite:
- PRD FR4 `bdft = sqft × thickness / 12` → **wrong**. TECH-SPEC: `bdft = sqft × thickness_in` (no `/12`).
- PRD decision 15 “creds client-side” → **wrong**. Server AES-GCM only; never browser storage for secrets.
- PRD / README “server does normalization / offline-first PWA / OCR / ruler” → superseded by hybrid + online-only MVP.
- IMPL-PLAN Phase 0–2 text that describes server `pipeline/` CLI, ruler, offline PWA, Tesseract, OpenCV.js ruler → obsolete (Phase 0 rewritten 2026-08-31).
- CONTENT-WOO / DEPLOYMENT smoke “pending” as default create → superseded: UAT uses **Woo draft** + `SLAB-UAT-*`.
---
## 1. Topology (locked — hybrid)
**Client (SvelteKit) owns the happy path:**
- Capture 1–5 photos
- Sheet detect + chroma-key / black threshold + sliders (sensitivity, edge offset, feather)
- Mask overlay + **user confirm** before measurement
- Length axis overlay (rotate / confirm)
- sqft, bdft, 6" width samples
- 3:4 transparent PNG, 80% fill at extremes, shorter side ≥ 1600 when source allows
- Draft payload: originals + processed PNGs + length/thickness/SKU/sqft/bdft/widths
**Server (FastAPI) owns:**
- Store drafts / settings / taxonomy cache
- **U2Net on demand only** (“Try harder” / coverage failure) — one photo in, mask out; sliders still apply on client
- WooCommerce (taxonomy sync, SKU check, create product) — browser never calls Woo
- Inference **proxy** (Call 1 / Call 2) — keys never in browser
- Prompt files (server text files, read fresh)
Happy path never leaves the phone until the user continues after confirmed mask + numbers.
---
## 2. What NOT to build (early phases / MVP)
| Deferred / killed for early phases | Notes |
|---|---|
| Offline capture / PWA / IndexedDB sync | Online-only MVP |
| OCR (SKU) | Manual SKU entry |
| Ruler detection / OpenCV on client for scale | Manual length; scale = longest_axis_px / L |
| Server-side happy-path pipeline | No server chroma-key / crop / bdft math as SoT |
| Fake upscale / inventing pixels | Warn + retake if < 1600 after crop |
| LoRA / fine-tune | Deferred |
| Figure-/category-weighted pricing | v1.5 |
| Auto-dimension overlay | v1.5 |
| Bulk publish, staff roles, analytics, SKU update path | Non-goals |
---
## 3. Deterministic core (non-negotiable)
Encode TECH-SPEC exactly:
- **bdft = sqft × thickness_in** (identity: 1 sqft @ 1" = 1 bdft). **No `/12`.**
- Units: inches in 1/8" steps; sqft/bdft 2 decimals; price half-up 2 decimals.
- Manual length, thickness, SKU, price always win.
- Image prep: sheet/sliders → mask confirm → length axis confirm → 3:4 PNG / 80% fill / ≥1600px when source allows.
- **No inventing pixels. No fake upscale.** Undersized source → warn + retake; photo not publishable until retake.
- Pricing: species `$/bdft` from Settings, seeded by Woo species sync; `rec = bdft × price_per_bdft`; user override sticks; missing rule → empty rec, manual allowed.
- Port TECH-SPEC **TV-1…TV-9** (TV-10 only after TECH-SPEC defines it). Client (or shared pure modules the client uses) is authoritative for math; server **stores** client-sent numbers — does not recompute as SoT.
---
## 4. Secrets & settings
**Never in browser storage / JS bundles:**
- Woo consumer key + secret
- Inference base URL + API key
- AES master key (`SLAB_AES_KEY` env only; never in DB or repo)
**Server:** AES-GCM at rest in SQLite for Woo + inference secrets. Settings UI POSTs to FastAPI; server encrypts. All Woo and inference calls are server-side.
**Browser may persist (non-secret):** sheet mode, sensitivity, edge offset, feather (localStorage OK; reset-to-default required).
---
## 5. Woo UAT & create status (Ty locked 2026-08-31)
- Store: **PROD** `www.whidbeywoodstore.com`
- Mid-phase UAT creates: Woo status **draft**, SKU prefix **`SLAB-UAT-*`**
- Settings field **`woo_create_status`**: **`draft | publish`**, default **`draft`**
- Hard rule: if SKU matches `SLAB-UAT-*`, force **Woo draft** even if Settings say publish
- Final POC success: one real (non-UAT) SKU → **Woo publish** only after Ty review
- Duplicate SKU → stop, surface error; no edit-same-SKU in MVP
- After successful publish: purge originals + processed images on server
- Do **not** conflate with slab lifecycle status (below)
---
## 6. Inference (inside POC, sequenced)
- Feature-flagged; **full suite must pass with inference OFF**.
- **Gate C** claimable path: inference **OFF**; manual taxonomy + deterministic numbers + Woo draft path.
- **Call 1 (vision) / Call 2 (text) only after Gate C is green, still inside overall POC** before “done” — not post-POC.
- Call 1 auto only if `inference_enabled`; Call 2 never auto-fires (user taps Generate text).
- LoRA deferred. No numeric/dimension invention by LLM; templates inject deterministic numbers.
---
## 7. Slab status machine (app-internal only)
`draft → calibrated → ready → publishing → published` (or `failed` with detail).
- **calibrated:** mask + axis confirmed; length/thickness/SKU entered; client computed sqft/bdft/widths; draft uploaded.
- **ready:** mandatory fields filled (manual and/or Call 1); inference optional.
- Inference does not gate publish.
- Never confuse slab `draft` with **Woo draft** (`woo_create_status`).
---
## 8. Test & delivery expectations
- No feature without a passing test and shown execution output (PR/commit message).
- Deterministic core unit-tested before UI consumes it.
- One phase = reviewable, shippable increment; Ty exit-gates phases.
- Phase naming: Phase 0–6 stay delivery chapters. **Phase 0 = Gates A+B.** **Gate C = early Phase 1** claimable publish slice.
- Do not write secrets, real Woo keys, or AES keys into the repo.
- Prefer fixing docs when code and locked decisions diverge — do not silently reintroduce `/12`, offline, ruler, or server happy-path pipeline.
---
## 9. Deploy sketch (see DEPLOYMENT.md)
- Target: `.201`, Docker Compose (`frontend` + `fastapi`) + Caddy → `slab.tyubumini.local` (LAN/Tailscale).
- Inventory host ports/Caddy before binding (bolt.diy collision risk).
- Health: `GET /api/health`.
- Inference is a remote configurable endpoint, not on `.201` by default.
+53 -21
View File
@@ -1,6 +1,7 @@
# Implementation Plan
Status: SPEC · 2026-08-24 · SlabUploader
Status: SPEC · Gate A freeze 2026-08-31 · SlabUploader
Phase 0 rewritten for hybrid topology (client-authoritative TECH-SPEC). Phases 1+ still contain stale offline/OCR/ruler/pending language — do not implement those lines; follow AGENTS.md.
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 dependency — do not skip ahead. Definition of done for EVERY phase:
@@ -14,27 +15,55 @@ Ground rules (from AGENTS.md):
---
## Phase 0 — Skeleton & deterministic core (server)
Goal: repo scaffold + the tested deterministic pipeline, no UI, no Woo.
## Phase 0 — Hybrid skeleton & client deterministic core (= Gates A+B)
Goal: scaffold the hybrid app and ship a **tested client-authoritative** TECH-SPEC
pipeline (mask → axis → sqft/bdft → 3:4 PNG) plus a minimal FastAPI store/settings
shell. No Woo publish, no inference, no offline/OCR/ruler.
**Gate A (docs):** AGENTS.md + this Phase 0 rewrite + review package locks (signed
2026-08-31). **Gate B (infra/code):** items below.
Scope — in:
- Repo layout: `frontend/` (SvelteKit), `backend/` (FastAPI), `deploy/` stubs; docs stay SoT.
- Client pure modules implementing TECH-SPEC: constants, sheet/chroma/black + sliders,
mask confirm hooks, length axis, scale, 6" widths, sqft, **bdft = sqft × thickness_in**,
pricing rec from species $/bdft, 3:4 PNG / 80% fill / ≥1600 guard (**no upscale**).
- FastAPI: app factory, logging, config, `GET /api/health`, SQLite + alembic skeleton,
settings stub with AES-GCM round-trip (secrets never returned on GET), draft store stub
(accept client numbers + files — store only, do not recompute SoT).
- `deploy/docker-compose.yml`, `Caddyfile.fragment`, `.env.example` (placeholders only).
- Unit tests for TECH-SPEC **TV-1…TV-9** (esp. TV-3 bdft, TV-6/7 crop/no-upscale, TV-8 pricing).
- Fixture photo set for Ty Gate B review.
Scope — out:
- Server happy-path pipeline / CLI that recomputes mask/crop/bdft as SoT.
- Real U2Net (stub OK; on-demand later).
- Woo create/publish, live taxonomy sync (mock/seed species list OK later).
- Inference Call 1/2, LoRA.
- Offline PWA, IndexedDB sync, OCR, ruler.
- Fake upscale / inventing pixels.
Deliverables:
- `backend/` FastAPI skeleton: app factory, health endpoint, config, logging.
- `pipeline/` implementing TECH-SPEC: constants.py, ruler→scale, geometry, width
sampler, bdft math, pricing, green-removal, crop/normalize, merch filter.
- `db/`: schema.sql + alembic migration for all tables (DATA-MODEL).
- `tests/`: port every TECH-SPEC test vector (TV-1…TV-9) to pytest; add property
tests (bdft monotonic in inputs; width sampler returns 3–5 points; crop fill ∈
[0.75,0.85]).
- Dockerfile for backend; runs `pytest` in CI/local.
- `frontend/` scaffold + deterministic modules + unit tests TV-1…TV-9.
- `backend/` FastAPI health + settings encryption + draft persistence stub + schema migration baseline.
- `deploy/` compose + `.env.example` + Caddy fragment.
- Shown output in PR/commit: printed TV-3, TV-6, and TV-7 (no-upscale/retake) on **real fixtures**.
Acceptance:
- `pytest` green, covering all 9 TVs + property tests; coverage of `pipeline/` ≥ 95%.
- `GET /api/health` works on the dev container.
- A fixture photo set (3–5 real slab photos) runs end-to-end through the pipeline
CLI (`python -m app.pipeline.run <dir>`) and prints bdft/widths/normalized outputs
matching hand-computed expectations.
- Gate B: client (or shared pure) tests green for TECH-SPEC vectors; bdft identity holds
(no `/12`); undersized source warns and refuses fake upscale.
- `GET /api/health` up in compose over HTTPS hostname after `.201` inventory + Caddy merge.
- Settings AES round-trip; secret fields never appear in GET.
- Draft can store client-uploaded originals + PNGs + numbers without server recompute.
- Coverage target: deterministic modules ≥ 95% where practical; property checks
(bdft monotonic in thickness/sqft; fill band; no upscale path).
Exit gate: Ty reviews the pipeline CLI output on real photos; math confirmed.
Exit gate: Ty confirms Gate B math/image prep shown output on real fixtures; health +
encrypted settings + draft store stub demonstrated (curl or thin UI).
**Next (not Phase 0):** Gate C = early Phase 1 claimable publish — Woo **draft** +
`SLAB-UAT-*` on prod, inference OFF. Call 1/2 after Gate C, still inside POC.
## Phase 1 — API surface (no Woo, no inference)
Goal: full slab CRUD + photo upload + settings, backed by the Phase-0 core.
@@ -55,6 +84,8 @@ Acceptance:
Exit gate: Ty drives the API with curl through a full draft→ready cycle.
## Phase 2 — Frontend: capture & offline PWA
> **SUPERSEDED for POC (Gate A 2026-08-31):** online-only; no OCR/ruler/offline PWA. Do not implement this section as written. Capture UX moves to early Phase 1 / Gate C shape per AGENTS.md.
Goal: mobile-first capture flow, fully offline, with the local light pass.
Deliverables:
@@ -93,26 +124,27 @@ Acceptance:
- Inference disabled → review screen fully functional with manual species/character.
- Every field is editable and the override persists (asserted in a test).
Exit gate: Ty publishes a test slab to the real store as a **pending** product.
Exit gate: Ty publishes a test slab to the real store as a **Woo draft** (`SLAB-UAT-*`) product.
(Gate A lock: not pending/published for UAT.)
## Phase 4 — WooCommerce integration (real)
Goal: production Woo sync, taxonomy, dedupe, logging (FR24–28).
Deliverables:
- Woo client (httpx, Basic auth, timeouts), taxonomy sync, auto-assign, media upload,
product create (status=pending default), dedupe, sync_log.
product create via `woo_create_status` (default **draft**; `SLAB-UAT-*` forces draft), dedupe, sync_log.
- `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).
Acceptance:
- Real publish: product appears in store admin as pending, correct SKU/price/images/
- 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.
- 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).
Exit gate: Ty verifies a real pending product in the store is correct.
Exit gate: Ty verifies a real **Woo draft** `SLAB-UAT-*` product in the store is correct.
## Phase 5 — Inference (vision + optional content)
Goal: scoped, toggleable inference with guardrails.