Record the seventh sweep, over the rigs and the sixth sweep's own fix

Six findings in iris f8aa0c5, and the seven things that tripped a rule and
were left. Three of those are named as work waiting for the pre-review-gate
pass, since they pre-date #19 and are outside its diff.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
iris-aiandClaude Opus 5 committed 2026-09-20 02:54:22 -04:00
1 parent 29dd84e04e
commit 5b34d406c7
2 files changed
+133 -10

No files matched your search

+22 -10
View File
@@ -6,8 +6,8 @@ found is in `docs/LAYOUT_LOG.md`.
## The Iris layout repair is submitted ## The Iris layout repair is submitted
**Iris PR #19** (`layout/one-ask`) replaces closed #18. The tip is `b7b8d09`, **Iris PR #19** (`layout/one-ask`) replaces closed #18. The tip is `f8aa0c5`,
and past the reviewed `cadfba0` it is eight rounds, each described in and past the reviewed `cadfba0` it is nine rounds, each described in
`docs/LAYOUT_LOG.md`: `docs/LAYOUT_LOG.md`:
- **The repair**, `add6774` and `84dad21` -- collapsed-share placement, - **The repair**, `add6774` and `84dad21` -- collapsed-share placement,
@@ -32,10 +32,16 @@ and past the reviewed `cadfba0` it is eight rounds, each described in
two constants the shader and the CPU both count in written twice, and a two constants the shader and the CPU both count in written twice, and a
`Scroll` positioning content the framework positions, which cost a redraw `Scroll` positioning content the framework positions, which cost a redraw
at the default alignment. at the default alignment.
- **A sweep over the rigs, `Fixed`, and `b7b8d09` itself**, `f8aa0c5` -- the
other half of that same `Scroll` test, which could never decide it; a grid
conversion and its helper that only their own test called; `Len`
arithmetic written a component at a time; a question asked through a value
one line from its `&self` sibling; and `run-headless.sh --resize` leaving
a replayed gesture scaled against the mode the output used to have.
The settled design of the last three is in `docs/LAYOUT.md` under "Three The settled design of the vocabulary rounds is in `docs/LAYOUT.md` under
names, and the one argument that says them". Bryan settled the API over "Three names, and the one argument that says them". Bryan settled the API
2026-09-17 to 19; it is current, not frozen. over 2026-09-17 to 19; it is current, not frozen.
The core design remains sound. Round-to-nearest is still unchanged. The core design remains sound. Round-to-nearest is still unchanged.
@@ -105,16 +111,22 @@ a compile error, which reads exactly like a fuzzer failure.
1. **Bryan's review of #19.** Fixes are themselves unreviewed code: repeat 1. **Bryan's review of #19.** Fixes are themselves unreviewed code: repeat
`pre-submit-review` over each round's changes, and apply the gate above to `pre-submit-review` over each round's changes, and apply the gate above to
whatever each one touched. The ordinary oracle does not replace absolute whatever each one touched. The ordinary oracle does not replace absolute
geometry and retained-primitive expectations. geometry and retained-primitive expectations. The seventh sweep is the
evidence: reading `b7b8d09`, the sixth sweep's own fix, found that it had
removed one dead operand from a test and left the one beside it.
2. **A review of everything written before the review gate existed.** 2. **A review of everything written before the review gate existed.**
`pre-submit-review` and the rule that nothing is submitted unreviewed `pre-submit-review` and the rule that nothing is submitted unreviewed
arrived on 2026-09-13, well after the Rust port and most of Iris were arrived on 2026-09-13, well after the Rust port and most of Iris were
written, so all of that code went in unreviewed and none of the five written, so all of that code went in unreviewed and none of the sweeps
sweeps above covered more than the layout branch. It wants a pass of its above covered more than the layout branch. It wants a pass of its own
own (Bryan, 2026-09-20). The surface-texture defect in `02048ea` is the (Bryan, 2026-09-20). The surface-texture defect in `02048ea` is the
argument: nothing about that arm was hard, and it was written wrong argument: nothing about that arm was hard, and it was written wrong
anyway, which is what a first reader catches and a later sweep of some anyway, which is what a first reader catches and a later sweep of some
other subject does not. other subject does not. Three things it already has waiting, left out of
#19 because they are outside its diff: `Align::tuple` and both
`partial_align`s have no callers, `Vec2::align`/`partial_align` are
`UiVec2`'s with a conversion in front, and `impl_op!` carries four
grammars of which `core/src/util/vec2.rs` uses two, one line apart.
3. **Integrate the app's Iris capabilities before changing its pin.** 3. **Integrate the app's Iris capabilities before changing its pin.**
`32f6ad8` has 45 commits not reachable from the review branch; shared UI `32f6ad8` has 45 commits not reachable from the review branch; shared UI
ownership, richer masks, Android support, and app-side performance work ownership, richer masks, Android support, and app-side performance work
+111
View File
@@ -6,6 +6,117 @@ nothing here is rediscovered. Each entry says who found it and when.
it (settled design, the measurement method) belongs in `docs/LAYOUT.md`, and it (settled design, the measurement method) belongs in `docs/LAYOUT.md`, and
the current plan is in `docs/HANDOFF.md`. the current plan is in `docs/HANDOFF.md`.
## Seventh sweep: the rigs, `Fixed`, and the sixth sweep's fix (2026-09-20)
Over what no earlier round named -- `src/random.rs` and `tests/scenario/`,
`core/src/fixed.rs`, `scripts/run-headless.sh` -- and once more over
`b7b8d09`, which was itself unreviewed because it was the sixth sweep's own
fix. Six findings, all in `f8aa0c5`. The cold dump over 400 depth-5 trees is
byte-identical to `b7b8d09` across all 34,492 boxes, and all three seed scans
pass (400 at depth 5 in 62.75s, 1,000 at depth 6 in 162.37s, 2,000 at depth 4
in 305.25s).
**A scroll still asking a question it has already answered.** The sixth
sweep found `anchor != Px::ZERO` in `Scroll`'s content test and removed it;
the operand beside it is the same defect and survived. `self.content_len` is
`answer_px.max(container_len)`, so content that fits has nothing to scroll
through, and `update_amt` on the line above clamps `amt` to
`content_len - container_len`, which is then zero. So in
let content = match self.amt != Px::ZERO || self.content_len != self.container_len {
the first disjunct can never decide the match: `amt != ZERO` implies
`content_len != container_len`, which the second already tests. It is now
`match self.content_len > self.container_len`, the exact complement of the
`content_len <= container_len` that guards the contract fifteen lines above.
Verified by asserting the implication in place and running the whole suite,
the scrolling tests included; it held. This is the lesson of "the fixes a
review produces are themselves unreviewed code" arriving on schedule -- one
round's fix left the same mistake in the expression it was editing.
**Things nothing reads, in the new arithmetic.** `Fixed::to_scale` converts a
value between two fixed-point grids, and `shift_round` exists only to serve
it. Both arrived on this branch; the only caller either has ever had is
`a_coarser_grid_rounds_and_a_finer_one_does_not`, the test written for them.
Every other `Fixed` method has real callers (checked one by one), and nothing
needs a grid conversion: `ratio`, `mul` and `div` already cross grids where
layout has to. All three deleted. `to_scale` was also the one operation in
the file that could overflow silently without documenting it -- going to a
finer grid is `self.0 << (TO - SHIFT)` and the test only ever went coarse,
fine, coarse with a value small enough to survive the trip.
**`Len` arithmetic written a component at a time.** `Len::align` built a
`Len` whose `px` is always `Px::ZERO`, then added to and subtracted from both
of its components by hand:
start: Len::from_parts(at.rel.sub(self.rel.mul(rel)), at.px.sub(self.px.mul(rel))),
end: Len::from_parts(at.rel.add(self.rel.mul(rest)), at.px.add(self.px.mul(rest))),
`Len::scale` is "both parts by the same fraction" and `Len` has `Add` and
`Sub`, so the whole rule is `at - self.scale(rel)` and
`at + self.scale(Rel::ONE.sub(rel))` -- the point the alignment names, less
the part of the length before it. Identical arithmetic, which the dump
confirms. It is the only place in the codebase that expanded a `Len`
operation like this; `LayoutLen::apply_leftover` touches `rel` alone, which
is genuinely one component.
**A question asked through a value, one line from its sibling.** `445287c`
moved every method that answers a question about a value to `&self`.
`LayoutLen::without_leftover` was missed, and its own doc comment calls
`apply_leftover` -- which takes `&self` -- "the opposite reading of the same
value". Now `&self` too. Every other by-value method in the workspace that
does not return `Self` was checked: the rest are conversions on numbers
(`Fixed::raw`, `to_f32`) or builders.
**A rig that scales a gesture against a mode the output no longer has.**
`run-headless.sh --mode` sets `out_w`/`out_h` beside the `swaymsg output ...
mode`, because those two numbers are the extent `replay-touch` passes to
`zwlr_virtual_pointer_v1::motion_absolute` -- the recording's coordinates are
a fraction of them. `--resize`, added in `b7b8d09`'s round, changed the mode
and left the extent alone, so `--resize 800x600@60Hz --replay flick.touch`
replayed every sample at `x * 800 / 1920` and finished looking like a run
that worked. Both are one `set_mode` function now, so a third mode change
cannot get it wrong. Found by reading rather than by running: this VM has no
recorded gesture that also resizes, which is exactly why it went unnoticed.
**A comment the plan/build split stranded.** `src/random.rs`'s "a row takes
the height it is given rather than its tallest child" sat above
`let gap = self.rng.below(3) as i32 * 4`, telling a reader that the line
consumes no randomness. It describes the `set_size_rules` that `98d4e98`
moved into `Build::kind`, and the line it was left above is one of the two
draws in the function. Moved to the rule it is about, and the
seed-stability half dropped: building a plan consumes no randomness at all
now, so there is nothing left for that clause to say.
### Tripped a rule and left as it stands
- `Align::tuple`, `UiVec2::partial_align` and `Vec2::partial_align` have no
callers anywhere. All three pre-date this PR and are outside its diff, so
they belong to the pre-review-gate sweep rather than to this branch.
- `Vec2::align`/`partial_align` are `UiVec2`'s two functions again with
`UiVec2::from(*self)` in front. Also pre-existing, and merging them means
deciding whether `Vec2` should have them at all.
- `impl_op!` has four grammars for one macro, and `core/src/util/vec2.rs`
spells two of them one line apart -- `impl_op!(impl Add for Vec2: add x y)`
beside `impl_op!(Vec2 Sub sub; x y)`. The `impl ... for ...:` arm has that
one caller. Pre-dates this PR; the two arms this PR added (`same ...`) are
a real distinction, since a type mixing a fraction and an offset has no
meaning for a bare `f32`.
- `AxisAlign`, `RegionAlign` and `Align` lost `Eq` when `AxisAlign` became a
`Rel` wrapper, which `Rel` derives. Nothing needs it, so it was left rather
than adding a derive with no reader.
- `Holds::through` special-cases a fully unbounded range before inverting a
fraction, which the general path would also answer correctly through
`narrow`. Left: it is the one place a `Px::MIN`-to-`Px::MAX` interval is
shifted and divided, and the early return says that no fraction can narrow
"every length".
- `Scroll`'s `content_len <= container_len` can only be equality, given the
`max` that built it. Left: `<=` reads as "the content fits", which is what
the branch means, and the mirror `>` now reads as "it overflows".
- `scenario::Case::name`, `window` and `Shuffle::of` take `self` on `Copy`
enums. They are test-rig code the `&self` pass did not cover, and an enum
with no fields is the one place taking a value costs a caller nothing.
## Sixth sweep: the shader boundary and the position widgets (2026-09-20) ## Sixth sweep: the shader boundary and the position widgets (2026-09-20)
Over what the five earlier rounds did not name -- the WGSL prelude and how Over what the five earlier rounds did not name -- the WGSL prelude and how