docs: replace AGENTS.md with Slice 1 contract
This commit is contained in:
@@ -1,198 +1,65 @@
|
|||||||
# AGENTS.md — SlabUploader
|
# AGENTS.md — SlabUploader
|
||||||
|
|
||||||
Working contract for coding agents. Read before writing code.
|
Working contract. Read this before writing code or more docs.
|
||||||
|
|
||||||
Status: **Gate A freeze** · signed 2026-08-31 PT · docs cut to `main` 2026-09-06 · SoT: Gitea `Ty_Tech/SlabUploader` on **gitea-atd** (not mirrors).
|
Branch intent: `doc-simplification` replaces the live doc pile with a small production set. Old files live under `docs/archive/` and are **not** sources for new work.
|
||||||
|
|
||||||
---
|
## Live files
|
||||||
|
|
||||||
## 0. Where each doc lives
|
| File | Owns |
|
||||||
|
|
||||||
| Doc | Owns |
|
|
||||||
|---|---|
|
|---|---|
|
||||||
| `docs/TECH-SPEC-PIPELINE.md` | Deterministic math, image prep, constants, test vectors |
|
| `README.md` | What this is |
|
||||||
| `docs/ARCHITECTURE.md` | Hybrid topology, what-runs-where, secrets why, status machine |
|
| `AGENTS.md` | Locks, slice scope, proof |
|
||||||
| `AGENTS.md` | Hard rules for coding agents (locks below) |
|
| `docs/PRODUCT.md` | Listing factory, fields, who decides, Woo outcome |
|
||||||
| `docs/DATA-MODEL.md` | Store row / schema facts |
|
| `docs/CAPTURE.md` | Photo, mask slider, crop, numbers |
|
||||||
| `docs/OPENAPI.md` | Wire / HTTP contracts |
|
| `deploy/` | How it is hosted |
|
||||||
| `docs/CONTENT-WOO.md` | Listing copy and Woo payload |
|
|
||||||
| `docs/PROMPTS.md` | Call 1 / Call 2 prompt files |
|
|
||||||
| `docs/DEPLOYMENT.md` | Compose, Caddy, secrets placement, ops |
|
|
||||||
| `docs/UX.md` | Presentation: what the mill owner sees and does (signed) |
|
|
||||||
| `docs/UX-DESIGN.md` | Non-normative wireframes only. `docs/UX.md` wins on any conflict. |
|
|
||||||
| `docs/TEST-SPEC.md` | Engineer test bar: pyramid cases, phased by gate, through PROD |
|
|
||||||
| `docs/UAT-PRACTICE.md` | Walkable practice UAT for `SLAB-UAT-*` |
|
|
||||||
| `docs/IMPL-PLAN.md` | Phases 0–6, Gates A/B/C meanings, exit gates. Phase 2 is a deferred stub. |
|
|
||||||
| `deploy/INVENTORY.md` | Host worksheet before port bind (Gate B) |
|
|
||||||
| `deploy/RELEASE-PROCESS.md` | Release, deploy, and promotion process (Gitea releases to UAT) |
|
|
||||||
| `README.md` | Orientation only |
|
|
||||||
| `docs/archive/PRD-2026-08.txt` | Frozen archive. Not a source for new work. |
|
|
||||||
|
|
||||||
Live docs agree. If two live files disagree, that is a docs bug: fix the files, then continue.
|
Do not add a new live markdown file without Ty saying so.
|
||||||
|
|
||||||
Run `docs/checks/check-structure.sh` after doc structure edits. It pins ownership, calendar home, and lock phrases.
|
## Slice 1 (build this, nothing else)
|
||||||
|
|
||||||
### Known open gaps
|
Client-side capture on phone and PC:
|
||||||
|
|
||||||
- Issue #4: exact minimum browser version pins and deeper load-bearing API inventory. Phone + PC families are already locked in `docs/UX.md` / TECH-SPEC.
|
1. Open the app.
|
||||||
|
2. Take or pick one slab photo.
|
||||||
|
3. Isolate the slab with **one live sensitivity slider** (plus Auto / Green / Black if the sheet is not obvious). Instant overlay. No Apply. No server trip for the slider.
|
||||||
|
4. User approves the edge.
|
||||||
|
5. App then produces the uniform inventory PNG **and** sizes / board feet from that reference photo.
|
||||||
|
6. User sees both on screen.
|
||||||
|
|
||||||
---
|
Out of Slice 1: Woo, vision, titles, extra sliders, U2Net, offline, OCR, bulk.
|
||||||
|
|
||||||
## 0b. Gates and phases
|
Add a second slider only after real slabs prove one cannot put the edge on the wood.
|
||||||
|
|
||||||
**Phase 0 = Gates A+B. Gate C = early Phase 1.** Call 1/2 only after Gate C is green, still inside POC.
|
## Proof
|
||||||
|
|
||||||
The Gate A/B/C meanings table and phase deliverables live in `docs/IMPL-PLAN.md` §Gates. Other docs may name a gate or map test cases to a phase. They must not copy the meanings table.
|
A passing test suite is not proof.
|
||||||
|
|
||||||
### Freeze provenance (Gate A)
|
Proof is Ty on a phone, real slab, approved edge, visible uniform PNG, visible length-derived widths and board feet. If step 5 does not run after approve, the build failed.
|
||||||
|
|
||||||
| SHA | What locked |
|
## Locks
|
||||||
|---|---|
|
|
||||||
| `a94d81d` | gitea-atd `Ty_Tech/SlabUploader` as remotes SoT |
|
|
||||||
| `457711b` | AGENTS.md + hybrid IMPL Phase 0 |
|
|
||||||
| `4ec478f` | Contradiction patches (README/PRD/Woo/DEPLOYMENT) |
|
|
||||||
| `4d9f307` / `4e97770` | Inference review + hybrid capture pipeline (pre-freeze baseline) |
|
|
||||||
|
|
||||||
Docs refactor is on `main` (cut 2026-09-06). Product locks above are unchanged.
|
- Every slab is quantity one. Photos + metadata are the listing.
|
||||||
|
- User types **length**, **thickness**, and **SKU**.
|
||||||
|
- App proposes everything else. User reviews before submit.
|
||||||
|
- Non-math fields stay editable until submit.
|
||||||
|
- Board feet is `sqft × thickness_in`. User does not invent the math.
|
||||||
|
- Price is proposed as `bdft × species $/bdft`. User may override. No rate → empty price, user types it.
|
||||||
|
- Manual length, thickness, SKU, price, and taxonomy always win over a model.
|
||||||
|
- `SLAB-UAT-*` is always a Woo draft.
|
||||||
|
- Vision does not gate publish. Full path must work with inference off.
|
||||||
|
- Undersized source → retake, not a soft publish.
|
||||||
|
- Duplicate SKU → stop. This version does not update the other product.
|
||||||
|
|
||||||
### Known stale PRD lines
|
## Conflict order
|
||||||
|
|
||||||
`docs/archive/PRD-2026-08.txt` is frozen history. These PRD lines are superseded by live docs:
|
1. These locks
|
||||||
FR17 (fig-* 1+, feat-* 0+), FR19 (species $/bdft only), FR40 (client-side, not server-side
|
2. `docs/PRODUCT.md` and `docs/CAPTURE.md`
|
||||||
processing), NFR server normalization timing, FR42 (env-agnostic DEPLOYMENT + deploy/INVENTORY.md).
|
3. `docs/archive/` (history only)
|
||||||
|
|
||||||
---
|
## Do not
|
||||||
|
|
||||||
## 1. Topology (locked — hybrid)
|
- Restate these locks in a fourth file
|
||||||
|
- Treat `docs/archive/` as the spec
|
||||||
**Client (SvelteKit) owns the happy path:**
|
- Ship four sliders because an older spec listed four
|
||||||
|
- Call the slice done because unit tests passed
|
||||||
- Capture 1–5 photos
|
|
||||||
- Sheet detect + chroma-key / black threshold + **four knobs inline on capture** (sheet, sensitivity, edge offset, feather) with **live re-run** and mask overlay on the source photo
|
|
||||||
- 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 "Try harder" — **deferred from POC**, not deleted. Architecture keeps the one-photo server path for awkward backgrounds post-POC. POC mask tuning is **client-side only** (sheet + four knobs, live re-run).
|
|
||||||
- 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.
|
|
||||||
|
|
||||||
Deferred work and non-goals are in `docs/ARCHITECTURE.md` and `docs/IMPL-PLAN.md`.
|
|
||||||
|
|
||||||
Depth: `docs/ARCHITECTURE.md`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Deferred work
|
|
||||||
|
|
||||||
The MVP is online-only. Measuring lives in the browser. Manual SKU entry, manual length, and client-side measure/crop are the source of truth. Undersized source warns and requires retake. The server stores client-sent numbers; there is no server happy-path pipeline for the deterministic core.
|
|
||||||
|
|
||||||
Phase 2 is a deferred stub for offline PWA, OCR, and ruler detection. Post-POC items: LoRA / fine-tune, figure-/category-weighted pricing (v1.5), auto-dimension overlay (v1.5), bulk publish, staff roles, analytics, SKU update path. Depth: `docs/ARCHITECTURE.md` §4 and `docs/IMPL-PLAN.md` Phase 2.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Deterministic core (non-negotiable)
|
|
||||||
|
|
||||||
Encode TECH-SPEC exactly:
|
|
||||||
|
|
||||||
- **bdft = sqft × thickness_in** (identity: 1 sqft @ 1" = 1 bdft).
|
|
||||||
- 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.
|
|
||||||
- Undersized source warns and requires retake. Photo is not publishable until retake.
|
|
||||||
- Pricing: species `$/bdft` from Settings, seeded by Woo species sync; `rec = bdft × price_per_bdft`; user override sticks; missing rule leaves rec empty, manual allowed.
|
|
||||||
- Port TECH-SPEC **TV-1 through TV-10** (including TV-10 Rounding). Client (or shared pure modules the client uses) is authoritative for math; server **stores** client-sent numbers.
|
|
||||||
|
|
||||||
Depth: `docs/TECH-SPEC-PIPELINE.md`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Secrets and settings
|
|
||||||
|
|
||||||
**Server-side only:**
|
|
||||||
|
|
||||||
- Woo WordPress username + application password (dedicated low-privilege WP user for product create/edit + media)
|
|
||||||
- Inference API key and endpoint (treat inference URL as sensitive)
|
|
||||||
- AES master key (`SLAB_AES_KEY` env only; never in DB or repo)
|
|
||||||
|
|
||||||
**`WOO_BASE_URL`:** compose / host `.env` only (`https://` required). Settings shows it read-only. Not a SQLite settings row. FastAPI rejects `http://` at startup.
|
|
||||||
|
|
||||||
**Server:** AES-GCM at rest in SQLite for Woo Application Password + inference secrets. Settings UI POSTs username + application password to FastAPI; server encrypts. All Woo and inference calls are server-side. Rotate by revoking the application password in WP Users → Profile and re-entering in Settings. WooCommerce consumer keys (`ck_`/`cs_`) are not implemented.
|
|
||||||
|
|
||||||
**Browser may persist (non-secret):** sheet mode, sensitivity, edge offset, feather (localStorage OK; reset-to-default required).
|
|
||||||
|
|
||||||
Why/flow: `docs/ARCHITECTURE.md` §5. Ciphertext key inventory: `docs/DATA-MODEL.md` settings table.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Woo UAT and create status (Ty locked 2026-08-31)
|
|
||||||
|
|
||||||
- Store URL: **`WOO_BASE_URL`** from Docker Compose (`https://` only). Intended UAT/POC store is Whidbey Wood Store production; the compose value is the SoT, not a Settings field.
|
|
||||||
- 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; UI maps **409** to the SKU field (edit SKU or open existing)
|
|
||||||
- After successful publish: purge originals + processed images on server
|
|
||||||
|
|
||||||
Woo draft/publish status is separate from slab lifecycle status (§7).
|
|
||||||
|
|
||||||
Payload and publish sequence depth: `docs/CONTENT-WOO.md`. Key only: DATA-MODEL `woo_create_status`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 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."
|
|
||||||
- Call 1 auto only if `inference_enabled`; Call 2 never auto-fires (user taps Generate text).
|
|
||||||
- Call 1 body: all inventory photos @1024. Taxonomy lists sent to Call 1 are **exactly the Woo-synced species / attribute sets**.
|
|
||||||
- **Confidence 0.7 (default):** ≥ 0.7 → pre-populate species, wood categories, edge, figure, grade, and feat-* from Call 1 (**mutable**). < 0.7 → those fields stay **empty**. Ready still requires the full named set in §7. Presentation: `docs/UX.md`.
|
|
||||||
- **Taxonomy cache:** `taxonomy_last_sync_at` vs `taxonomy_anchor` (bump only on stored-subset hash change, including deletions). Client re-pulls iff the anchor advanced. `test-woo` always syncs; publish if last_sync >1h. No per-nav sync. No Woo taxonomy timestamp.
|
|
||||||
- Call 2 portable context: default full resend; never require `previous_response_id`. `test-inference` two-turn probe `stateful|stateless|fail`. Fail blocks **Call 2 enablement only**.
|
|
||||||
- Templates inject deterministic numbers. LLM does not invent dimensions.
|
|
||||||
|
|
||||||
Prompt file mechanics: `docs/PROMPTS.md`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 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:** SKU, length, thickness, client sqft/bdft/widths, ≥1 inventory PNG, exactly one species, ≥1 wood category, 1 edge type, ≥1 figure term, ≥1 fig-* tag, ≥1 grade, thickness store band (round-up), 1 moisture (default kiln-dried; not inferred), price. feat-* 0+. Title/description may be typed, generated, or templated at publish. Vision may prefill the taxonomy subset only when confidence ≥ 0.7.
|
|
||||||
- Inference does not gate publish. Below-threshold Call 1 does not prefill; empty mandatory fields block ready.
|
|
||||||
|
|
||||||
Slab `draft` is separate from Woo draft (`woo_create_status`).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. Test and 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 and gate naming: follow `docs/IMPL-PLAN.md` only (§0b).
|
|
||||||
- Secrets, real Woo application passwords, and AES keys stay out of the repo.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 9. Deploy sketch
|
|
||||||
|
|
||||||
- Target: inventoried Docker host. Docker Compose (`frontend` + `fastapi`) + Caddy → `https://${APP_HOSTNAME}` (LAN/Tailscale).
|
|
||||||
- Fill `deploy/INVENTORY.md` before binding ports or merging Caddy (co-resident apps can collide; bolt.diy is one example).
|
|
||||||
- Health: `GET https://${APP_HOSTNAME}/api/health`.
|
|
||||||
- `WOO_BASE_URL` in compose (HTTPS). Settings displays it read-only.
|
|
||||||
- Inference is a remote configurable endpoint, not on the app host by default.
|
|
||||||
|
|
||||||
Depth: `docs/DEPLOYMENT.md`.
|
|
||||||
|
|||||||
Reference in New Issue
Block a user