Files
SlabUploader/.audit/m3-build-plan.md
T
Ty 20f08bbc25 docs(m3): record create build plan and decisions
Working record for a fresh session: settled contract rulings (R1-R4),
implementation decisions (stdlib sqlite3 + schema.sql, in-process job queue,
BLOB photo store, DB-only prompts), slice order S0-S7, guardrails. Cites
requirement IDs; defines none.
2026-09-19 09:29:02 -07:00

85 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.