diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index 334bd3a..9160c3b 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -6,8 +6,8 @@ found is in `docs/LAYOUT_LOG.md`. ## The Iris layout repair is submitted -**Iris PR #19** (`layout/one-ask`) replaces closed #18. The tip is `77ed7a2`, -and past the reviewed `cadfba0` it is ten rounds, each described in +**Iris PR #19** (`layout/one-ask`) replaces closed #18. The tip is `c2b8bf8`, +and past the reviewed `cadfba0` it is eleven rounds, each described in `docs/LAYOUT_LOG.md`: - **The repair**, `add6774` and `84dad21` -- collapsed-share placement, @@ -44,6 +44,12 @@ and past the reviewed `cadfba0` it is ten rounds, each described in sitting at the top of the same file, the GPU rigs' adapter probe written twice, a mask resolved three times, a field nothing reads, a shrinker claim its own assertion does not make, and three stale numbers. +- **A sweep over the widget vocabulary and the eighth sweep's own fix**, + `c2b8bf8` -- a widget's own size hint overriding a length rule the hint + cannot express, marking a widget for redraw having no name at twenty-one + call sites, the helper the eighth sweep shared being unable to see the + defect it had just fixed, two bare arrays where the framework names the + pair, forty-five lines nothing references, and one word for two things. The settled design of the vocabulary rounds is in `docs/LAYOUT.md` under "Three names, and the one argument that says them". Bryan settled the API @@ -126,7 +132,10 @@ a compile error, which reads exactly like a fuzzer failure. The eighth adds a second lesson, about the tests themselves: a fixture a fuzzer shrank is a regression test, so a round that rewrites one has to prove the tree is the same rather than that the case still passes, since - a fixture that quietly changed still passes and covers nothing. + a fixture that quietly changed still passes and covers nothing. The ninth + adds a third: where a round fixed a class of defect one instance at a + time, put the check in the shared helper, which both closes the class and + tests the instances that round said were already fine. 2. **A review of everything written before the review gate existed.** `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 @@ -140,6 +149,8 @@ a compile error, which reads exactly like a fuzzer failure. `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. + `Align` now has `Index`, so the `if let Some` each + `partial_align` writes twice collapses when that sweep reaches them. 3. **Integrate the app's Iris capabilities before changing its pin.** `32f6ad8` has 45 commits not reachable from the review branch; shared UI ownership, richer masks, Android support, and app-side performance work diff --git a/docs/LAYOUT_LOG.md b/docs/LAYOUT_LOG.md index d921d99..f2b3988 100644 --- a/docs/LAYOUT_LOG.md +++ b/docs/LAYOUT_LOG.md @@ -6,6 +6,118 @@ nothing here is rediscovered. Each entry says who found it and when. it (settled design, the measurement method) belongs in `docs/LAYOUT.md`, and the current plan is in `docs/HANDOFF.md`. +## Ninth sweep: the widget vocabulary, and the eighth sweep's fix (2026-09-20) + +Over the part no earlier round named -- `src/widget/trait_fns.rs` and +`wrapper.rs`, `core/src/widget/widgets.rs`, the `util` additions, `examples/` +and the two manifests -- and once more over `77ed7a2`, which was the eighth +sweep's own commit. Eight findings, all in `c2b8bf8`. The cold dump over 400 +depth-5 trees is byte-identical to `77ed7a2` across all **34,488** boxes, and +all three seed scans pass (400 at depth 5 in 63.27s, 1,000 at depth 6 in +160.45s, 2,000 at depth 4 in 302.52s). + +**A hint overrode a rule.** `Widgets::declared_lens` asked +`rules[axis].declared()` and fell through to the widget's own `size_hint` +whenever that answered `None` -- which it does for a share, because a share is +not a declaration. So a widget carrying `width(leftover(1))` *and* hinting a +pixel length of its own was given a box of the hint, against the rule and +against the comment inside the function ("a hint still narrows the box where no +rule does"). `Painter::size_hint` spells the same rule-else-hint step three +hundred lines up and gets it right, with the reason written on it; both read +`Widgets::exact_len` now, and `declared_lens` is the part of its answer that +needs nobody to divide it. + +`Image` is the only widget in the repository whose hint is a declared length +(every other hints `LEFTOVER`, whose `declared()` is `None` either way), and +neither the tests nor the generator builds one, so nothing here could reach the +difference -- which is why the dump is unchanged, and why +`a_share_rule_beats_the_widgets_own_pixel_size` builds a widget of its own. It +records the box it was asked in: 400 with the rule and 50 without, and 50 +either way at `77ed7a2`. The alternative reading -- a hint narrows even under a +share rule -- would mean changing that comment instead, and is Bryan's to +prefer if he does. + +**Marking a widget for redraw had no name.** Twenty-one sites under `tests/` +said it as `widgets_mut().get_dyn_mut(id);` with the widget thrown away: five +with a `let _ =` in front, one with a comment explaining what the line was for +("taking mutable access is the ordinary content-change signal"), and one inside +a local function already called `mark`. The framework's own word for it is in +its comments -- "marked for redraw" -- and `Widgets::mark_for_redraw` is now +the method. `revision_cost.rs` keeps the long spelling and says why in place, +since it is deliberately in the API subset an old worktree also has. + +**`assert_same_regions` could not see the defect the eighth sweep had just +fixed.** It zips the warm and cold id lists, so a list naming one widget twice +compares fewer boxes than it lists and reports nothing. It now rejects a +repeated id and two lists of different lengths, which is the same check applied +to the whole class rather than to the four fixtures that had it wrong -- and it +verifies that round's claim about the other nine: all eighteen cases pass. + +**Bare pairs where the framework has named ones.** `random.rs`'s `Lens` and +`Aligns` were `[Option; 2]` and `[Option; 2]`, read as +`[0]`/`[1]` and zipped against a hand-written `[Axis::X, Axis::Y]` in two rigs. +They are `SizeRules` and `Align`, which is what the framework calls those +pairs; `Align` took the `Index` every other per-axis pair on this branch +has, and `RegionAlign::from` does the "an axis left out is centred" step both +rigs were spelling per axis. The three sites that wrote the axis pair out say +`Axis::BOTH`, which the layout core already says ten times. The plan's +`Debug` is why `Align` gained one. + +**Forty-five lines nothing references.** `BothAxis`, `AxisT`, `XAxis` and +`YAxis` -- a const trait, two marker types and three accessors -- have no user +anywhere in the workspace, and are the mechanism `impl_axis_index!` replaced, +in the very file this branch took `Vec2::axis`/`axis_mut` out of. Deleted as a +drive-by in a block the branch was already rewriting, the way `MASK_NONE` was; +drop it if the scope matters more. They pre-date the PR, so a plain "is this +name used" scan over the diff does not surface them. + +**One word for two things.** `Wrapper` (the widget) arrived on this branch +beside core's `WidgetWrapper` (a dynamic borrow guard), both in the prelude. +The alias had two uses in one file and `DynBorrower` is what they +are, so it is gone rather than renamed. `Wrapper::new`, `Wrapper::empty` and +its hand-written `Default` were three names for one value, two unused. + +**Smaller.** `Arena::get_mut` was the only `pub(crate)` among `pub` siblings on +a public type. `Selector` rounded the pointer onto the pixel grid in order to +add two values already on it, losing the precision the platform gave it for +nothing -- the step between the two regions is taken on the grid instead, which +also makes it agree with `Selectable`, the other caller of `select`. And the +two `debug` profile settings now carry their reason where the next reader +looks: `profile.dev`'s was added in a commit about renaming `rest` and +explained nowhere, and `profile.test`'s only in the message of the commit that +made the tests one target. + +### Tripped a rule and left as it stands + +- `examples/random.rs` is a viewer for the fuzz generator rather than a + demonstration of a feature, which is what an example is for here. Left: + `scripts/run-headless.sh` opens an example by name, so this is how a + generated tree is put on a screen at all. +- It also carries a fifth copy of the six-line `env` helper, on top of the four + under `tests/`. Same answer as the eighth sweep gave: sharing it means a new + file for six lines of `std`. +- `IRIS_SEED`/`IRIS_DEPTH`, `IRIS_GENERATED_SEED(S)`/`IRIS_GENERATED_DEPTH` and + `IRIS_DUMP_SEEDS`/`IRIS_DUMP_DEPTH` are three names for two knobs. Left: the + prefixes are what lets one shell set a rig's seed without changing another's, + and the two rigs that grow the same tree from the same number do share the + unprefixed pair. +- `set_size_rule` takes a `SizeRule` while `set_size_rules` takes + `impl Into` per axis, so seven callers write `SizeRule::Exact(len)` + where two write `Some(len)`. Left: widening the single-axis one makes + `harness.rs`'s `len.into()` ambiguous between two conversions. The doc that + called it "for a caller holding a pair" -- which it is not, since it takes + two values -- now just says both axes at once. +- `Wrapper::set` is `replace` with the result dropped, and both have a caller + in `examples/tabs`. Left: it is `Option::insert` beside `Option::replace`, + and the tab bar wants each. +- `Masked`'s size comment credits `Scroll` with the same reasoning rather than + restating it. Left: the reason it gives is its own ("it clips what is inside + to that box"), and the cross-reference is to a design parallel, not an API. +- The nine `RegionAlign`/`CardinalAlign` constants include four nothing names + (`TOP_CENTER`, `CENTER_RIGHT`, `BOT_CENTER`, `V_CENTER`). Left: they are one + vocabulary of nine positions and six cardinals, and deleting the members + nobody has needed yet is the rule written on one member of a set. + ## Eighth sweep: the tests, and the seventh sweep's fix (2026-09-20) Over the part no earlier round named -- the 6,300 lines under `tests/`, which