- live-mask.spec.ts: replace green-overlay avgAlpha assertions with
checkerboard + slab pixel detection; add green-overlay color rejection
check; fix hasColorVariance false-positive with monochrome test image
- mask-ux.spec.ts: rename test to match new rendering (no green overlay)
- playwright.config.ts: set baseURL when webServer is used so page.goto('/')
resolves correctly in local dev
Fixes: issue #55 mask preview
Replace green overlay rendering with cutout-on-checkerboard preview:
- Slab pixels show actual photo RGB with mask-based alpha
- Removed background area shows 10px checkerboard (224/192 gray)
- Knob debounce from a1d4a96 preserved unchanged
Update Playwright test to assert checkerboard + slab pixels
visible, not green overlay avgAlpha.
- drawMaskOverlay: reduce green overlay max alpha 140→80 (~31% opacity),
lighten green from (0,200,0) to (60,220,60) so photo stays visible
- resolveSheet: when auto-detect is ambiguous, try both green and black
sheet modes, pick whichever produces non-trivial slab area (1%-99%)
- Knob sliders: debounce full remask to 150ms; oninput redraws with
previous mask (cheap), onchange triggers immediate full remask
- Add onDestroy cleanup for debounce timeout
Fixes: issue #55 — iPad mask preview nearly solid green, knobs laggy
- Add id='photo-file-input' to file input and for='photo-file-input' to label
- Convert on:change → onchange, on:click → onclick, on:input → oninput
(Svelte 5 requires native DOM event properties, not on: directives)
- Add Playwright BVT suite confirming file selection → mask step advance
- Test image: 100x60 PNG with green border (hue=66°) and white interior
- Caddyfile: switch slab.local site block to :443 catch-all so
any SNI (IP or hostname) reaches the reverse proxy. Bare IP
HTTPS no longer fails with SSL_ERROR_INTERNAL_ERROR_ALERT.
- Cert: regenerate self-signed cert with SANs: DNS:slab.local,
IP:192.168.1.202, IP:100.64.0.9. IP SANs ensure cert is presented
for IP-based connections; :443 catch-all ensures Caddy routes
the request regardless of SNI.
- INVENTORY.md: document IP SANs, :443 catch-all, phone access
steps, and bare-IP test results.
Verified on ubuDual5060:
- curl -sk https://192.168.1.202/api/health → 200 ok
- curl -sk https://192.168.1.202/ → 200, frontend HTML
- curl -sk https://100.64.0.9/api/health → 200 ok
- curl -skI http://192.168.1.202/ → 308 redirect to HTTPS
- openssl s_client to 192.168.1.202:443 → TLS 1.3 handshake OK
- Caddyfile: switch from :80 catch-all to slab.local with file-based TLS
using self-signed cert from deploy/certs/
- docker-compose.yml: publish 443, mount certs volume read-only
- INVENTORY.md: update smoke section with HTTPS results, note phone CA TBD
Host fix: added slab.local → 192.168.1.202 to /etc/hosts,
Caddy now serves HTTPS with HTTP→HTTPS redirect,
curl -k https://slab.local/api/health returns 200 ok.
Real-fixture path and harness for product-state evidence on TV-3 bdft identity,
TV-6 crop/PNG rules, TV-7 undersized warn/retake, and TV-10 rounding.
- fixtures/real/: acquisition README, placeholder metadata JSON templates
- fixtures/real-fixtures.ts: loader that decodes real PNGs, generates masks,
runs the pipeline, fails-closed when absent or unmarked
- tv3-real.test.ts / tv6-real.test.ts / tv7-real.test.ts / tv10-real.test.ts:
product-state tests that skip when real fixtures are absent
- Updated RESULTS.md with real-fixture harness note
- Added pngjs devDependency for PNG decoding in Node.js
Dual-proof preserved: proxy-synthetic unit rows remain separate and unchanged.
Gate B is not green; product-state rows are blocked pending Ty-supplied photos.
D-1 contract tests now pass (8/8). The python-multipart>=0.0.30,<1
dependency was already in requirements.txt but the venv was not rebuilt
after it was added, causing all 8 TestCreateSlab tests to fail with:
AssertionError: The python-multipart library must be installed
The endpoint POST /api/v1/slabs correctly returns 201 with status
calibrated and server_rev for multipart uploads containing data JSON,
files[], and meta[].
Evidence: test-results/d1-fix-2026-09-07/pytest.txt (8 passed)
Gate B reports treated synthetic TV passes as if they satisfied product-state
exit. Soft result words like "not run" also blurred fail-closed reporting.
Encode the seven evidence and process locks in docs/TEST-SPEC.md as closed
vocab tables, required columns, Gate B exit checklist, Build brief refusal
fields, and SoT mutation permit-before-write. Add Evidence class to every
case table. Reclassify gate-b RESULTS so TV-3/6/7/10 vitest passes stay
proxy-synthetic, Gate exit product-state rows are blocked, and "not run"
becomes blocked. Gate B remains not green.
Record phone LAN hang root cause (308 HTTPS redirect + TLS handshake fail),
HTTP-only Caddy fix, and verified smoke results for 100.64.0.9 and
192.168.1.202. Caddy :80 catch-all returns 200, /api/health ok.
Root cause: Caddy on :80 returned 308 → HTTPS, and TLS handshake failed
(Go/OpenSSL compat). Phones hung on HTTPS redirect loop.
Fix: Caddyfile serves all HTTP on :80 without TLS/redirect. Docker
compose drops port 443 and cert mount. Interim — post-POC restores
proper TLS.
Pure units module: bdft = sqft × thickness_in (no /12), half-up
rounding for sqft/bdft/price, length to nearest 1/8". Property
checks for monotonicity in thickness and sqft.
TDD: red evidence in test-results/tv3-tv10-red.txt, then minimum impl.
Results:
TV-3 | pass | test-results/tv3-tv10-green.txt
TV-10 | pass | test-results/tv3-tv10-green.txt
Pure client modules for TECH-SPEC sheet detect, chroma-key / black
threshold with border flood-fill, four knobs (live re-run), and mask
confirm hooks. Synthetic fixtures. U2Net never invoked.
TDD: red evidence in test-results/tv1-tv2-red.txt, then minimum impl.
Results:
TV-1 | pass | test-results/tv1-tv2-green.txt
TV-2 | pass | test-results/tv1-tv2-green.txt