docs: signed UX + PC browsers + UAT practice walk
Replay local tip onto origin/docs/refactor-v2. Keeps remote UX-DESIGN wireframes. Adds signed UX, browser matrix (phone+PC), and UAT-PRACTICE.
This commit is contained in:
@@ -13,7 +13,7 @@ When docs disagree, use this order:
|
||||
1. **`docs/TECH-SPEC-PIPELINE.md`** — deterministic math, image prep, constants, test vectors
|
||||
2. **`docs/ARCHITECTURE.md`** — hybrid topology, what-runs-where, secrets why, status machine
|
||||
3. This **`AGENTS.md`** — hard rules for agents (verbatim locks below)
|
||||
4. **`docs/DATA-MODEL.md`**, **`docs/OPENAPI.md`**, **`docs/CONTENT-WOO.md`**, **`docs/PROMPTS.md`**, **`docs/DEPLOYMENT.md`**, **`docs/UX.md`** (presentation; functional gates stay in this file)
|
||||
4. **`docs/DATA-MODEL.md`**, **`docs/OPENAPI.md`**, **`docs/CONTENT-WOO.md`**, **`docs/PROMPTS.md`**, **`docs/DEPLOYMENT.md`**. Presentation: `docs/UX.md` (full rewrite landed 2026-09-02, same day as the deletion). Functional gates stay in this file.
|
||||
5. **`docs/IMPL-PLAN.md`** — hybrid-aligned Phases 0–6. Phase 2 is a deferred stub only (offline PWA / OCR / ruler). Do not implement deferred stubs or any server happy-path measure/crop as SoT.
|
||||
6. **`README.md`** — orientation only
|
||||
7. **`docs/archive/PRD-2026-08.txt`** — frozen archive only. Not a source for new work.
|
||||
@@ -32,7 +32,7 @@ Archived PRD lines that conflict with the files above are non-authoritative (see
|
||||
|
||||
### Open doc defects
|
||||
|
||||
Landed on `docs/refactor-v2`: #5 Application Passwords, #8 Call 1 prefill, #9 confidence gate, #11 `WOO_BASE_URL`, #10 UX spec (`docs/UX.md`), #12 synced-species-only, #15 inline mask knobs (U2Net deferred), #14 taxonomy manufactured last-modified, #2 portable Call1→Call2, #17 shared validation module, #18 409/422 field recovery. Grok Build owns remaining OPENAPI #3 wording if still open.
|
||||
Landed on `docs/refactor-v2`: #5 Application Passwords, #8 Call 1 prefill, #9 confidence gate, #11 `WOO_BASE_URL`, #12 synced-species-only, #15 inline mask knobs (U2Net deferred), #14 taxonomy manufactured last-modified, #2 portable Call1→Call2, #17 shared validation module, #18 409/422 field recovery. **#10 UX spec:** rewritten 2026-09-02; Ty signed the walk 2026-09-06 PT with one amendment (PC browsers in the compatibility matrix). Presentation: `docs/UX.md`. The pre-deletion file is history, not a source. **#4** browser floor: phone + PC families locked; exact minimum version pins and API inventory depth still open. OPENAPI #3 wording already landed if the body still looks open.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -26,8 +26,8 @@ Build order is in `docs/IMPL-PLAN.md`. Empty app on the real host first, then th
|
||||
| `docs/CONTENT-WOO.md` | Titles, descriptions, store payload |
|
||||
| `docs/PROMPTS.md` | Vision and copy prompts (server files) |
|
||||
| `docs/DEPLOYMENT.md` | Compose, Caddy, backup |
|
||||
| `docs/UAT-PRACTICE.md` | Walkable practice UAT: host check → App Password → one `SLAB-UAT-*` prove |
|
||||
| `deploy/INVENTORY.md` | Host worksheet before you bind ports |
|
||||
| `docs/UX.md` | What the mill owner sees on the phone |
|
||||
| `docs/IMPL-PLAN.md` | Phases and exit gates |
|
||||
| `docs/archive/PRD-2026-08.txt` | Frozen history. Not a source for new work. |
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
Grok headless sessions for this folder
|
||||
(copy a line below, then Ctrl+V into the Grok TUI)
|
||||
|
||||
Overnight execute (docs refactor v2 commits):
|
||||
01a05b6c-5ad4-7110-a706-215faa1c94ed
|
||||
|
||||
Interrogate (multi-model review):
|
||||
01a05b57-e790-7dc3-a8f2-671f15152ee3
|
||||
|
||||
How + architect (first proposal):
|
||||
01a05b3f-dfe2-79c0-b36a-c43320ebdd04
|
||||
|
||||
Resume from a second terminal on this computer:
|
||||
grok --resume 01a05b6c-5ad4-7110-a706-215faa1c94ed --cwd /workspace/gitea-atd/Ty_Tech/SlabUploader
|
||||
@@ -163,7 +163,7 @@ any → failed (with error)
|
||||
Values can come from Call 1 (≥ 0.7 pre-fill the user kept), manual entry, or a mix.
|
||||
**Review gate:** below-threshold Call 1 leaves species, wood category, figure,
|
||||
edge, grade, and feat-* empty. That subset is not the submit list. Inline nudges
|
||||
on every still-required control, not submit-only (`docs/UX.md`). Inference is
|
||||
on every still-required control, not submit-only. Presentation: `docs/UX.md`. Inference is
|
||||
optional; with inference OFF the user fills everything by hand.
|
||||
4. **publishing** — Woo create in flight. Poll for result.
|
||||
5. **published** — Woo product created, woo_product_id stored. Images purged.
|
||||
@@ -227,4 +227,4 @@ Providers differ. Default portable path: **resend the full Call 1 turn** (prompt
|
||||
|
||||
Per-slab thread on the **server** (client does not hold provider IDs). Best-effort delete on publish / abandon / fail; log and continue.
|
||||
|
||||
Presentation of the review screen: `docs/UX.md`.
|
||||
Presentation: `docs/UX.md`.
|
||||
|
||||
+2
-2
@@ -103,7 +103,7 @@ If it is less sure, those fields stay empty. That empty-start subset is not
|
||||
the submit list. Review will not continue until the full ready set is filled:
|
||||
SKU, length, thickness, price, species, wood category, edge type, figure,
|
||||
grade, thickness band, moisture, fig-* tags, and inventory PNG(s). Species
|
||||
plus one figure is not enough. How that looks on the phone: `docs/UX.md`.
|
||||
plus one figure is not enough. Presentation: `docs/UX.md`.
|
||||
Do not fill a low-confidence guess.
|
||||
|
||||
Names from vision or from typing match store ids case-insensitive, trimmed. No
|
||||
@@ -195,4 +195,4 @@ The server still writes `sync_log`. Success still sets `woo_product_id`, marks p
|
||||
Templates, numbers, matching names to store ids, payload, dedupe, logging: code.
|
||||
Generate text may write description prose only. It must not invent dimensions. It may only pick species and attributes that exist in the synced store lists.
|
||||
|
||||
How errors look on the phone is in `docs/UX.md`. The wire uses `{ field, code, message, actions[] }`. The mill owner never sees those codes. If the phone's copy of the rules is stale, refresh the copy and retry. That is not a duplicate-SKU conflict.
|
||||
Presentation: `docs/UX.md`. The wire uses `{ field, code, message, actions[] }`. The mill owner never sees those codes. If the phone's copy of the rules is stale, refresh the copy and retry. That is not a duplicate-SKU conflict.
|
||||
|
||||
@@ -181,6 +181,9 @@ provide `python -m app.security.rekey NEW_KEY` (reads old key from env
|
||||
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.
|
||||
|
||||
For the full human-run walk (host check → App Password → one `SLAB-UAT-*` prove → confirm →
|
||||
cleanup), see `docs/UAT-PRACTICE.md`.
|
||||
|
||||
## 9. Scaling path (not v1)
|
||||
|
||||
- Postgres when SQLite write contention appears (schema already portable).
|
||||
|
||||
+21
-10
@@ -46,7 +46,7 @@ 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).
|
||||
- Woo create/publish and live taxonomy sync (Phase 1). Species offered in UI, pricing seeds, and Call 1 are **exactly** Woo-synced species-category leaves (CONTENT-WOO §2.2 / AGENTS §6 / DATA-MODEL `woo_categories`). No mock, seed, hardcoded, or fallback species list in this phase or later.
|
||||
- Inference Call 1/2, LoRA.
|
||||
- Offline PWA, IndexedDB sync, OCR, ruler.
|
||||
- Fake upscale / inventing pixels.
|
||||
@@ -169,9 +169,14 @@ Deliverables:
|
||||
re-crop UI as SoT.
|
||||
- Inference UI (only meaningful after Gate C and when `inference_enabled`): Call 1
|
||||
results with confidence. **≥ 0.7** (configurable default) → pre-fill mutable
|
||||
fields. **< 0.7** → leave empty; inline nudges; block continue until exactly
|
||||
one species, ≥1 wood category, and ≥1 figure (`docs/UX.md`). "Generate text"
|
||||
triggers Call 2; never auto.
|
||||
fields. **< 0.7** → leave empty; inline nudges. Continue/ready is blocked until
|
||||
the **full ready set** is filled — not species + wood category + figure alone.
|
||||
Canonical lock (AGENTS §7 / DATA-MODEL slabs **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. Presentation:
|
||||
`docs/UX.md`. "Generate text" triggers Call 2; never auto.
|
||||
- Capture: four knobs on the mask screen with live re-run. No U2Net control in POC.
|
||||
- Publish button (online-only), poll `publishing` → `published` | `failed`, surface
|
||||
error detail from sync_log.
|
||||
@@ -243,9 +248,14 @@ Deliverables:
|
||||
taxonomy **from the Woo cache only**; confidence threshold **0.7** (configurable
|
||||
default; not a POC Settings slider). Auto-run on calibrated create only if
|
||||
`inference_enabled`. Not top-down-only.
|
||||
- Apply AGENTS §6 gate: ≥ threshold → pre-fill mutable species/categories/attributes/
|
||||
tags; < threshold → empty fields + review gate requiring species + wood category
|
||||
+ figure before ready (`docs/UX.md`).
|
||||
- Apply AGENTS §6 prefill: ≥ threshold → pre-fill mutable species/categories/attributes/
|
||||
tags; < threshold → those fields stay empty. Call 1 is assist only and does not
|
||||
define ready. Ready/publish requires the **full ready set** in AGENTS §7 /
|
||||
DATA-MODEL (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+). Species + wood category + figure is not the gate.
|
||||
Presentation: `docs/UX.md`.
|
||||
- Call 2 (text) proxy: never auto. Portable Call 1 context (full resend default).
|
||||
LLM prose only; templates inject deterministic numbers. See `docs/PROMPTS.md`.
|
||||
- `test-inference` endpoint (vision + portable vs stateful probe). Fail closed if
|
||||
@@ -257,8 +267,9 @@ Acceptance:
|
||||
|
||||
- Local vision model configured: ≥ 0.7 fields pre-fill and remain editable; user
|
||||
override sticks.
|
||||
- Below 0.7: fields stay empty; UI blocks continue/ready until exactly one
|
||||
species, ≥1 wood category, and ≥1 figure are set manually (asserted).
|
||||
- Below 0.7: fields stay empty; UI blocks continue/ready until the full AGENTS §7
|
||||
/ DATA-MODEL ready set is filled (asserted). Species + wood category + figure
|
||||
is not the ready definition.
|
||||
- No doc/code path auto-fills below-threshold guesses or treats inference as a
|
||||
publish gate.
|
||||
- Inference OFF: app works end-to-end; tests assert zero calls to the inference endpoint.
|
||||
@@ -287,7 +298,7 @@ Deliverables:
|
||||
|
||||
Acceptance (measured):
|
||||
|
||||
- E2E capture→publish on iPhone and Android browsers works (online).
|
||||
- E2E capture→publish on iPhone, Android, and PC browsers (desktop Chrome/Edge/Firefox) works (online).
|
||||
- Manual length override recalculates widths/sqft/bdft correctly (UAT asserts).
|
||||
- Client crop: slab ~80% frame in ≥95% of UAT photos; shorter side ≥1600 when source allows.
|
||||
- Woo product created with processed PNG images + correct taxonomy.
|
||||
|
||||
+1
-1
@@ -156,7 +156,7 @@ Sheet/slider prefs are client localStorage only in POC (not in SettingsView).
|
||||
- `PUT /api/v1/slabs/{id}` — Update draft fields. Body: partial `SlabCreate` (any subset of editable fields + `client_rev`).
|
||||
- Server **stores** client-sent derived fields (`sqft`, `bdft`, widths). It may validate shape and ranges. It must **not** recompute mask, sqft, bdft, or widths as source of truth. Client re-runs TECH-SPEC math and PUTs the new numbers.
|
||||
- `409 revision_conflict`.
|
||||
- `409 duplicate_sku` → `{ "field":"sku", "code":"duplicate_sku", "message":"...", "actions":["edit_sku","open_existing"], "woo_product_id": "...?", "woo_admin_url": "...?" }`. `open_existing` is required even if lookup failed (then omit id/url). UI: `docs/UX.md`. Never show the code.
|
||||
- `409 duplicate_sku` → `{ "field":"sku", "code":"duplicate_sku", "message":"...", "actions":["edit_sku","open_existing"], "woo_product_id": "...?", "woo_admin_url": "...?" }`. `open_existing` is required even if lookup failed (then omit id/url). Never show the code. Presentation: `docs/UX.md`.
|
||||
- `422` stale/invalid field → `{ "field":"species_id", "code":"stale_field", "message":"...", "actions":[], "options": [...] }` after a submission-time taxonomy refresh. Draft: phone form + server slab `failed` (retryable).
|
||||
- `412` validation module stale → `{ "code":"validation_module_stale", "module": { "hash":"...", "rules":[] } }`. Not a 409.
|
||||
- `DELETE /api/v1/slabs/{id}` → `204`. Soft: only unpublished drafts. Published slabs cannot be deleted from the app.
|
||||
|
||||
+5
-4
@@ -102,11 +102,12 @@ Return valid JSON matching this schema:
|
||||
user may clear or change it).
|
||||
- Field confidence **< threshold** → leave empty. Do not show the model's
|
||||
low-confidence guess as a default.
|
||||
- Before the slab can leave review toward ready/publishable, the user must
|
||||
have **species** and **≥1 figure attribute** set (manually if Call 1 left
|
||||
them empty), plus the rest of the mandatory ready set.
|
||||
- Call 1 does not define the ready gate. Species + figure (or any vision
|
||||
subset) is assist/prefill only. Ready is the **full named set** in AGENTS §7
|
||||
/ DATA-MODEL (filled manually if Call 1 left fields empty). Species + one
|
||||
figure is not enough.
|
||||
- Manual entry always wins. Call 1 is assist-only and does not gate publish.
|
||||
Full suite must pass with inference OFF. Canonical lock: AGENTS §6.
|
||||
Full suite must pass with inference OFF. Canonical lock: AGENTS §6–7.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -18,6 +18,24 @@ Inference, Woo payload, API, and schema live in other docs.
|
||||
4. **Units** — inches in 1/8" steps; sqft and bdft to 2 decimals; price half-up to 2 decimals. Those rounded values are what get stored. No sqin in the UI, tests, or product copy.
|
||||
5. **Client is authoritative** — all deterministic processing runs in the browser. The server stores the numbers and files the client sends, and handles Woo sync.
|
||||
|
||||
|
||||
## 1b. Browser compatibility floor
|
||||
|
||||
Client processing runs in the browser on phones and PCs. Ty lock 2026-09-06 PT: the
|
||||
compatibility matrix includes PC browsers; this is not a mobile-only app.
|
||||
|
||||
Supported families:
|
||||
- Phone: iOS Safari, Android Chrome
|
||||
- PC: desktop Chrome, desktop Edge, desktop Firefox (Windows and macOS at minimum)
|
||||
|
||||
Exact minimum version pins remain issue #4. Unsupported clients get a hard-stop message with an
|
||||
update / switch link. No polyfill strategy in POC.
|
||||
|
||||
Load-bearing client APIs for the deterministic path include canvas (or OffscreenCanvas where used)
|
||||
for mask overlay and knob re-run, image decode/crop/encode for 3:4 transparent PNG prep, and
|
||||
standard fetch to the app API. Do not depend on APIs outside this inventory without updating this
|
||||
section.
|
||||
|
||||
## 2. Units and rounding
|
||||
|
||||
| Quantity | Source | Stored / displayed |
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
# UAT Practice Walk: one `SLAB-UAT-*` listing
|
||||
|
||||
Status: WALK · for Ty to run · SlabUploader
|
||||
|
||||
This is the walkable practice plan. It proves one listing end to end without touching a live product.
|
||||
Follow the sections in order. Each one names what you do, what you should see, and where the rule
|
||||
lives.
|
||||
|
||||
**Inference is OFF for this walk.** The optional ON path is only valid after Gate C is green and
|
||||
the inference endpoint is already configured. Do not turn it on here.
|
||||
|
||||
---
|
||||
|
||||
## 1. Host and deploy preconditions
|
||||
|
||||
Before this walk, the app must already be on the inventoried host. This section only checks that.
|
||||
Do not invent host IPs or hostnames. Read them from `deploy/INVENTORY.md`.
|
||||
|
||||
1. **Inventory is filled.** `deploy/INVENTORY.md` has `APP_HOST`, `APP_HOSTNAME`, ports, Caddy
|
||||
location, and the smoke results. It is the only source for what runs where on this host.
|
||||
See `docs/DEPLOYMENT.md` §1 for the worksheet rules.
|
||||
2. **Reachable over LAN/Tailscale.** Your phone reaches `https://${APP_HOSTNAME}` on the same
|
||||
network (LAN or Tailscale). No public exposure. Verify with a browser, not `curl`.
|
||||
3. **HTTPS only.** The URL is `https://`. The browser shows a valid certificate. If the phone
|
||||
warns, the Caddy TLS trust is not set up yet. That is a deploy problem, not a listing problem.
|
||||
4. **Health passes.** In a browser on the phone, open `https://${APP_HOSTNAME}/api/health`.
|
||||
You should see an ok response. This is the same check in `docs/DEPLOYMENT.md` §8 step 1.
|
||||
5. **Store URL is set.** `WOO_BASE_URL` is in the host `.env` and points at the store over
|
||||
`https://`. It is not something you type in the app. See `docs/DEPLOYMENT.md` §3 and §5.
|
||||
|
||||
If any of these fail, fix the deploy before continuing. This walk does not cover Gate B host work.
|
||||
It assumes the app is up and reachable.
|
||||
|
||||
---
|
||||
|
||||
## 2. WordPress Application Password setup
|
||||
|
||||
You do this once per store, in the store's WordPress admin. Do it before you open the app's
|
||||
Settings.
|
||||
|
||||
1. **Store is on HTTPS.** WordPress only offers Application Passwords over HTTPS. The store must
|
||||
already be on TLS. If it is not, stop and fix the store first.
|
||||
2. **Dedicated low-privilege user.** Create or use a WordPress user that can create and edit
|
||||
products and upload media. Do not reuse the store admin login. A shop manager role or a custom
|
||||
role with product create/edit plus media upload is enough.
|
||||
3. **Generate the password.** Sign in as that user. Go to **Users → Profile → Application
|
||||
Passwords**. Generate a password and label it "SlabUploader". Copy the password. WordPress
|
||||
shows it once.
|
||||
4. **Enter it in the app.** Open the app's Settings on the phone. Enter the WordPress username and
|
||||
the application password. The server stores them encrypted. The browser does not keep them.
|
||||
5. **Run `test-woo`.** In Settings, tap the test. It should report ok. This also resyncs the
|
||||
store's species, categories, tags, and attributes into the app. If it fails, the username or
|
||||
password is wrong, or the store is unreachable.
|
||||
|
||||
Rules: this is a WordPress Application Password, not WooCommerce consumer keys (`ck_`/`cs_`).
|
||||
The app never calls the store from the browser. FastAPI is the only caller. Rotate by revoking
|
||||
the old password in the WP profile and entering a new one in Settings, then re-running
|
||||
`test-woo`. Full lock: `AGENTS.md` §4 and §5, `docs/DEPLOYMENT.md` §5.
|
||||
|
||||
---
|
||||
|
||||
## 3. Practice build rules
|
||||
|
||||
These rules keep practice runs invisible to customers. They are locked. Do not change them for
|
||||
the walk.
|
||||
|
||||
- **SKU prefix `SLAB-UAT-*`.** Every practice listing uses a SKU that starts with `SLAB-UAT-`.
|
||||
Pick a unique one each time, for example `SLAB-UAT-001`.
|
||||
- **Always a Woo draft.** A `SLAB-UAT-*` SKU creates a WooCommerce draft even if Settings say
|
||||
publish. Customers cannot open it. This is hard rule in `AGENTS.md` §5.
|
||||
- **`woo_create_status` defaults to draft.** The Settings field that controls create status
|
||||
defaults to `draft`. For practice you do not need to change it. If it is `publish`, the
|
||||
`SLAB-UAT-*` rule still forces a draft.
|
||||
- **Customers never see practice listings.** A Woo draft is hidden from the storefront. If you
|
||||
can see the product on the live store, something is wrong.
|
||||
|
||||
If a practice SKU is not `SLAB-UAT-*`, it is not a practice run. Do not publish a real SKU in
|
||||
this walk.
|
||||
|
||||
---
|
||||
|
||||
## 4. Prove one listing end to end
|
||||
|
||||
This is the main walk. Run it on a phone browser first, then repeat on a PC browser. Both are in
|
||||
the signed compatibility matrix (`docs/UX.md`, signed 2026-09-06).
|
||||
|
||||
Supported browsers: iOS Safari, Android Chrome on phones; desktop Chrome, Edge, Firefox on PC.
|
||||
Exact version pins are tracked under issue #4. If the browser is not supported, the app shows a
|
||||
hard stop. Switch to a supported browser.
|
||||
|
||||
### 4.1 Capture
|
||||
|
||||
1. Open the app on the phone. Start a new slab.
|
||||
2. Take 1 to 5 photos of the slab. The first should be top-down, lens parallel to the broad
|
||||
face. A tilted shot makes the numbers wrong. Retake if it is tilted.
|
||||
3. **Mask overlay and four knobs.** The app draws a mask over the photo. You see which pixels are
|
||||
slab and which are not. Tune the four knobs: **Sheet** (auto/green/black), **Sensitivity**,
|
||||
**Edge offset**, **Feather**. Each change re-runs the mask immediately. Look at the edge
|
||||
against the real wood. If it is still off, retake the photo.
|
||||
4. **Confirm the mask.** Tap confirm.
|
||||
5. **Length axis.** The app draws the bounding rectangle and the length axis. Confirm it follows
|
||||
the slab's long direction. Rotate if it is skewed. On a nearly square slab, you say which way
|
||||
is length.
|
||||
6. **Enter numbers by hand.** Length in 1/8" steps. Thickness as a 1/8" pick. SKU: type
|
||||
`SLAB-UAT-001` (or your chosen practice SKU).
|
||||
7. **Check the computed numbers.** The browser shows square feet, board feet, and width samples.
|
||||
Board feet = square feet × thickness in inches. No divide-by-12. If the numbers look wrong,
|
||||
go back and fix the mask or the length.
|
||||
8. **Continue.** This uploads the originals, the processed PNGs, and the numbers to the server.
|
||||
The slab status becomes `calibrated`.
|
||||
|
||||
### 4.2 Review
|
||||
|
||||
1. You are on the review screen. Fill every required field. Inference is OFF, so everything starts
|
||||
empty.
|
||||
2. **Species.** Pick exactly one from the synced store list. The list came from `test-woo`.
|
||||
Do not type a species that is not in the list.
|
||||
3. **Wood categories.** Pick at least one from the synced list.
|
||||
4. **Edge type, figure, grade.** Pick from the synced lists. At least one figure, at least one
|
||||
grade.
|
||||
5. **Thickness band.** Pick the Woo band that the measured thickness rounds up into.
|
||||
6. **Moisture.** Keep the default (kiln-dried) or change it.
|
||||
7. **fig-* tags.** Pick at least one.
|
||||
8. **feat-* tags.** Zero or more. Only pick existing `feat-*` tags.
|
||||
9. **Price.** A recommendation fills from the species $/bdft rule times board feet. You can
|
||||
override it. If there is no rule for the species, the price starts empty. Type it. Publish
|
||||
is still allowed with a manual price.
|
||||
10. **Title and description.** You can type them, or leave them for templates at publish.
|
||||
Templates inject the deterministic numbers. Generate text is OFF for this walk.
|
||||
|
||||
Every empty required field shows an inline nudge where it sits. The slab cannot reach `ready`
|
||||
until the full set is filled.
|
||||
|
||||
### 4.3 Publish
|
||||
|
||||
1. Tap **Publish**.
|
||||
2. The server checks the SKU against the store. If `SLAB-UAT-001` is new, it creates the
|
||||
product.
|
||||
3. **What you should see:** a success message on the screen. The local draft and its photos are
|
||||
gone from the app. The slab status in the app is `published`.
|
||||
4. **What that does NOT mean:** the product is not live in the store. It is a WooCommerce draft.
|
||||
The app's "published" means the store create succeeded and the local row was cleaned up.
|
||||
|
||||
### 4.4 Confirm in the store
|
||||
|
||||
1. Open the store's WooCommerce admin.
|
||||
2. Find the product by SKU `SLAB-UAT-001`.
|
||||
3. **It must be a draft.** Status is draft, not published.
|
||||
4. **Check the fields:** SKU, price, category (species), tags (fig-*, feat-*), attributes
|
||||
(Edge Type, Figure, Grade, Thickness, Moisture), and images (the processed transparent PNGs).
|
||||
5. **Open the live storefront.** The product must not appear. A draft is invisible to customers.
|
||||
|
||||
### 4.5 Repeat on a PC browser
|
||||
|
||||
Repeat 4.1 through 4.4 on a desktop browser (Chrome, Edge, or Firefox). Use a different
|
||||
practice SKU, for example `SLAB-UAT-002`. The flow must work the same way. This is the signed
|
||||
PC-browser compatibility check.
|
||||
|
||||
---
|
||||
|
||||
## 5. Pass and fail checks
|
||||
|
||||
You can see all of these without reading code.
|
||||
|
||||
| Check | Where | Pass looks like |
|
||||
|---|---|---|
|
||||
| Health endpoint | Phone browser, `https://${APP_HOSTNAME}/api/health` | ok response |
|
||||
| `test-woo` ok | App Settings | ok; species list is populated |
|
||||
| `woo_create_status` is draft | App Settings | shows `draft` (or `publish`, but `SLAB-UAT-*` forces draft anyway) |
|
||||
| Slab reaches `calibrated` | App, after capture | status is `calibrated` |
|
||||
| Slab reaches `ready` | App, after review | no empty required fields; inline nudges gone |
|
||||
| Publish succeeds | App, after Publish | success message; local draft is gone |
|
||||
| Woo product exists as draft | Store admin, products | SKU `SLAB-UAT-*`, status draft |
|
||||
| SKU prefix correct | Store admin | starts with `SLAB-UAT-` |
|
||||
| Store not live | Live storefront URL | product does not appear |
|
||||
| Board feet identity | App review screen, or a fixture | bdft = sqft × thickness in inches; no divide-by-12 |
|
||||
| Images are processed PNGs | Store admin, product images | transparent PNGs, not the original JPEGs |
|
||||
|
||||
### Duplicate SKU recovery
|
||||
|
||||
If you publish the same SKU twice, the second time the app stops before creating a second product.
|
||||
Under the SKU field you see: "This SKU already exists in the store (any status)." Two paths:
|
||||
edit the SKU to a new value, or open the existing listing. The draft stays in the app. Nothing is
|
||||
lost. This is the expected behavior, not a bug. See `docs/CONTENT-WOO.md` §2.5.
|
||||
|
||||
### What a fail looks like
|
||||
|
||||
- **No success message after Publish.** The app shows a plain message that the listing could not
|
||||
be created. No status number, no stack trace. The server kept a log. You can retry from the
|
||||
same screen without re-photographing.
|
||||
- **A value is no longer valid.** If a category, attribute, or price was removed from the store
|
||||
between sync and publish, that field shows "This value is no longer valid. Pick from the
|
||||
current list." Re-pick and retry.
|
||||
- **Store unreachable.** A plain message says the store could not be reached. Check the network
|
||||
and retry.
|
||||
|
||||
---
|
||||
|
||||
## 6. Cleanup
|
||||
|
||||
After the walk, decide what to do with the Woo draft UAT product.
|
||||
|
||||
- **Leave it or delete it.** Both are fine. It is a draft. Customers cannot see it. If you delete
|
||||
it, the store admin is the place to do it. The app does not delete store products.
|
||||
- **Never customer-visible.** If the product is visible on the live storefront, it is not a
|
||||
draft. That is a bug. Stop and report it.
|
||||
|
||||
**Real live publish is a separate step.** It is not part of this walk. A real product uses a
|
||||
non-UAT SKU (no `SLAB-UAT-` prefix) and is published only after you review it. That is a
|
||||
different decision. Do not run it here. The name of that step is **final POC publish**, and it
|
||||
is gated on your review of one real listing.
|
||||
|
||||
---
|
||||
|
||||
## Where each rule lives
|
||||
|
||||
| Rule | Source |
|
||||
|---|---|
|
||||
| Practice SKU forces draft | `AGENTS.md` §5 |
|
||||
| `woo_create_status` default draft | `AGENTS.md` §5, `docs/CONTENT-WOO.md` §2.4 |
|
||||
| App Password setup, not consumer keys | `AGENTS.md` §4, `docs/DEPLOYMENT.md` §5 |
|
||||
| Host inventory before deploy | `docs/DEPLOYMENT.md` §1, `deploy/INVENTORY.md` |
|
||||
| Capture → review → publish flow | `docs/UX.md` (SIGNED 2026-09-06) |
|
||||
| Board feet identity | `docs/TECH-SPEC-PIPELINE.md` |
|
||||
| Duplicate SKU recovery | `docs/CONTENT-WOO.md` §2.5, `docs/UX.md` Publish |
|
||||
| PC browser compatibility | `docs/UX.md` Browser compatibility |
|
||||
| Gate C = early Phase 1 | `docs/IMPL-PLAN.md` Phase 1, `AGENTS.md` §0b |
|
||||
+181
-87
@@ -1,146 +1,240 @@
|
||||
# Phone flow
|
||||
# Mill-owner UX — phone and PC browsers
|
||||
|
||||
What the mill owner does on the phone, from first photo to a store listing. Math and constants live in `docs/TECH-SPEC-PIPELINE.md`. Field names and the ready set live in `docs/DATA-MODEL.md`. Store payload and create steps live in `docs/CONTENT-WOO.md`. Wireframes and interaction patterns: `docs/UX-DESIGN.md`.
|
||||
Status: SIGNED · 2026-09-06 PT · Ty approved with PC browsers in the compatibility matrix · SlabUploader
|
||||
|
||||
Values you type are kept as-is. Vision fills store fields per the confidence gating rules in `AGENTS.md` §6.
|
||||
This is the presentation spec for the mill-owner flow. It says what a mill owner sees and does, in the
|
||||
order they do it. The rules behind each screen live elsewhere: `AGENTS.md` locks the product
|
||||
decisions, `docs/TECH-SPEC-PIPELINE.md` owns the math and image prep, `docs/DATA-MODEL.md` owns
|
||||
the store row and its statuses, `docs/CONTENT-WOO.md` owns listing copy and the Woo payload,
|
||||
and `docs/OPENAPI.md` owns the wire. This file names what shows up on screen; it does not restate
|
||||
those rules.
|
||||
|
||||
---
|
||||
The reader is a mill owner photographing one slab at a time, online, from a supported browser on a
|
||||
phone or a PC. The app is a single column of screens, top to bottom: capture, then review, then
|
||||
publish. There is no dashboard and no batch view in this version. Capture often starts on a phone
|
||||
camera; the same flow must also run on a PC browser.
|
||||
|
||||
## Main path
|
||||
## Jobs
|
||||
|
||||
### Settings once
|
||||
Five jobs cover the whole flow. Each section below names what the owner does, what they see, and
|
||||
the lock that governs it.
|
||||
|
||||
The store URL shows read-only from the deploy (`WOO_BASE_URL`). You always know which store this phone talks to.
|
||||
1. **Phone photo to a store listing.** Photograph the slab, confirm the cut and the numbers on
|
||||
the browser, review the listing, create the WooCommerce product. The happy path stays in the
|
||||
browser until the owner confirms the mask and the numbers.
|
||||
2. **Practice drafts customers never see.** A practice run creates a WooCommerce draft with an SKU
|
||||
that starts `SLAB-UAT-`. Customers cannot open it. It is always a draft, even when Settings say
|
||||
publish.
|
||||
3. **Retake when the overlay cut looks wrong.** The mask overlay on the source photo is the only
|
||||
quality check. If the edge is off after tuning the four knobs, the owner retakes the photo.
|
||||
4. **Fill or override store fields.** On review, the owner fills every required field by hand, or
|
||||
keeps what vision filled when it was confident enough. Typed values always win.
|
||||
5. **Publish.** The owner taps publish. The server creates the product in the store and reports the
|
||||
result back on the same screen.
|
||||
|
||||
Enter the WordPress username and application password for the low-privilege store user, under Users → Profile in WordPress. The password is write-only: you type it once. To rotate, revoke the old one in WordPress and enter a new one here.
|
||||
## Capture: photo to numbers
|
||||
|
||||
On the same screen: species $/bdft rates, vision on/off with endpoint and key, draft vs publish for new listings (default draft), reset capture knobs to factory, and an optional refresh of the store lists. **Test connection** always pulls fresh species, attributes, and tags from the store when it succeeds.
|
||||
The capture flow is one continuous screen with a few confirmations, not separate pages. The owner
|
||||
stays on it from first photo through confirmed mask and axis.
|
||||
|
||||
### First run on the floor
|
||||
### Photos
|
||||
|
||||
One short card, then dismiss. Lay a green or black sheet under the slab. Shoot from above with the lens parallel to the face. Put a tape measure in the shot so you can **type** the length later. Your typed number is what the app measures against.
|
||||
The owner takes 1 to 5 inventory photos of the slab. The first should be top-down, lens parallel
|
||||
to the broad face; a tilted shot makes square feet and board feet wrong, and retake is the fix.
|
||||
There are no calibration photos in this design. Only inventory photos exist.
|
||||
|
||||
### Capture
|
||||
### Mask overlay and four knobs
|
||||
|
||||
Take 1 to 5 inventory photos.
|
||||
The app removes the background sheet and draws the mask as an overlay on the source photo, showing
|
||||
which pixels count as slab and which do not. The owner looks at that edge against the real wood.
|
||||
This is the only quality check in the pipeline. There is no confidence score, no auto-gate, and no
|
||||
photo-quality checker. A clipped or over-inclusive mask shifts every downstream number with nothing
|
||||
later to catch it, so the eye on the overlay is what stands behind the measurement.
|
||||
|
||||
The source photo stays on screen, and the mask draws over that original: kept wood versus dropped sheet. Four knobs sit next to the overlay:
|
||||
Four knobs sit on this same screen, next to the overlay:
|
||||
|
||||
1. Sheet — auto, green, or black
|
||||
2. Sensitivity
|
||||
3. Edge offset
|
||||
4. Feather
|
||||
- **Sheet** — auto, green, or black. Auto samples the borders and picks; the owner overrides when
|
||||
the sheet is an odd color.
|
||||
- **Sensitivity** — one slider from tighter to looser detection.
|
||||
- **Edge offset** — pulls the cut in or out a few pixels so it sits on the wood, not in the sheet
|
||||
or into the grain.
|
||||
- **Feather** — hard edge versus a couple of pixels of blend.
|
||||
|
||||
Move a knob and background removal re-runs on this photo; the overlay refreshes immediately. Nothing round-trips to the server for mask tuning. The knobs stick in local storage until you reset them to factory.
|
||||
Changing any knob re-runs background removal immediately and refreshes the overlay. There is no
|
||||
apply step and no server round trip for mask tuning in this version. The four values persist on the
|
||||
browser until reset to default; they are not Settings fields.
|
||||
|
||||
Confirm the mask edge by eye. If the cut sits on the wood edge, confirm it. If it clips grain or still holds sheet after you tune, retake the photo. The one-photo server U2Net path stays in the architecture for post-POC; it is deferred from POC.
|
||||
If the edge still looks wrong after tuning, the owner retakes the photo. That is the resolution in
|
||||
this version. A server-side matting path exists in the architecture for awkward backgrounds later,
|
||||
but it is deferred and has no control here.
|
||||
|
||||
### Length axis
|
||||
|
||||
The bounding rectangle and length axis draw over the confirmed mask, with width sample lines every 6 inches along that axis. Rotate until the axis matches the slab's prevailing length, then confirm. On a nearly square piece you pick which side is length.
|
||||
After the mask is confirmed, the app draws the bounding rectangle and the length axis over the slab,
|
||||
with width sample lines perpendicular to that axis. The owner confirms the axis follows the
|
||||
slab's prevailing length. If it is skewed, they rotate it; samples and square feet update live. On a
|
||||
nearly square slab the owner says which way is length.
|
||||
|
||||
### Type length, thickness, SKU
|
||||
### Numbers
|
||||
|
||||
Manual values win. The phone computes square feet, board feet (`bdft = sqft × thickness_in`, no `/12`), and width min / max / avg from the mask and your typed length.
|
||||
The owner enters length (in 1/8" steps), thickness (a 1/8" pick), and the SKU by hand. The browser
|
||||
then computes square feet, board feet, and width samples from the confirmed mask and axis:
|
||||
|
||||
It builds 3:4 transparent PNG inventory images at 80% fill on the extremes. After that crop, if the source does not have enough pixels for a shorter side of 1600 px, that photo is not publishable until you retake it. The floor and the math are in `docs/TECH-SPEC-PIPELINE.md` (constants and TV-7).
|
||||
- Board feet = square feet × thickness in inches. One square foot at one inch thick is one board
|
||||
foot. There is no divide-by-12.
|
||||
- Widths are sampled every 6" along the length axis; the listing carries min, max, and average.
|
||||
- Square feet and board feet show to two decimals.
|
||||
|
||||
### Draft upload (calibrated)
|
||||
These numbers are computed in the browser and stored as sent. The server does not recompute them.
|
||||
|
||||
When mask, axis, length, thickness, and SKU are set and the numbers and PNGs are ready, the phone uploads originals + processed PNGs + length / thickness / SKU / sqft / bdft / widths. App status becomes **calibrated**.
|
||||
### Photo prep
|
||||
|
||||
### Review (store fields)
|
||||
Each inventory photo is cropped to a 3:4 portrait frame with the slab centered at 80% fill, saved as
|
||||
a transparent PNG. If the source has enough pixels, the shorter side lands at 1600 or more after
|
||||
crop. If it cannot reach that, the app warns and asks for a retake; it does not invent pixels. The
|
||||
owner sees the processed result alongside the original while the draft is retained.
|
||||
|
||||
Everything the listing needs lands here. A required control that is still empty shows an inline empty state as you work.
|
||||
### Continue
|
||||
|
||||
**Always on the form from capture and defaults**
|
||||
When the mask, axis, length, thickness, and SKU are set, the owner continues. That uploads the
|
||||
originals plus the processed PNGs plus the numbers to the server as a calibrated draft, and moves to
|
||||
review. The slab status becomes `calibrated`.
|
||||
|
||||
- Inventory photos as processed transparent PNGs (at least one)
|
||||
- SKU, length, thickness
|
||||
- Computed sqft, bdft, width min / max / avg
|
||||
- Thickness store band from your typed thickness, rounded **up** into the synced Woo thickness range
|
||||
- Moisture starts at **kiln-dried**. Vision does not set moisture. Change it for green or air-dried slabs.
|
||||
- Price: species $/bdft × bdft prefills when a rate exists for that species. If there is no rate, the field starts empty and you type the price. Override anytime; your override sticks for this slab.
|
||||
## Review: fill or override store fields
|
||||
|
||||
**Species and store taxonomy**
|
||||
Review is where the listing row gets filled. Every field below maps to a column in the `slabs` table
|
||||
in `docs/DATA-MODEL.md`. The owner can type any of them; vision may have pre-filled some, but typed
|
||||
values always win and every value stays editable.
|
||||
|
||||
Pickers and any vision lists are exactly the last pull from the store. A new Woo species becomes selectable only after a sync lands it in the cache.
|
||||
### What vision fills, and when
|
||||
|
||||
Call 1 prefill behavior: see CONTENT-WOO §2.3. Every pre-fill is editable. Typed values win.
|
||||
When inference is on, Call 1 runs after the calibrated draft uploads. It sends all inventory photos
|
||||
downscaled to 1024 plus the store's synced species, attribute, and tag lists, and returns a guess
|
||||
with a confidence per field. The threshold is 0.7 by default:
|
||||
|
||||
**Required before the slab is ready to list**
|
||||
- A field at or above 0.7 pre-fills on review. It stays editable; the owner can change or clear it.
|
||||
- A field below 0.7 starts empty. The app does not show the model's low-confidence guess as a
|
||||
default, and an empty start is not the same as a finished listing.
|
||||
|
||||
- Exactly one species
|
||||
- At least one wood category
|
||||
- One edge type
|
||||
- At least one figure term
|
||||
- At least one `fig-*` tag
|
||||
- At least one grade
|
||||
- Thickness band (the round-up above)
|
||||
- Moisture (kiln-dried unless you changed it)
|
||||
- Price
|
||||
- SKU, length, thickness, client sqft / bdft / widths, at least one inventory PNG
|
||||
With inference off, every field starts empty and the owner fills all of them by hand. Inference
|
||||
never blocks publish and never auto-publishes. Call 2 (Generate text) does not run on its own; it
|
||||
fires only when the owner taps it.
|
||||
|
||||
Feature (`feat-*`) tags are optional; zero is fine.
|
||||
### The fields
|
||||
|
||||
### Title, short title, description
|
||||
The required set for a list-ready slab is the full store row, not species plus one figure:
|
||||
|
||||
Type them, or tap **Generate text**. Generate text is off unless content generation is enabled in Settings. Templates still work when the text service is down. The model writes prose only. Templates inject the measured values into the final text.
|
||||
- **SKU** — `[A-Z0-9-]{3,24}`. Practice runs use `SLAB-UAT-*`.
|
||||
- **Length and thickness** — from capture; still editable here.
|
||||
- **Square feet, board feet, widths** — shown as the client-computed values, with a manual override
|
||||
path. The owner's typed number wins over the computed one.
|
||||
- **Species** — exactly one leaf category from the synced store list. No hardcoded or invented
|
||||
species; a new store species appears only after the next sync.
|
||||
- **Wood categories** — at least one, from the synced list.
|
||||
- **Edge type** — one term from the synced attributes.
|
||||
- **Figure** — at least one term.
|
||||
- **Grade** — at least one term.
|
||||
- **Thickness band** — the Woo range that the measured thickness rounds up into (for example, a 1"
|
||||
pick lands in the `1"–1 1/2"` band). The exact labels come from the synced store attribute.
|
||||
- **Moisture** — one term, defaulting to kiln-dried. It is never inferred; the owner sets or keeps it.
|
||||
- **fig-* tags** — at least one.
|
||||
- **feat-* tags** — zero or more (character, inclusions, voids, checks map to existing `feat-*`
|
||||
tags only).
|
||||
- **Price** — a recommendation pre-fills from the species $/bdft rule times board feet; the owner may
|
||||
override and that sticks. With no rule for the species, the price starts empty and the owner types
|
||||
it. Publish is still allowed with a manual price.
|
||||
|
||||
If title, short title, or description are still blank at publish, templates fill them from the measured facts (species, lengths, widths, thickness, bdft). You can edit any of it afterward.
|
||||
Title, short title, and description are not required to reach ready. They can be typed by hand,
|
||||
generated from templates at publish, or written by Generate text (Call 2) when enabled. Templates
|
||||
always run; they inject the deterministic numbers so the model never touches dimensions.
|
||||
|
||||
### Publish
|
||||
### How missing fields show up
|
||||
|
||||
When the ready set above is complete, tap publish. App status moves through **publishing** while you wait for the result.
|
||||
A field that is still empty and required shows its nudge inline, in place, the moment it is empty.
|
||||
The owner sees each gap where it sits rather than learning about a problem after scrolling past it.
|
||||
There is no single submit-time check that lists everything at once; the nudges are per-field as the
|
||||
owner works. The slab cannot reach `ready` until the full set above is filled, whether by hand or by
|
||||
a confident Call 1 the owner kept.
|
||||
|
||||
Create status comes from Settings (`draft` or `publish`, default `draft`). SKUs that match `SLAB-UAT-*` always create as Woo **draft**, even when Settings say publish. Practice listings stay draft so customers do not see them.
|
||||
### Generate text
|
||||
|
||||
On success the server deletes originals and processed images. A slab marked published in the app means the store create succeeded; that is not the same word as WooCommerce "publish."
|
||||
When content generation is enabled, a **Generate text** button runs Call 2 and fills title, short
|
||||
title, and description with model prose plus template numbers. The owner can edit the result freely.
|
||||
If the endpoint is missing or down, the button is disabled; templates still work and publish is never
|
||||
blocked by a failed Generate text.
|
||||
|
||||
---
|
||||
## Publish: create the store listing
|
||||
|
||||
## Practice path (UAT)
|
||||
The owner taps **Publish** when review is complete. What happens next depends on the SKU and Settings.
|
||||
|
||||
Same capture and review as production, with a `SLAB-UAT-*` SKU. The listing is forced to Woo draft regardless of the draft/publish setting. Change the SKU when you are ready for a real listing.
|
||||
### Draft versus live
|
||||
|
||||
---
|
||||
- A normal SKU publishes according to the `woo_create_status` setting, which defaults to draft.
|
||||
- Any SKU matching `SLAB-UAT-*` always creates a WooCommerce draft, even if Settings say publish.
|
||||
That is how practice runs stay invisible to customers.
|
||||
|
||||
## Usual failures
|
||||
The app's "published" means the store create succeeded and the local row plus photos were cleaned up.
|
||||
That is not the same as the product being live in the store; a draft is created but hidden until Ty
|
||||
promotes it.
|
||||
|
||||
**Mask clips wood or still holds sheet.** Turn the four knobs; confirm by eye. If it is still wrong, retake. A bad edge shifts square feet and widths without a later numeric check.
|
||||
### What the owner sees on each outcome
|
||||
|
||||
**Axis skewed or on the wrong length side.** Rotate the axis, or retake. Widths and sqft follow whatever axis you confirm.
|
||||
- **Success.** The screen shows that the listing was created, with the store's product reference when
|
||||
available. The local draft and its photos are gone from the app.
|
||||
- **SKU already exists.** Under the SKU field: "This SKU already exists in the store (any status)."
|
||||
Two paths appear: edit the SKU to a new value, or open the existing listing. This version does not
|
||||
update the other product in place. The draft stays in the browser; nothing is lost.
|
||||
- **A value no longer valid.** If a category, attribute, or price was removed from the store between
|
||||
sync and publish, that field shows "This value is no longer valid. Pick from the current list," with
|
||||
the current options. The owner re-picks and retries.
|
||||
- **Store unreachable or other failure.** A plain message on the screen says the listing could not be
|
||||
created and to try again. No status number, no stack trace, no raw error code reaches the owner.
|
||||
|
||||
**Source too small after the 3:4 / 80% crop.** The app warns and asks for a retake of that photo. See `docs/TECH-SPEC-PIPELINE.md` TV-7.
|
||||
The server keeps a log of every attempt either way, so a failed publish can be retried from the same
|
||||
screen without re-photographing.
|
||||
|
||||
**Required store field still empty.** Inline empty state on that control. Publish stays blocked until the full ready set in Main path is filled.
|
||||
|
||||
**SKU already exists in the store under any status** (draft, private, publish, and the rest). The message sits on the SKU field: it already exists in the store. Actions there: edit the SKU, or open the existing listing when a link is available. This version does not edit the other product in place. Raw status codes do not appear on screen.
|
||||
|
||||
**Category, attribute, tag, or price no longer valid after a list refresh.** The message sits on that field: the value is no longer valid; pick from the current list, and the current options come with it. Your draft stays on the phone.
|
||||
|
||||
**Rules copy on the phone went stale at submit.** Brief "Updating rules." The phone refetches the shared rules, checks again, and retries.
|
||||
|
||||
**Store unreachable, auth failure, or other hard Woo failure.** Plain failure message. The slab can move to failed with detail. Draft and photos stay so you can retry.
|
||||
|
||||
**Vision off or below threshold.** Fill the required set by hand. Moisture starts kiln-dried, and the thickness band comes from your typed thickness. Inference is assist-only.
|
||||
|
||||
**Generate text unavailable.** Button disabled. Type copy or let templates write at publish.
|
||||
|
||||
Leaving a field: SKU is `A-Z`, `0-9`, and hyphen, length 3–24. Title, short title, and description cannot exceed the store max length.
|
||||
|
||||
## Browser compatibility
|
||||
|
||||
The app requires **iOS Safari 17** or **Android Chrome 110** (or newer). These versions provide the APIs the image pipeline depends on: OffscreenCanvas, MediaDevices, Web Workers, File System Access, and structuredClone.
|
||||
Ty signed this walk on 2026-09-06 PT with one amendment: the compatibility matrix includes PC
|
||||
browsers as well as phones. This app is not mobile-only.
|
||||
|
||||
When an older browser loads the app, the landing page shows:
|
||||
Supported families (matrix):
|
||||
|
||||
> This app requires a recent browser. Please update Safari or Chrome, then try again.
|
||||
- **Phone:** iOS Safari, Android Chrome
|
||||
- **PC:** desktop Chrome, desktop Edge, desktop Firefox (Windows and macOS at minimum)
|
||||
|
||||
The message includes a link to the browser's update page..
|
||||
Exact minimum version pins are still tracked under issue #4. The matrix above is the product lock
|
||||
for which classes of client are in scope.
|
||||
|
||||
---
|
||||
Unsupported browser behavior: hard stop. Show a clear message that this browser is not supported,
|
||||
with a link to update or switch to a supported browser. Do not attempt a broken mask. No polyfill
|
||||
strategy in this version.
|
||||
|
||||
## Out of scope for this POC UI
|
||||
Load-bearing client surfaces (do not silently drop): canvas-based mask overlay and live knob re-run,
|
||||
image crop/prep to 3:4 transparent PNG, file/photo intake for inventory shots, and the online API
|
||||
calls for draft upload, review, and publish. Depth for APIs stays in `docs/TECH-SPEC-PIPELINE.md`
|
||||
and `docs/OPENAPI.md`.
|
||||
|
||||
Server U2Net "Try harder" on capture (deferred from POC). A user slider for the 0.7 vision threshold. Offline / airplane capture. Any image-quality classifier or score. Strings painted on the live photo about lighting, steadiness, mask confidence, or a pixel requirement.
|
||||
## What this version does not show
|
||||
|
||||
These are locked out for now; they do not appear on any screen:
|
||||
|
||||
- No photo-quality checker and no banner over the photo telling the owner it is bad. The overlay edge
|
||||
plus the owner's eye is the only gate.
|
||||
- No OCR of a printed SKU label. SKU is typed by hand.
|
||||
- No ruler or scale detection. Length is entered by hand; the browser derives its own pixel-to-inch
|
||||
scale from that length and the confirmed axis.
|
||||
- No server-side measuring or cropping as the source of truth. The browser owns the happy path.
|
||||
- No U2Net "try harder" control in this version. It stays in the architecture for later.
|
||||
- No offline capture, no airplane-mode flow. Online only.
|
||||
|
||||
## Where each job lives
|
||||
|
||||
| Job | Screen(s) | Governing lock |
|
||||
|---|---|---|
|
||||
| Photo to listing | Capture through review to publish | AGENTS §1, TECH-SPEC, ARCHITECTURE swimlane |
|
||||
| Practice drafts (`SLAB-UAT-*`) | Review (SKU field), Publish outcome | AGENTS §5, CONTENT-WOO payload status |
|
||||
| Retake on bad cut | Capture mask overlay | TECH-SPEC §3.4–3.5, issue #15 |
|
||||
| Fill or override fields | Review | DATA-MODEL `slabs`, AGENTS §6/7, issue #9 |
|
||||
| Publish and recovery | Publish outcome states | CONTENT-WOO §2.5–2.6, OPENAPI 409/422, issue #18 |
|
||||
|
||||
Reference in New Issue
Block a user