# Gate B Results — 2026-09-07 pass 5 Evidence on **gate-b** @ `8deb18d`. | Case | What this proves (mill-owner) | Evidence class | Result | Evidence | Blocker | |---|---|---|---|---|---| | TV-1 | Green-sheet mask: green interior patch stays, border pixels removed, U2Net not invoked | proxy-synthetic | pass | vitest.txt:11 | | | TV-2 | Black-sheet mask: dark wood interior stays, flood-fill isolates border-connected pixels only | proxy-synthetic | pass | vitest.txt:11 | | | TV-3 | bdft = sqft × thickness_in; identity 1 sqft @ 1" = 1 bdft; no divide-by-12 | product-state | blocked | No PNG under `fixtures/real/`; vitest skip :14 | | | TV-3 | bdft identity computed correctly by pure module | proxy-synthetic | pass | vitest.txt:75-77,81 | | | TV-4 | Width samples at 6" stations report min/max/average perpendicular to confirmed axis | proxy-synthetic | pass | vitest.txt:5-8,10 | | | TV-5 | Angled-end slab: length axis follows prevailing length; widths stay perpendicular after confirm | proxy-synthetic | pass | vitest.txt:7-8,10 | | | TV-6 | 3:4 crop, 80% fill, transparent PNG, shorter side ≥1600 when source allows | product-state | blocked | No PNG under `fixtures/real/`; vitest skip :12 | | | TV-6 | 3:4 crop, 80% fill, ≥1600px computed correctly by pure module | proxy-synthetic | pass | vitest.txt:84-94 | | | TV-7 | Undersized source warns, refuses upscale, photo not publishable until retake | product-state | blocked | No PNG under `fixtures/real/`; vitest skip :13 | | | TV-7 | Undersized detection and no-upscale logic correct | proxy-synthetic | pass | vitest.txt:96-103 | | | TV-8 | Pricing: bdft × price_per_bdft; override sticks; missing rule leaves rec empty | proxy-synthetic | pass | vitest.txt:74 | | | TV-9 | Thickness 1" → Woo range rounds up using synced attribute term | proxy-synthetic | pass | vitest.txt:82 | | | TV-10 | Rounding: 96.04→96", 0.641→0.64 sqft, 11.525→11.53 bdft, 12.345→12.35 price; half-up 2 decimals | product-state | blocked | No PNG under `fixtures/real/`; vitest skip :83 | | | TV-10 | Rounding logic correct | proxy-synthetic | pass | vitest.txt:78-80,81 | | | V-1 | SKU charset `[A-Z0-9-]{3,24}` accepted; invalid values rejected with errors | proxy-synthetic | pass | vitest.txt:15-28,73 | | | V-2 | Title/short_title/description maxLength enforced at field exit and submit | proxy-synthetic | pass | vitest.txt:46-62,73 | | | V-3 | Validation module hash: client caches; mismatch returns 412 with fresh module | proxy-synthetic | pass | vitest.txt:64-71,73 | | | AES-1 | Encrypt-decrypt round-trip; ciphertext never in GET settings; password never returned | api-contract | pass | pytest.txt:46 | | | AES-1 | Encrypt-decrypt round-trip; ciphertext ≠ plaintext; fresh nonce each call | proxy-synthetic | pass | pytest.txt:9-17 | | | AES-2 | Missing SLAB_AES_KEY refuses startup; no plaintext fallback | proxy-synthetic | pass | pytest.txt:23-24 | | | H-1 | GET /api/health returns 200 `{ "status":"ok" }` with woo_reachable false | api-contract | pass | pytest.txt:27-31 | | | D-1 | POST /api/v1/slabs accepts multipart data JSON + files; returns 201 calibrated with server_rev | api-contract | pass | pytest.txt:32-39 | | | OPS-1 | GET https://${APP_HOSTNAME}/api/health returns 200 after compose up | deploy | pass | health.txt:2-13 (HTTP/2 200, status ok, woo_reachable false). Self-signed cert (curl -k); phones need CA. UI at / returns 200 text/html. | | | UI-BVT-1 | Select Photos label has for/id, file selection advances to mask step, Process Photos enabled, knobs adjustable — thin BVT only; does not unlock UI readiness | e2e | pass | playwright-select-photos.txt:4-9 (4 passed) | | | UI-MASK-1 | Overlay translucent: photo visible under green mask | e2e | pass | live-mask-bvt.txt:4-8 + live-mask-ux/mask-overlay-live.png | | | UI-MASK-2 | Knobs respond without multi-second lag | e2e | pass | live-mask-bvt.txt:9-11 (sensitivity 36ms, edge offset 18ms) | | | UI-MASK-3 | Sheet mode does not mark entire frame as slab | e2e | pass | live-mask-bvt.txt:12-14 (slab fraction 0.200) | | | UI-MASK-LAN | Phone/iPad mask fundamentals on LAN host | human-walk | blocked | — | Ty must walk https://192.168.1.202/ (or LAN) on device; runner cannot reach LAN | **Counts:** pass = 23, blocked = 5, fail = 0. Total rows = 28. **Gate B is not green.** TV-3, TV-6, TV-7, TV-10 product-state rows are blocked (no real fixtures under `fixtures/real/`). All proxy-synthetic companions passed. OPS-1 deploy pass confirmed on `https://100.64.0.9` but self-signed cert. UI-MASK-1/2/3 pass via live-host Tailscale e2e, but UI-BVT-1 remains a thin Select Photos BVT only and does not unlock UI readiness. **UNPROVEN:** TV-3/6/7/10 product-state (no real PNGs); LAN/iPad human-walk (UI-MASK-LAN blocked, runner cannot reach LAN); full capture→publish UAT; Gate B / UAT not green. Tailscale mask fundamentals pass does NOT equal Gate ready.