diff --git a/.audit/m3-build-plan.md b/.audit/m3-build-plan.md deleted file mode 100644 index 967421a..0000000 --- a/.audit/m3-build-plan.md +++ /dev/null @@ -1,84 +0,0 @@ -# M3 Create — build plan (working record) - -Status: WORKING RECORD · 2026-09-19 · **not normative**. - -`docs/REQUIREMENTS.md` is the only normative text; `docs/MILESTONES.md` holds the M3 ID subset; -behavior is in `docs/capabilities/CREATE.md` and `docs/contracts/`. This file records decisions -made on 2026-09-19 so a new session does not re-litigate or drift from them. It **cites** -requirement IDs; it defines none. - -## 1. Scope - -Implement Milestone 3 only, per the M3 ID subset in `docs/MILESTONES.md` and -`docs/capabilities/CREATE.md`. Do not implement outside that subset unless the owner expands scope -in chat. - -## 2. Settled contract rulings (owner, 2026-09-19) - -| # | Question | Ruling | Now lives in | -|---|---|---|---| -| R1 | Soft delete representation | Add `deleted_at TEXT NULL`; deleted rows excluded from all GET responses; status enum unchanged | `docs/contracts/DATA-MODEL.md` §1–2, `docs/contracts/API.md` §4 | -| R2 | Publish completion vs the poll endpoint | The terminal poll returns `woo_product_id`; the local working slab row is then discarded; the device draft is cleared client-side; the app **never deletes from WooCommerce** | `docs/REQUIREMENTS.md` PUBLISH-25, `docs/contracts/API.md` §4 + §7, `docs/contracts/DATA-MODEL.md` §1 | -| R3 | Prompt storage | SQLite is the point of record (`prompts` + `prompt_revisions`). File-based prompts are **superseded**; no prompt files ship and the `backend/app/prompts:ro` compose mount was removed | `docs/contracts/PROMPTS.md` (unchanged), `deploy/docker-compose.yml`, `docs/DEPLOYMENT.md` | -| R4 | `/images` volume | Photo bytes are SQLite BLOBs (source of truth). `/images` holds only a health-probe file and short-lived staging (1024 px Call-1 JPEGs, Woo media uploads), purged after use. Health goes non-200 when the DB or `/images` is unwritable; `woo_reachable:false` stays 200 | `docs/contracts/DATA-MODEL.md` §1, APP-45 | - -## 3. Implementation decisions - -**Stack.** FastAPI + stdlib `sqlite3` + `schema.sql` (`CREATE TABLE IF NOT EXISTS`). No SQLAlchemy, -no Alembic. Pragmas `journal_mode=WAL`, `foreign_keys=ON`, `busy_timeout`, `synchronous=NORMAL`; -one process-wide connection behind a lock. The app factory takes `db_path` and `image_dir` so tests -get per-test isolation. Revisit an ORM/migrations only after real UAT data exists. - -**Async (202 + poll).** One in-process job queue with a single consumer — **not** FastAPI -`BackgroundTasks` (Starlette background tasks make TestClient 202 tests accidentally synchronous). -Status transitions CAS inside `BEGIN IMMEDIATE`. Tests run the queue in an inline mode so contract -tests stay deterministic. - -**Photos.** `slab_photos.original_blob` / `processed_blob` are the source of truth; `/images` per R4. - -**Prompts.** DB-only with revisions per R3. - -**Publish.** Per R2 and `docs/contracts/API.md` §7 + `docs/contracts/CONTENT-WOO.md`. On success: -set `woo_product_id`, purge photos, delete the working row after the terminal poll is served, clear -the device draft. `SLAB-UAT-*` always creates a draft. `max_upload_size` is checked before media -upload (`docs/contracts/WOO-API.md` §10). - -**Other.** -- `ready` is server-derived from the REVIEW-5 set. -- SKU is entered at the Measure step (UX §2.5); do not widen the Capture union. -- The server stores client-sent derived numbers and never recomputes them as source of truth (APP-40). -- PUT order: 404 → 409 `revision_conflict` → 409 `duplicate_sku` → 422 `stale_field` → apply → `server_rev++`. -- Call 1 fills empty fields only; background inference writes bump `server_rev` and the client adopts the polled rev. -- Frontend additions stay outside the Capture union: `frontend/src/api/*`, `frontend/src/create/*`. - -## 4. Slice order - -| Slice | Delivers | M3 IDs | -|---|---|---| -| S0 | factory, config/startup guards, health/meta, JSON logs, Dockerfile | APP-30, APP-45, APP-55, APP-65, SETTINGS-10, SETTINGS-25, OPS-25 | -| S1 | AES-GCM crypto + Settings (masked, write-only secrets) | SETTINGS-5/15/20/25/30/35/75/85/90, QUALITY-125 | -| S2 | slabs CRUD, optimistic concurrency, soft delete | APP-5/15/20/35/40, REVIEW-45 | -| S3 | photos (multipart → BLOBs, original/processed, 410 after purge) | CAPTURE-95/105/130, PUBLISH-40 | -| S4 | Woo client (app-password Basic) + taxonomy sync + test-woo | METADATA-5/10/15/20/30, SETTINGS-40/50/51/55/60/80, QUALITY-35 | -| S5 | pricing, deterministic templates, server-derived ready | REVIEW-5/10/15/20/25/30/35/40/50, METADATA-70/80/85/90, QUALITY-90/95 | -| S6 | publish (sequence, UAT draft, duplicate/stale/partial-failure) | PUBLISH-5/10/15/20/25/30/31/35/36/37/38/40/45/50/55/60 | -| S7 | inference last (Call 1/Call 2, prompt revisions, templates fallback) | METADATA-35/40/45/50/55/60/65/66/75/95/100/105/110/115, SETTINGS-45/70 | -| Cross | frontend API client + Review / Settings / Publish screens | (UI for the slices above) | -| Exit | inference-off suite, Woo-mock integration, Compose UI E2E, real UAT draft | QUALITY-20/35/50/105/110/120 | - -TDD every slice: the failing test precedes its implementation; never parallelize RED and GREEN of -the same scenario. - -## 5. Guardrails - -- Subagents run on a single local GPU: **one at a time, never in parallel**. -- Agents never declare a gate green; Ty owns gate exit (QUALITY-25, QUALITY-30). -- Run `python tools/check_requirements.py` after any docs change; it must pass. -- Ports `10880`–`10889` are this project's; never bind `:10880`. Dev-test runs on `:10881`, bound `0.0.0.0`. -- Create must work with inference off (zero inference calls) — QUALITY-20. -- No raw status codes, stack traces, or internal codes reach the owner — QUALITY-120. - -## 6. Not yet validated - -- Long-horizon multi-step agentic loops (more than one or two tool round-trips). -- M3 implementation has not started; this file is the pre-build record.