Feature: Tape-line overlay replaces axis rectangle (calibration length + crop) — post-M3 #99

Closed
opened 2026-09-22 16:44:07 +00:00 by grokbot_ty · 2 comments
Owner

Summary

Replace the M3 axis-rectangle calibration step with a tape-line overlay: a black line with two rifle-scope-style end ticks that the owner drags to exactly where the tape measure was laid. Aligning the photo to a fixed reference line sets the measurement axis and the crop framing in one action, and the declared length becomes an owner-declared value (distance between the two ticks) instead of a projected guess.

Scope: large, post-M3, managed in a branch — this is a feature, not a basic bug fix. New requirement row + dedicated branch + task breakdown.

Motivation (the point)

The as-built length model is a guess:

  1. The axis rectangle's long side = the inferred tape direction.
  2. The declared length is mapped to pixels by taking the slab's visual extent projected along that axis (maskSpanAlong, geometry/rect.ts:118) — i.e. the full mask span in the axis direction is assumed to be the taped length.
  3. That is wrong when the tape didn't span the visual extent, the slab has crazy corners, or the setup shot is slanted (foreshortened projection). The px/inch scale is only as good as the assumption.

The tape-line model removes the guess: the owner rotates the photo until the taped line sits on the fixed reference line, then drags the two ticks to the exact tape start/end. L = the real-world distance between the two ticks, anchored to the owner's actual tape placement. maskSpanAlong is demoted to an optional sanity check.

Requirements

1. Overlay — black tape line + two end ticks

  • Solid black ~2pt line along the measurement axis (MS Word "2 weight").
  • Two rifle-scope-style end ticks (short perpendicular strokes) at each end.
  • Ticks are draggable to the exact tape start/end positions.
  • Hint copy: "Drag each tick to where your tape started and ended."
  • Replaces the current cyan bbox + red axis arrow in PhotoStage.svelte (lines 56-82).
  • The ticks must read intuitively as "place these where your tape was."

2. Control model — desktop-first, touch-compatible (Pointer Events unified)

  • Pan: drag the photo (mouse / one-finger).
  • Zoom: mouse-wheel / trackpad pinch (zoom-to-cursor) / two-finger pinch.
  • Rotate the photo: drag a rotate handle or arrow keys (1°) / Shift+arrow (0.1°) — coarse-then-fine (Adobe nudge model).
  • Ticks: drag end handles, ≥44×44px hit area each; arrow/Shift+arrow fine-nudge when focused.
  • Precision: arrow/Shift+arrow nudges the focused tick for pixel-level tape-mark alignment.
  • One Pointer-Events code path for mouse + touch + pen.

Library to use: sveltedrag (add to frontend/package.json dependencies).

  • Constraint: the installed major must declare peerDependencies.svelte including Svelte 4.2.20 (this project is Svelte 4, not 5). Branch prerequisite: pin a compatible version, confirm npm install + tsc --noEmit clean before wiring.
  • Usage: useDrag on the photo stage (pan/zoom), the rotate handle (rotation), and each end tick (tape marks).

3. Fixed reference line = the crop line

  • Rendered on the calibration photo at the position the crop places the slab's long edge — derived from the same fill + 3:4 aspect constants as composeInventoryFrame.
  • Aligning the photo's tape line to it sets both the measurement axis and the crop framing.

4. Crop % is a global setting

  • Single source of truth: replace the two duplicated FILL = 0.9 (png/inventory.ts:4, png/overlay.ts:489) with one shared constant fed by a settings.crop_fill value, default 90%.
  • Adjustable in the Settings UI; applies to all slabs the owner processes going forward (non-retro: existing stored PNGs are not re-cropped).
  • Ref line position and the PNG crop both read it live.

5. Crop is post-BG-removal, slab-only (existing behavior, preserved)

  • Crop bounds from the approved-mask slab extent (slabBounds, min/max over kept pixels only).
  • Crop contents = slab pixels only; background transparent (inventory.ts:53).

6. Transform scope

  • The photo rotation that aligns to the line persists only to the calibration pic + its dimension-overlay clone (role "dimension-overlay", #63).
  • Other batch siblings keep their own orientation (a backside shot may legitimately be flipped). Do not propagate rotation via the shared mask/crop settings.

7. Doc fix

  • capture/types.ts:46 comment says "80%-fill PNG crop" — stale. The live constant is FILL = 0.9 (90%). Fix the comment.

Acceptance criteria (UAT)

  • On the cal photo, the tape line sits at the current (90%) crop line; adjusting crop % in Settings moves the line and re-crops the output consistently.
  • Rotating the photo to the line + placing the two ticks produces a vertical cal pic whose long edge is on the line; the dimensioned clone matches; a backside sibling is not re-rotated.
  • Crop output contains only slab pixels (background transparent), bounds = slab extent.
  • Ticks are draggable on both desktop (mouse) and touch; ≥44×44px hit area; arrow/Shift+arrow fine-nudge works.
  • Declared L maps to the distance between the two ticks (not a projected mask span).
  • Existing slabs' stored PNGs are not retro-re-cropped.
  • tsc --noEmit clean; npm run test, npm run build, backend pytest, and both repo gates (tools/check_requirements.py, tools/check_coverage.py) pass.
  • Live UAT on 5060 (LAN :10881), real Woo, never a mock (QUALITY-105).

Implementation notes / impact

  • Touches: PhotoStage.svelte (overlay render), AxisPanel.svelte (controls), session.ts (setAxisAngle → transform model), geometry/rect.ts (maskSpanAlong demoted), png/inventory.ts + png/overlay.ts (unify FILL, read crop_fill), Settings schema + backend (new crop_fill setting).
  • New requirement row in the CREATE namespace (propose ID in the branch).
  • Not retro on crop %: affects future captures only.
  • Branch: issue-<N>-tape-line-overlay.
## Summary Replace the M3 axis-rectangle calibration step with a **tape-line overlay**: a black line with two rifle-scope-style end ticks that the owner drags to exactly where the tape measure was laid. Aligning the photo to a fixed reference line sets the measurement axis and the crop framing in one action, and the declared length becomes an owner-declared value (distance between the two ticks) instead of a projected guess. **Scope: large, post-M3, managed in a branch** — this is a feature, not a basic bug fix. New requirement row + dedicated branch + task breakdown. ## Motivation (the point) The as-built length model is a **guess**: 1. The axis rectangle's long side = the *inferred* tape direction. 2. The declared length is mapped to pixels by taking the slab's **visual extent projected along that axis** (`maskSpanAlong`, `geometry/rect.ts:118`) — i.e. the full mask span in the axis direction is *assumed* to be the taped length. 3. That is wrong when the tape didn't span the visual extent, the slab has crazy corners, or the setup shot is slanted (foreshortened projection). The px/inch scale is only as good as the assumption. The tape-line model **removes the guess**: the owner rotates the photo until the taped line sits on the fixed reference line, then drags the two ticks to the exact tape start/end. L = the real-world distance between the two ticks, anchored to the owner's actual tape placement. `maskSpanAlong` is demoted to an optional sanity check. ## Requirements ### 1. Overlay — black tape line + two end ticks - Solid **black ~2pt** line along the measurement axis (MS Word "2 weight"). - Two **rifle-scope-style end ticks** (short perpendicular strokes) at each end. - Ticks are **draggable** to the exact tape start/end positions. - Hint copy: *"Drag each tick to where your tape started and ended."* - Replaces the current cyan bbox + red axis arrow in `PhotoStage.svelte` (lines 56-82). - The ticks must read intuitively as "place these where your tape was." ### 2. Control model — desktop-first, touch-compatible (Pointer Events unified) - **Pan:** drag the photo (mouse / one-finger). - **Zoom:** mouse-wheel / trackpad pinch (zoom-to-cursor) / two-finger pinch. - **Rotate the photo:** drag a rotate handle **or** arrow keys (1°) / Shift+arrow (0.1°) — coarse-then-fine (Adobe nudge model). - **Ticks:** drag end handles, **≥44×44px** hit area each; arrow/Shift+arrow fine-nudge when focused. - **Precision:** arrow/Shift+arrow nudges the focused tick for pixel-level tape-mark alignment. - One Pointer-Events code path for mouse + touch + pen. **Library to use: `sveltedrag`** (add to `frontend/package.json` `dependencies`). - Constraint: the installed major must declare `peerDependencies.svelte` including **Svelte 4.2.20** (this project is Svelte 4, not 5). Branch prerequisite: pin a compatible version, confirm `npm install` + `tsc --noEmit` clean before wiring. - Usage: `useDrag` on the photo stage (pan/zoom), the rotate handle (rotation), and each end tick (tape marks). ### 3. Fixed reference line = the crop line - Rendered on the calibration photo at the position the crop places the slab's long edge — derived from the same fill + 3:4 aspect constants as `composeInventoryFrame`. - Aligning the photo's tape line to it sets both the measurement axis and the crop framing. ### 4. Crop % is a **global** setting - Single source of truth: replace the two duplicated `FILL = 0.9` (`png/inventory.ts:4`, `png/overlay.ts:489`) with one shared constant fed by a `settings.crop_fill` value, **default 90%**. - Adjustable in the Settings UI; applies to **all slabs the owner processes going forward** (non-retro: existing stored PNGs are not re-cropped). - Ref line position and the PNG crop both read it live. ### 5. Crop is post-BG-removal, slab-only (existing behavior, preserved) - Crop bounds from the approved-mask slab extent (`slabBounds`, min/max over kept pixels only). - Crop contents = slab pixels only; background transparent (`inventory.ts:53`). ### 6. Transform scope - The photo rotation that aligns to the line persists **only to the calibration pic + its dimension-overlay clone** (`role` "dimension-overlay", `#63`). - Other batch siblings keep their own orientation (a backside shot may legitimately be flipped). Do **not** propagate rotation via the shared mask/crop settings. ### 7. Doc fix - `capture/types.ts:46` comment says "80%-fill PNG crop" — stale. The live constant is `FILL = 0.9` (90%). Fix the comment. ## Acceptance criteria (UAT) - [ ] On the cal photo, the tape line sits at the current (90%) crop line; adjusting crop % in Settings moves the line and re-crops the output consistently. - [ ] Rotating the photo to the line + placing the two ticks produces a vertical cal pic whose long edge is on the line; the dimensioned clone matches; a backside sibling is **not** re-rotated. - [ ] Crop output contains only slab pixels (background transparent), bounds = slab extent. - [ ] Ticks are draggable on both desktop (mouse) and touch; ≥44×44px hit area; arrow/Shift+arrow fine-nudge works. - [ ] Declared L maps to the distance between the two ticks (not a projected mask span). - [ ] Existing slabs' stored PNGs are **not** retro-re-cropped. - [ ] `tsc --noEmit` clean; `npm run test`, `npm run build`, backend `pytest`, and both repo gates (`tools/check_requirements.py`, `tools/check_coverage.py`) pass. - [ ] Live UAT on 5060 (LAN :10881), real Woo, never a mock (QUALITY-105). ## Implementation notes / impact - Touches: `PhotoStage.svelte` (overlay render), `AxisPanel.svelte` (controls), `session.ts` (`setAxisAngle` → transform model), `geometry/rect.ts` (`maskSpanAlong` demoted), `png/inventory.ts` + `png/overlay.ts` (unify `FILL`, read crop_fill), Settings schema + backend (new `crop_fill` setting). - New requirement row in the CREATE namespace (propose ID in the branch). - **Not retro** on crop %: affects future captures only. - Branch: `issue-<N>-tape-line-overlay`.
Author
Owner

Design proposal — approved by owner 2026-09-24. Wireframe is on branch issue-99-tape-line-overlay.

Artifact: docs/design/issue-99-tape-line-overlay.html — commit 63b8879. Single self-contained HTML file, no build: open it in a browser and drive it.

What the design settles

  • The black ~2pt line is a fixed entity. It never rotates, pans or zooms, and it sits in the middle of the rectangle.
  • The slab is the only thing that moves: drag to pan, wheel / pinch / visible slider to zoom, the ↻ handle or the arrow keys to tilt — until the board's long axis lands on the line, tick to tick.
  • The two ticks belong to the line, slide along it only (≥44×44 px grab target, drag or arrow / Shift+arrow fine nudge), and L = the distance between the ticks, not a projected mask span.
  • Crop % moves only the overlay's two ends — (1 − fill)/2 of the long axis in from the top and the bottom. At 90% the ends sit 5% in from the top and 5% in from the bottom. It does not change the aspect ratio: the 3:4 rectangle is unchanged.
  • Zoom keeps wheel / trackpad pinch (zoom-to-cursor) as the fast path and adds a visible control (− / slider / + / % / Fit) so it is discoverable and exact, with the per-event wheel step capped so one ctrl+wheel notch cannot slam the clamp.
  • One Pointer Events path, desktop-first and touch-compatible, per req. 2.

Before this reaches the production workflow — scrub the dev items

The core idea is there. These are wireframe scaffolding and none of them belong in the shipped UI:

  • the viewport switcher (412 / 390 / 720 / 1440) and the flowbar step nav
  • the requirements-coverage board under the stage
  • the "was / now" pair of mini diagrams
  • the PROPOSAL / OPEN markers and the implementation-notes card
  • the in-stage tutorial labels ("crop 90% — overlay 5.0% in from the top", …)
  • the alignment read-out box — a proposal, not in the issue; nothing is gated on it
  • the synthetic slab drawing + grain, and the sample values (88.0″ tape, 2.125 thickness)
  • the window.__wf debug hook

How it was checked

Driven live with Playwright at 390 / 412 / 1440 rather than read off the source: the line's x stays 240 under tilt, pan and zoom; changing crop 90 → 60 moves the ends while the rectangle keeps its 3:4 aspect; the tick grab target measures 50×50 px; the operator's alignment task completes end-to-end; and all five screens show 0 px overflow, 0 clipped text, 0 overlapping text and no console errors. A vision pass on fresh captures confirmed the middle-line model and the crop bands.

Scope note: the production implementation of this design is not in this commit — the commit contains only the artifact.

**Design proposal — approved by owner 2026-09-24. Wireframe is on branch `issue-99-tape-line-overlay`.** Artifact: [`docs/design/issue-99-tape-line-overlay.html`](https://gitea.vps1.afterthedemo.com/Ty_Tech/SlabUploader/src/branch/issue-99-tape-line-overlay/docs/design/issue-99-tape-line-overlay.html) — commit `63b8879`. Single self-contained HTML file, no build: open it in a browser and drive it. ### What the design settles - **The black ~2pt line is a fixed entity.** It never rotates, pans or zooms, and it sits in the **middle of the rectangle**. - **The slab is the only thing that moves**: drag to pan, wheel / pinch / visible slider to zoom, the ↻ handle or the arrow keys to tilt — until the board's long axis lands on the line, tick to tick. - **The two ticks belong to the line**, slide along it only (≥44×44 px grab target, drag or arrow / Shift+arrow fine nudge), and **L = the distance between the ticks**, not a projected mask span. - **Crop %** moves only the overlay's two **ends** — `(1 − fill)/2` of the long axis in from the top and the bottom. At 90% the ends sit 5% in from the top and 5% in from the bottom. It **does not change the aspect ratio**: the 3:4 rectangle is unchanged. - **Zoom** keeps wheel / trackpad pinch (zoom-to-cursor) as the fast path and adds a visible control (− / slider / + / % / **Fit**) so it is discoverable and exact, with the per-event wheel step capped so one ctrl+wheel notch cannot slam the clamp. - One Pointer Events path, desktop-first and touch-compatible, per req. 2. ### Before this reaches the production workflow — scrub the dev items The core idea is there. These are wireframe scaffolding and none of them belong in the shipped UI: - the viewport switcher (412 / 390 / 720 / 1440) and the flowbar step nav - the requirements-coverage board under the stage - the "was / now" pair of mini diagrams - the PROPOSAL / OPEN markers and the implementation-notes card - the in-stage tutorial labels ("crop 90% — overlay 5.0% in from the top", …) - the alignment read-out box — a proposal, not in the issue; nothing is gated on it - the synthetic slab drawing + grain, and the sample values (88.0″ tape, 2.125 thickness) - the `window.__wf` debug hook ### How it was checked Driven live with Playwright at 390 / 412 / 1440 rather than read off the source: the line's x stays **240** under tilt, pan and zoom; changing crop 90 → 60 moves the ends while the rectangle keeps its 3:4 aspect; the tick grab target measures **50×50 px**; the operator's alignment task completes end-to-end; and all five screens show **0 px overflow, 0 clipped text, 0 overlapping text and no console errors**. A vision pass on fresh captures confirmed the middle-line model and the crop bands. Scope note: the production implementation of this design is **not** in this commit — the commit contains only the artifact.
Author
Owner

As-built annotation — SHIPPED & UAT signed off (2026-09-25)

The issue body above describes the mechanic that was prototyped and then rejected. The line is NOT an owner input. Recording the as-built here so this issue resolves against what actually shipped. Owner sign-off: Ty, 2026-09-25.

The mechanic as built

A fixed black centre line with two rifle-scope end ticks, drawn on the stage fixture. It is never draggable, never focusable, never keyboard-nudgeable, and is not part of the published pose. The source photo/slab is the only thing that moves — drag to pan, wheel/pinch to zoom, the ↻ handle or arrow keys (Shift = fine step) to tilt. The owner pans/zooms/tilts the slab until it lines up with the fixed line at the exact place the tape measurement was taken. The hint on the page says so; the old "Drag the ticks" copy is gone.

  • Declared Length = the real inches read off the tape measure. The fixed line represents that same length, because the owner aligned the slab where the tape was laid.
  • Scale = fixed-line pixel span ÷ declared Length. maskSpanAlong is not the scale source.
  • The crop slider is the sole control of the line ends. crop_fill=90 ⇒ each end sits 5% from its corresponding edge. Ends are a pure function of crop_fill (capture/tapeLine.ts cropEnds(fillPercent)).
  • Calibration rotation applies to the calibration clean PNG + its dimension-overlay clone only. Batch siblings keep their own orientation.

Acceptance criteria — verified

# Criterion Result
1 Tape line sits at the current crop line; crop % moves line and re-crops consistently PASS — ends derive from crop_fill; live crop-fill changes move the ends
2 Rotating to the line gives a vertical cal pic on the line; clone matches; sibling not re-rotated PASS — 1882fa3; 0°/20° produce differing clean PNG + overlay bytes; sibling isolation test
3 Crop output = slab px only, background transparent, bounds = slab extent PASS — approved-mask slabBounds; opaque bounds 280×529 @50%, 504×954 @90% in an 800×1067 frame
4 Ticks draggable desktop+touch, ≥44×44, arrow nudge SUPERSEDED by design decision — the line is deliberately not an owner input. Rejected during UAT.
5 Declared L maps to tick distance, not a projected mask span PASS — measure() always receives the tape span on the shipped path; the maskSpanAlong branch is legacy fallback for a tape-less call and is unreachable in calibration
6 Existing slabs' stored PNGs not retro-re-cropped PASS — crop_fill is non-retro; applies to captures going forward
7 tsc --noEmit, npm run test, npm run build, backend pytest, both repo gates pass PASS — 102/102 unit, tsc clean, requirements PASS (160 rows), coverage PASS (0 gaps). Stale 80%-fill comment from §7 corrected in f725e04.
8 Live UAT on 5060, real backend, never a mock (QUALITY-105) PASS — M4 review preview http://192.168.1.202:10883/?noload=1, image deploy-frontend:m4-a756cf5, real FastAPI on deploy_default. 10881 is the old main stack, not this.

Superseded from the body above — do not restore

  • Draggable ticks and the copy "Drag each tick to where your tape started and ended." — rejected; the fixed line is the whole point of the calibration.
  • sveltedrag — 404s on npm, never added as a dependency.
  • TapePrototype.svelte + the ?tape-prototype sandbox — deleted.
  • frontend/src/png/crop.ts — removed in 6000ac2; crop geometry lives in capture/tapeLine.ts.
  • The wireframe docs/design/issue-99-tape-line-overlay.html lives on branch issue-99-tape-line-overlay (63b8879), not m4.
  • Issue said a new row in the CREATE namespace — there is no CREATE namespace; the as-built rows are CAPTURE-170 / CAPTURE-175 / CAPTURE-75.

Commits (branch m4)

7908a99 one-page tape calibration · 1882fa3 calibration tilt → PNG/overlay only · a756cf5 fixed line + drop stale tick-nudge hint · 8673ae0 as-built docs + stale-ref sweep · f725e04 §7 comment fix.

Evidence & known limits

Requirements are normative in docs/REQUIREMENTS.md; as-built narrative in .planning/milestones/v0.5/M4/tasks.md (T2); deploy record in deploy/INVENTORY.md. The regression guard was proven to fail against the pre-fix build, not merely pass post-fix. Ty's visual acceptance was on the real deployed preview.

Known limits: svelte-check still reports its known 42-error baseline (none introduced here). Calculate (42px) and Fit (41px) sit marginally under the 44px touch reference — pre-existing, deliberately deferred to its own decision.

## As-built annotation — SHIPPED & UAT signed off (2026-09-25) **The issue body above describes the mechanic that was prototyped and then rejected. The line is NOT an owner input.** Recording the as-built here so this issue resolves against what actually shipped. Owner sign-off: Ty, 2026-09-25. ### The mechanic as built A **fixed** black centre line with two rifle-scope end ticks, drawn on the stage fixture. It is never draggable, never focusable, never keyboard-nudgeable, and is not part of the published pose. The **source photo/slab is the only thing that moves** — drag to pan, wheel/pinch to zoom, the ↻ handle or arrow keys (Shift = fine step) to tilt. The owner pans/zooms/tilts the slab until it lines up with the fixed line **at the exact place the tape measurement was taken**. The hint on the page says so; the old "Drag the ticks" copy is gone. - **Declared Length** = the real inches read off the tape measure. The fixed line *represents that same length*, because the owner aligned the slab where the tape was laid. - **Scale** = fixed-line pixel span ÷ declared Length. `maskSpanAlong` is **not** the scale source. - **The crop slider is the sole control of the line ends.** `crop_fill=90` ⇒ each end sits 5% from its corresponding edge. Ends are a pure function of `crop_fill` (`capture/tapeLine.ts` `cropEnds(fillPercent)`). - **Calibration rotation** applies to the calibration clean PNG + its dimension-overlay clone only. Batch siblings keep their own orientation. ### Acceptance criteria — verified | # | Criterion | Result | |---|---|---| | 1 | Tape line sits at the current crop line; crop % moves line and re-crops consistently | **PASS** — ends derive from `crop_fill`; live crop-fill changes move the ends | | 2 | Rotating to the line gives a vertical cal pic on the line; clone matches; sibling **not** re-rotated | **PASS** — `1882fa3`; 0°/20° produce differing clean PNG + overlay bytes; sibling isolation test | | 3 | Crop output = slab px only, background transparent, bounds = slab extent | **PASS** — approved-mask `slabBounds`; opaque bounds 280×529 @50%, 504×954 @90% in an 800×1067 frame | | 4 | ~~Ticks draggable desktop+touch, ≥44×44, arrow nudge~~ | **SUPERSEDED by design decision** — the line is deliberately not an owner input. Rejected during UAT. | | 5 | Declared L maps to tick distance, not a projected mask span | **PASS** — `measure()` always receives the tape span on the shipped path; the `maskSpanAlong` branch is legacy fallback for a tape-less call and is unreachable in calibration | | 6 | Existing slabs' stored PNGs not retro-re-cropped | **PASS** — `crop_fill` is non-retro; applies to captures going forward | | 7 | `tsc --noEmit`, `npm run test`, `npm run build`, backend `pytest`, both repo gates pass | **PASS** — 102/102 unit, tsc clean, requirements PASS (160 rows), coverage PASS (0 gaps). Stale `80%-fill` comment from §7 corrected in `f725e04`. | | 8 | Live UAT on 5060, real backend, never a mock (QUALITY-105) | **PASS** — M4 review preview `http://192.168.1.202:10883/?noload=1`, image `deploy-frontend:m4-a756cf5`, real FastAPI on `deploy_default`. **`10881` is the old `main` stack, not this.** | ### Superseded from the body above — do not restore - **Draggable ticks** and the copy *"Drag each tick to where your tape started and ended."* — rejected; the fixed line is the whole point of the calibration. - **`sveltedrag`** — 404s on npm, never added as a dependency. - `TapePrototype.svelte` + the `?tape-prototype` sandbox — deleted. - `frontend/src/png/crop.ts` — removed in `6000ac2`; crop geometry lives in `capture/tapeLine.ts`. - The wireframe `docs/design/issue-99-tape-line-overlay.html` lives on branch `issue-99-tape-line-overlay` (63b8879), not `m4`. - Issue said a new row in the `CREATE` namespace — there is no `CREATE` namespace; the as-built rows are **CAPTURE-170 / CAPTURE-175 / CAPTURE-75**. ### Commits (branch `m4`) `7908a99` one-page tape calibration · `1882fa3` calibration tilt → PNG/overlay only · `a756cf5` fixed line + drop stale tick-nudge hint · `8673ae0` as-built docs + stale-ref sweep · `f725e04` §7 comment fix. ### Evidence & known limits Requirements are normative in `docs/REQUIREMENTS.md`; as-built narrative in `.planning/milestones/v0.5/M4/tasks.md` (T2); deploy record in `deploy/INVENTORY.md`. The regression guard was proven to **fail against the pre-fix build**, not merely pass post-fix. Ty's visual acceptance was on the real deployed preview. **Known limits:** `svelte-check` still reports its known 42-error baseline (none introduced here). `Calculate` (42px) and `Fit` (41px) sit marginally under the 44px touch reference — pre-existing, deliberately deferred to its own decision.
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Ty_Tech/SlabUploader#99