From bbaab9f2c74b42260764327921d8ba3924fd3272 Mon Sep 17 00:00:00 2001 From: Ty Date: Fri, 11 Sep 2026 13:47:18 +0000 Subject: [PATCH] docs: remove live TECH-SPEC-PIPELINE.md --- docs/TECH-SPEC-PIPELINE.md | 253 ------------------------------------- 1 file changed, 253 deletions(-) delete mode 100644 docs/TECH-SPEC-PIPELINE.md diff --git a/docs/TECH-SPEC-PIPELINE.md b/docs/TECH-SPEC-PIPELINE.md deleted file mode 100644 index 9c21419..0000000 --- a/docs/TECH-SPEC-PIPELINE.md +++ /dev/null @@ -1,253 +0,0 @@ -# Technical Spec: Deterministic Image Pipeline & Area → Bdft - -Status: SPEC · 2026-08-27 · Gate A freeze 2026-08-31 · TyTech / SlabUploader -Scope: algorithms and test vectors for the deterministic core (client-authoritative). -No inference, no LLM. Everything here is a pure function of (pixels, user inputs) → outputs. -Inference, Woo payload, API, and schema live in other docs. - ---- - -## 1. Invariants - -1. **Determinism** — same inputs → bit-identical outputs. No randomness, no timestamps in math, no network calls for the math. -2. **Manual entry is authoritative** — user-entered length, thickness, SKU, and price always win over any computed value. -3. **Fallback chain is explicit** — every computed value has a defined fallback; the system never silently guesses. - - BG removal: chroma-key / threshold → four knobs (sheet, sensitivity, edge offset, feather) live on capture → retake. U2Net last-resort is deferred from POC; the contract stays in the architecture. - - Length axis: min-area bounding rect → user rotate/confirm → retake if the photo is unusable. - - Area: requires a single contiguous slab mask; broken mask → flag and retake. -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. Only APIs listed here are used; update this section before adding new ones. - -## 2. Units and rounding - -| Quantity | Source | Stored / displayed | -|---|---|---| -| Length | user, 1/8" steps | nearest 1/8" | -| Thickness | user pick, 1/8" steps | that pick (used in math and copy) | -| Thickness Woo attribute | same pick, rounded **up** into a Woo range | Woo term, not the 1/8" value | -| sqft | computed from mask | 2 decimals | -| bdft | sqft × thickness (inches) | 2 decimals | -| Price | species $/bdft × bdft, or override | 2 decimals, half-up | - -- bdft is never rounded up to whole board feet. -- Pixel→inch scale is an implementation detail. Users never see pixels. - -## 3. Background removal (load-bearing) - -Background removal is the **load-bearing phase** of the image pipeline. The mask -defines the slab's pixel footprint. Length axis, 6" width samples, sqft, and bdft -all derive from that footprint. A clipped or over-inclusive mask silently shifts -the bounding rectangle and width samples with no later numeric check to catch it. -The user's eye on the overlay is the only validator. There is no confidence score -and no auto-gate on mask quality. - -### 3.1 Auto-detect sheet -1. Sample a 2–5% strip on all four borders. -2. Average HSV per side. -3. Hue in [35, 85] and saturation ≥ 40 → green. -4. Else value ≤ 30 and saturation ≤ 20 → black. -5. Else prompt: pick green or black. - -### 3.2 Visual feedback (required) -- Overlay the mask on the **source photo** (included vs excluded pixels), not only - a cropped result. -- Show the slab edge clearly. That edge is the measurement boundary. -- User confirms the edge before length / area run. - -### 3.3 Capture knobs (inline, live, persisted) -Factory values live in code. If the user changes them, the new values persist in -localStorage until reset-to-default. - -All four knobs are **on the capture screen next to the overlay**, not behind a -Settings navigation. Changing any knob **immediately re-runs** §3.4 on the current -photo and refreshes the overlay on the source image. No apply/save step. No server -round trip for mask tuning in POC. - -1. **Sheet** — auto / green / black. -2. **Sensitivity** — one slider; tighter vs looser detection (green: HSV range width; black: brightness cutoff). -3. **Edge offset** — pull the mask in or out a few pixels so the cut sits on the wood, not in the sheet or into the grain. -4. **Feather** — hard cut vs a couple of pixels of alpha blend. - -### 3.4 Algorithms -- **Green:** chroma-key in HSV. Border flood-fill so only sheet pixels connected to the image edge are removed (protects green-ish grain inside the slab). -- **Black:** grayscale threshold. Same flood-fill (protects dark grain). -- **POC path:** client-side only (sheet detect + four knobs). If the edge is still - wrong after tuning, **retake**. -- **Post-POC fallback (deferred, contract preserved):** U2Net matting on one photo via - FastAPI. Mask returns; knobs still apply on the client. The U2Net control is not - shipped in Gate C / first listing. The contract stays in the architecture for post-POC. -- Morphological close, then feather per the setting. - -### 3.5 Edge quality -- The mask must be one contiguous slab contour. -- If the user cannot get a clean edge with knobs, retake is the POC resolution. - -## 4. Length axis, widths, sqft, bdft - -### 4.1 Contour and length axis -1. Binary mask M from §3. -2. Largest contour → convex hull. -3. Minimum-area bounding rectangle on the hull. -4. Long side = length axis; short side = width axis. - This follows the **prevailing** length, not a diagonal cut on one end. - -### 4.2 Visual feedback (required) -- Overlay the bounding rectangle and the length axis. -- Width sample lines drawn perpendicular to that axis. -- User confirms the axis matches the slab’s prevailing length. -- **Rotate axis** if it is skewed; samples and sqft scale update live. -- Nearly square slabs: user says which axis is length. - -### 4.3 Scale -User length L (inches, 1/8") is authoritative. - -``` -scale = longest_axis_px / L -``` - -### 4.4 Widths (listing only, not bdft) -- Sample every **6"** along the confirmed length axis, perpendicular to it. -- Report min, max, and average width for the description. -- Auto-dimension overlay on the photo is **v1.5**, out of this spec. - -### 4.5 sqft -Projected top-down area of the mask. Live-edge thickness and the underside are not in the area. - -``` -sqft = (mask_pixel_count / scale²) / 144 -``` - -Round to 2 decimals. - -### 4.6 bdft - -``` -bdft = sqft × thickness_in -``` - -Round to 2 decimals. Identity: 1 sqft at 1" thick = 1 bdft. - -## 5. Photo normalization - -Applies to every inventory photo of the slab (first photo and the rest). - -- Crop aspect: **3:4 portrait** (default; matches the WooCommerce template; - configurable in settings). -- Transparent **PNG only**. -- Slab centered. **80% fill at the extremes** (widest and tallest points of the slab). Extra margin where the slab is narrower. -- After crop, if the source has enough pixels, shorter side ≥ **1600 px**. If it cannot, warn and ask for a retake. The source must supply enough pixels. -- Original + processed kept while draft/retry; both purged after successful publish (server storage; see Data Model). - -## 6. Pricing - -- Settings: sync **species** from Woo; user sets **$/bdft per species**. -- Capture: `recommendation = bdft × species.price_per_bdft`, pre-fills price. -- User may override. Override sticks for that slab. -- No rule for that species → empty price; user types it. Publish still allowed. -- Stored price: half-up to 2 decimals. -- Figure- or category-weighted price is **v1.5**. - -## 7. Constants - -### 7.1 Code (not in Settings) -``` -FILL_TARGET = 0.80 -ASPECT_W, ASPECT_H = 3, 4 -OUTPUT_PX_MIN = 1600 -WIDTH_SAMPLE_IN = 6 -INCH_STEP = 0.125 -AREA_DECIMALS = 2 -BDFT_DECIMALS = 2 -PRICE_DECIMALS = 2 -``` - -### 7.2 Factory defaults for persisted BG settings -``` -SHEET = auto -GREEN_HUE_LO/HI = 35, 85 -GREEN_SAT_MIN = 40 -GREEN_VAL_MIN = 40 -BLACK_THRESH = 30 -``` -Sensitivity, edge offset, and feather map onto these at runtime. Users interact with the knobs, not the raw HSV numbers. - -## 8. Test vectors - -Users think in inches, sqft, and bdft. Pixels are only how the computer gets there. - -### TV-1 Green sheet -Synthetic: green border, wood in the center. Interior green-ish patch that does not touch the border stays. Coverage enough that U2Net does not run. - -### TV-2 Black sheet -Same layout, black border, mid-tone wood. Dark interior wood that does not touch the border stays. - -### TV-3 Scale, sqft, bdft -Slab **96" long**, **1.5" thick**. Mask measures **0.64 sqft**. -bdft = 0.64 × 1.5 = **0.96**. - -### TV-4 Width samples -96" length axis, stations every 6". Widths perpendicular to the confirmed axis. Listing gets min, max, avg. - -### TV-5 Angled end -One square end, one 45° cut. Length axis follows prevailing length, not the diagonal. Widths stay perpendicular after user confirm. - -### TV-6 3:4 crop, 80% at extremes -Slab centered in a 3:4 frame. At the widest and tallest points, fill = 0.80. PNG with alpha. Shorter side ≥ 1600 when the source allows. - -### TV-7 Too-small source -After 80% crop, shorter side would be below 1600 and the source cannot supply it. Warn; no upscaling from nothing. That photo is not publishable until retake. - -### TV-8 Pricing -bdft 0.96, species $12.50/bdft → rec **12.00**. -Override **150.00** stored as 150.00. -No rule → rec empty; manual 99.99 stored as 99.99. - -### TV-9 Thickness → Woo range -User pick **1"** stored as 1". Attribute bucket is the Woo range that 1" rounds **up** into (e.g. `1"–1 1/2"`). Exact range labels come from the synced Woo attribute, not this file. - -### TV-10 Rounding -Length 96.04" → **96"** (nearest 1/8"). -sqft 0.641 → **0.64**. -bdft 11.525 → **11.53**. -Price 12.345 → **12.35**. - -## 9. Assumptions - -1. The first photo is top-down, lens parallel to the broad face. Tilted shots make sqft/bdft wrong; retake is the fix. -2. User length is the prevailing length of that face, in 1/8". The bounding-box axis is confirmed in the UI. -3. Thickness is the declared 1/8" pick, not measured from any photo. -4. sqft is the projected top-down area of the mask. Live-edge thickness and underside are not in the area. -5. bdft = sqft × thickness (inches). Not rounded up to whole board feet. -6. Sheet is uniform green or black. Odd colors → user picks sheet mode or retakes. -7. 3:4 portrait is the PIP crop, configurable in settings. -8. Client does all of this math. Server stores the numbers the client sends. - -## 10. Cross-reference to other docs - -| Topic | Depth doc(s) | -|---|---| -| Call 1 / Call 2 prompts, taxonomy mapping | PROMPTS, CONTENT-WOO | -| Portable Call 1→Call 2 context (full resend default) | ARCHITECTURE §9, PROMPTS | -| Woo payload, publish, 409/422 recovery | CONTENT-WOO, OPENAPI | -| SQLite tables, status machine | DATA-MODEL | -| HTTP contract, taxonomy `taxonomy_anchor` | OPENAPI | -| Client vs server topology, validation module | ARCHITECTURE | -| Auto-dimension overlay, figure-weighted price | IMPL-PLAN (v1.5) | -| Online-only capture (offline deferred) | AGENTS §2, ARCHITECTURE §4 |