# Host Port block `10880`–`10889` on ubudual5060 is allocated to this project. `:10880` is Ty's local Node/SvelteKit and must never be bound by project deploys. The dev-test environment binds `0.0.0.0` so any machine on `192.168.1.0/24` can reach it. Check to see if you are running on ubudual5060 (192.168.1.202) before attempting SSH. ## Dev-test environment (M2 current, M3 incoming) - Host: `ubudual5060` / LAN `192.168.1.202` / Tailscale `100.64.0.9` - Container: `slabuploader-m2-overnight` (image `slabuploader-m2:overnight`) - Port: **10881** → 80, bound `0.0.0.0` - URLs: `http://192.168.1.202:10881` (LAN) · `http://100.64.0.9:10881` (Tailscale) - M3 replaces this container on the same port; remove the M2 container once M3 is healthy. ## Ty local Node/SvelteKit (do not overwrite) - Host: `ubudual5060` / `100.64.0.9` - Container: `slabuploader-m1` - Port: **18080** → 80 (inventory previously listed 10880; that port is off limits to project deploys) - URL: `http://100.64.0.9:18080` - **Never** recreate or remap this container for project deploys. Future deploys use a **separate** container name and a free port in `10880`–`10889` (other than `10880`), bound `0.0.0.0`. Do not put a host name in the product spec. ## Deploy records ### 2026-09-21 02:41 PDT — #90 wood primary category + derived thickness band - Source ref: `0329052` on `issue-89-metadata-lock` (pushed to Gitea; pulled into `~/slab-deploy`). - Images (built on `ubudual5060`): `deploy-frontend:latest` `1c169c29f131`, `deploy-fastapi:latest` `32671659639b`. - Compose: `deploy/docker-compose.yml` + host-local (untracked) `deploy/override-m3.yml`. - URL: `http://192.168.1.202:10881` (LAN) · `http://100.64.0.9:10881` (Tailscale). - `matches HEAD`: **yes**. - Sample data preserved: **yes** — 24 slabs before and after `up -d` (no `-v`, no volume removal). - Verified on the live stack: served asset `assets/index-tlFib3RM.js` (sha256 `3b665746ec81f28e…`) contains the new markers and no `#thickness` select; `POST /api/v1/slabs` at `thickness_in 1.0` stored band `350` (`1"- 1 1/2"`); `PUT` at `2.75` re-banded to `352`; a client-sent stale band id was ignored; `POST /review` no longer lists thickness as missing; the Review screen in a real browser (LAN URL) shows the wood-category MultiSelect closed summary with names + count, the `Primary wood category` radio set switching on click, and the band as read-only text. - Not exercised: the Woo publish path. Rows created **before** this deploy keep `thickness_term_id` NULL until their next save (the band is derived on write, so any Review commit fills it in). ### 2026-09-21 03:12 PDT — follow-up: thickness band omitted entirely from Review - Commit: `204bc5e` (fix #90: band removed from UI, not merely read-only; publish test added). - Rebuilt `deploy-frontend:latest` and recreated the container on 10881. - Live verification: served HTML and bundle contain zero occurrences of "Thickness band" or "set from the measurement". - Sample data unchanged (still 25 slabs post-recreate). - All gates (unit, build, requirements, coverage, m3-create e2e) pass. ### 2026-09-21 11:52 PDT — merge `issue-89-metadata-lock` → main (Create-string model) - Source: branch `work/merge-89-90` (base `origin/issue-89-metadata-lock` + Create-string re-applied). `matches HEAD`: **no** — staged from the uncommitted working tree (local sync; Gitea push not attempted this round). 9 files changed: Review.svelte, slabs.ts, review.py, slabs.py, schema.sql, db/__init__.py, test_review.py, m3-create.spec.ts, review-photo-strip.spec.ts. - Model change: `metadata_locked` DB column + "Save and lock" UI **dropped end-to-end**. The deterministic title/short_description are now built by the review endpoint on every call; the owner's **Create title / Create short desc** buttons fire the build and write the result (overwrite contract). `commitReview` auto-fills only empty fields (no owner-clobber); `createTitle` is the explicit force-overwrite. Photo strip, wood primary/secondary, and server-side thickness band (item: not shown in UI, passed to Woo on publish) all taken from the branch. - LAN binds: `deploy/override-m3.yml` was absent from disk; the live stack's 10881/10882 LAN binds were re-created by editing `deploy/docker-compose.yml` ports in-place (loopback 18082/18083 from `.env` + `0.0.0.0:10881`/`0.0.0.0:10882`), then `down --remove-orphans` + `up -d` (no `-v`). - Bundle: `assets/index-YPAqx609.js` (new; contains `create-title`). API `/api/health` `woo_reachable:true`. - Sample data preserved: **yes** — 34 slabs before/after. - Live verification (LAN URL, real DB): `slabs` table has **no** `metadata_locked` column; `slab_photos.role` present. `POST /review` on slab `ec4088da…` → `title:'Curl Black Walnut Slab'`, `ready:True`. `PUT` title persisted (rev 1→2) then restored (original sample value intact, rev 3). - Not exercised live: the full browser Create-button click (verified via in-repo Playwright 6/6 m3-create + review-photo-strip; the endpoint + PUT path are proven live above). Woo publish path not exercised this round. ### 2026-09-21 12:10 PDT — redeploy: `main` after merge `8ee3da9` (reconciled PR #97 + branch) - Source: `main` @ `8ee3da9` (merge of `work/merge-89-90` into main, which itself reconciled main's PR #97 client-side `thicknessBand.ts` + `syncSelects`/`box-sizing` against the branch's server-side `thickness_band.py` + Create-string #89). `matches HEAD`: **yes**. - Resolution outcome in the deployed bundle: band is **server-side** — `Review.svelte` no longer imports `thicknessBand` and the read-only band display is gone; PR #97's `syncSelects()` (3× in source) + `box-sizing` CSS preserved; `metadata_locked` still dropped; Create-string + photo strip + wood primary live. - Rebuilt both images from `main`; recreated the stack (`down --remove-orphans` + `up -d`, no `-v`). LAN binds 10881/10882 intact. - Bundle: `assets/index-BScrgd8o.js` (new; differs from pre-merge `YPAqx609`; contains `create-title`, **zero** `thicknessBand` references). API `/api/health` `woo_reachable:true`. - Sample data preserved: **yes** — 35 slabs (one added upstream since last deploy). - Live verification (LAN URL, real DB): `POST /review` on slab `ec4088da…` → `title:'Curl Black Walnut Slab'`, `ready:True`. - Gates at merge time: backend 117/117, unit 88/88, tsc clean, e2e 6/6 (m3-create + photo-strip). - Branches `work/merge-89-90` and `issue-89-metadata-lock` deleted (local + remote); remote has only `main`. ### 2026-09-21 12:34 PDT — Review mount: restore dropped lineup/woodPrimary imports - Source: uncommitted working tree on `main` (parent `67aa3d6`). `matches HEAD`: **no**. Diff: `frontend/src/create/review/Review.svelte` +5 lines restoring imports of `keptInOrder`/`moveItem`/`missingMinimums`/`roleLabel`/`resolvePhotoUrl`/`LineupItem` from `lineup.ts` and `nextPrimary` from `woodPrimary.ts`. The morning merge kept the call sites and dropped the imports → `ReferenceError` on Review mount → "Could not load the slab." - Rebuilt `deploy-frontend` only (`compose build frontend` + `up -d frontend`, no `-v`). Image `56b35f6cdcdf`. FastAPI container left running. - URL: http://192.168.1.202:10881 (LAN) · loopback 127.0.0.1:18082 - Bundle: `assets/index-BVCyM130.js` (replaced `index-ck41xD02.js`). Served JS has **0** `nextPrimary`/`keptInOrder` (minified locals) vs 3 unresolved `nextPrimary` in the broken bundle. - Sample data preserved: **yes** — 47 slabs before rebuild; 48 after live Capture→Review probe (created `ef1f4101-b1d2-459d-b9a0-842e7650ea3c`, sku NULL, status calibrated, 48×2 in; not published). - Live verification (loopback, real API): Continue to Review → `review-screen` visible, heading "Review to Ready", no loadError, no pageerror. POST /slabs 201, photos 201×2, GET slab/taxonomy/settings 200, review 200. - In-repo mock e2e (m3-create + photo-strip) 6/6 green against the same bundle hash before deploy. - Not done: commit/push of the import restore; Woo publish path not re-exercised this round. ### 2026-09-25 08:57 PDT — M4 review preview: #99 fixed tape line + calibration rotation - Source: branch `m4`, commit **`a756cf5`** (`fix(m4): drop stale tick-nudge hint from tape page (#99)`), pushed and verified on `origin/m4`. `matches HEAD`: **yes**. Preceding M4 commits on the same branch: `1882fa3` (calibration tilt applied to the calibration PNG/overlay only) and `7908a99` (tape calibration collapsed onto one page). - Image `deploy-frontend:m4-a756cf5` (`sha256:26402a77…`) built on the 5060 from a clean release archive, **not** from the `~/slab-deploy` greenfield checkout. Container `slabuploader-m4-review` recreated on network `deploy_default`, host port `10883:80`, **zero mounts**. - URL: http://192.168.1.202:10883/?noload=1 (M4 review only). **Not** `10881` — that is the old `main` stack and has no Length page. `10882` is the API, not a UI. - Bundle: `assets/index-BI2cLnOA.js`. Served bundle has **zero** `Drag the ticks` / `nudge the last tick` strings and **does** contain the fixed-line hint. - Main stack untouched: `:10881` and `:10882/api/health` both 200 throughout. - Sample data preserved: **yes** — the review container is volume-free; the real DB was never rebuilt or re-seeded. - Live verification (deployed URL, real backend): the fixed line renders with both end ticks; a drag starting on a tick behaves identically to a pan elsewhere (line does not move); the crop slider moves the line ends; Length/Thickness → Calculate reaches Photos ready. A drag is still allowed (that is the intended pan). - Gates: `npm test` 102/102; `tsc --noEmit` clean; requirements + coverage gates green; render gate clean at 1440/768/375 (no overflow, no clipped text, no page/console/HTTP errors). - Regression guard proven to fail against pre-fix code, not just pass post-fix. - Ty UAT sign-off: **length e2e signed off 2026-09-25**; minor bugs to be filed separately. - Not done: `svelte-check` still reports its known 42-error baseline (no new error from this work); `Calculate` (42px) and `Fit` (41px) are marginally under the 44px touch reference — pre-existing, deliberately left for a separate decision; Gitea #99's **body** still describes the superseded draggable-tick model (the as-built is recorded in `tasks.md` T2 and requirements rows CAPTURE-170/175/75).