Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
197 changes: 197 additions & 0 deletions docs/line-box-fidelity-investigation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
# Line-box fidelity: measured corrections rejected

Measured on 2026-09-12 against `main` at `9f287d3`. No Rust change is
recommended from this investigation: each of three corrections improved the
360px landing screen but regressed both wider screens. The coordinator
explicitly changed the deliverable to this documented negative result.
Thresholds, plugin goldens, manifest checksums, diagnostics, and source maps
remain unchanged. No implementation was made, so no new RED/GREEN regression
test is claimed.

## Measurement identity and baseline

The binary built in this worktree reported
`devup-mcp 0.4.5 (9f287d34e4b1)`:

```text
C:/Users/owjs3/orca/workspaces/devup-mcp/W11-line-box-fidelity/target/debug/devup-mcp.exe
```

Acquisition ran from the main checkout's `harness/render`, with
`DEVUP_MCP_BIN` set to that binary. Its existing call bank replayed Figma
responses; this worktree's binary generated the modules and typography themes.
The unchanged main-checkout `devup-mcp-visual` release binary performed the
comparison at channel tolerance 24. Builds used two jobs, disabled dev/test
debug symbols and incremental compilation, and the worktree's own target.

An initial render of copied main-checkout generated inputs reproduced
11.41% / 3.11% / 1.84%. Fresh acquisition with the identified worktree binary
and a serial main-checkout render measured 11.41% / 3.10% / 1.84%.
The 0.01 percentage point difference at 992px is observed run-to-run variance,
not an improvement attributed to a code change. A concurrent preliminary
render is excluded from the baseline evidence; the serial rerun is authoritative.

| Screen | Width | Fresh baseline | Final unchanged generator |
| --- | ---: | ---: | ---: |
| `landing-833-3640` | 360 | 11.41% | 11.41% |
| `landing-833-3322` | 992 | 3.10% | 3.10% |
| `landing-832-2975` | 1920 | 1.84% | 1.84% |

## Three disposable probes

These were experiments on generated themes or the browser DOM, not Rust
implementations. Each starts from the same baseline; corrections were not
stacked. The final generated themes were restored byte for byte.

| Probe | 360px | 992px | 1920px | Decision |
| --- | ---: | ---: | ---: | --- |
| Whole-pixel token line boxes | 9.64% | 3.71% | 2.31% | Reject |
| Measured height on HUG text | 10.03% | 3.94% | 2.42% | Reject |
| Measured height on multiline HUG text | 10.35% | 3.81% | 2.31% | Reject |

The first probe changes generated numeric token line heights to
`round(fontSize * ratio) / fontSize`. For 130% it uses the captured raw ratio
`1.2999999523162842` before rounding the pixel advance; other ratios retain
their generated values. It retains numeric ratios rather than emitting pixel
overrides on token-bound text. This is a deliberately bounded experiment,
not a proposed general conversion for other percentages or variable font sizes.

The second probe assigns a browser element the corresponding Figma text
height only when vertical sizing is HUG. The third additionally requires the
design height to exceed 1.5 nominal percentage line advances. These height
probes match normalized text content and font size, selecting the innermost
matching element. Repeated labels can match the same element, so these probes
are not a complete node-ID-based projection or proof about every HUG node.
The distinctive headings in the table below have unambiguous matches.

The token experiment was repeated in the main harness on modules freshly
generated by `9f287d34e4b1`; the HUG experiments were also repeated there.
The table records those fresh-input runs. The initial copied-input HUG probe
measured 3.95% at 992px; this is the same 0.01-point variability as the baseline.
No changed binary exists: the experiment column names the only mutation.
All three violate the no-regression requirement. The mobile improvement alone
does not justify bending the R8 FILL/HUG policy.

## Per-block measurements explain the sign change

DOM measurements use `getBoundingClientRect`, rather than rounded display
sizes. Y coordinates are relative to the screen's top. These values were
measured on the unchanged generated inputs in the initial local harness.

| Node | Font | Design height | DOM height | DOM minus design | Design Y | DOM Y |
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
| `833:3646`, mobile heading | 38 | 245 | 246.953125 | +1.953125 | 90 | 90 |
| `833:3647`, mobile subtitle | 18 | 69 | 70.171875 | +1.171875 | 359 | 360.953125 |
| `833:3664`, mobile benchmarks | 28 | 36 | 36.390625 | +0.390625 | 888 | 893.125 |
| `833:3724`, mobile features | 28 | 36 | 36.390625 | +0.390625 | 1468 | 1475.015625 |
| `833:3770`, mobile community | 28 | 36 | 36.390625 | +0.390625 | 2274 | 2284.03125 |
| `833:3328`, tablet heading | 52 | 272 | 270.375 | -1.625 | 250 | 250 |
| `833:3329`, tablet subtitle | 24 | 62 | 62.375 | +0.375 | 546 | 544.375 |
| `833:3346`, tablet benchmarks | 36 | 47 | 46.796875 | -0.203125 | 940 | 940.75 |
| `832:2980`, desktop heading | 52 | 272 | 270.375 | -1.625 | 200 | 200 |
| `832:2981`, desktop subtitle | 24 | 62 | 62.375 | +0.375 | 496 | 494.375 |
| `832:2998`, desktop benchmarks | 36 | 47 | 46.796875 | -0.203125 | 900 | 900.75 |

The mobile heading has five lines at nominal 49.4px; the wider heading has
four at nominal 67.6px. Subtracting a constant from every line to fix mobile
would worsen the wider heading's existing deficit. This rules out a single
constant signed per-line adjustment, not every font-dependent correction.
The benchmark Y values also show that a heading's local error does not alone
predict the next section's error: other contributions can offset it.

## Direct PNG observation of repeated glyphs

For the mobile heading, primary-colored pixels in its design rectangle
(`x=16, y=90, width=328, height=245`) give:

```text
Figma PNG Zero glyph bands begin y 101 / 150 / 199 (49, 49)
browser same bands begin y 101 / 150 / 200 (49, 50)
```

The inclusive bands are Figma `101–127 / 150–176 / 199–225` and browser
`101–127 / 150–176 / 200–226`. Extraction selects rows with more than two
pixels satisfying `abs(R-90)<25`, `abs(G-68)<25`, and `B>230`, then groups
adjacent rows. This isolates the repeated blue word from the dark heading.

At desktop, using the design heading rectangle `x=280, y=200, width=551,
height=272`, the same extraction gives Figma `216–251 / 284–319 / 352–387`
and browser `214–250 / 282–318 / 349–385`. The design starts advance by
68/68px, versus browser 68/67px; the browser also starts two pixels higher.
Tablet's third band intersects nearby artwork, so it is not used as an
independent glyph-advance measurement.

These observations support whole-pixel advances for these particular design
headings and fractional browser advances. They do not determine Figma's
general rounding rule, font ascent/descent treatment, or first/last-line
leading algorithm. The desktop initial offset additionally leaves font
metrics, rasterization, and leading distribution unresolved. Wider aggregate
regression alone does **not** disprove local whole-pixel advance: a screenshot
also includes the other layout interactions and internal glyph placement.

## PERCENT normalization is not this residual

Both `codegen/text.rs::line_height` and
`theme/devup_json.rs::typography_value` already convert
`129.99999523162842` to `1.3` using `(percent / 10).round() / 10`.
The raw ratio differs by approximately `0.0000000476837158`.
At 38px, the advance difference is `0.0000018119812px`, under two millionths
of a pixel; across five lines it is about `0.00000906px`. It cannot explain
the measured 1.953125px height difference. At 52px the difference is about
`0.00000247955px` per line, also negligible.

All implicated heading/subtitle styles are token-bound. Re-rounding the
inline emission cannot change them, and the token path already has the same
normalization. Neither function was changed to manufacture progress. This
finding concerns the captured 130% value, not an audit of arbitrary percentage
precision in unrelated styles.

A correct future fix must account for the active font size and metrics,
actual wrapped line count, initial glyph placement, and how parent layout
propagates the result at each width. It must test those independently before
claiming a universal pixel-advance rule. Width-specific constants that merely
fit these three screenshots would not establish that behavior.

## Separate gate defect: build identity versus later untracked files

The first base test run measured **1048 passed, 1 failed, 2 ignored**.
Only `devup-mcp --test cli`'s
`version_build_id_reports_the_repository_dirty_state` failed, with
`left: false`, `right: true`. Its binary reported clean `9f287d34e4b1`, while
its runtime `git status --porcelain=v1 --untracked-files=normal` saw the
temporary probe scripts created after the build script had captured identity.

`build.rs` records dirty status during compilation and watches selected crate
paths plus Git HEAD/index. A later untracked file elsewhere in the worktree
changes runtime status without necessarily rerunning that build script. Thus
the test compares two different moments. Reproduction sequence: build a clean
binary, create an untracked root file without modifying a watched input, then
run `cargo test -p devup-mcp --test cli
version_build_id_reports_the_repository_dirty_state`.

This is separate from line-box fidelity and is not fixed here. Temporary
probe files were removed before the final gate, and the report was staged
before compiling so the build-time and test-time dirty state stayed stable.

## Final verification

All commands use this worktree's target. `CARGO_BUILD_JOBS=2` also limits
commands without an explicit `-j` argument.

| Gate | Result |
| --- | --- |
| `cargo fmt --all -- --check` | Pass |
| `cargo clippy --locked --workspace --all-targets --all-features -j 2 -- -D warnings` | Pass, zero warnings |
| `cargo test --workspace -j 2 --no-fail-fast` | 1049 passed, 0 failed, 2 ignored |
| `cargo insta test --workspace --all-features --check` | Pass; no snapshots to review |
| `cargo test --locked -p devup-mcp --test stdio_smoke` | 2 passed, 0 failed |

Test linking emitted the existing localized MSVC library-creation messages as
`linker_messages` warnings; the clippy gate itself emitted zero warnings.
After staging this document, the test binary correctly reported
`9f287d34e4b1-dirty`. The dirty suffix records the report, not a generator
implementation change. The render measurements above all use the modules
acquired from the earlier identified clean binary.

The final restored render passed at 11.41% / 3.10% / 1.84%. There is no
accepted improvement, and the mobile fidelity defect remains open.