diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index d6de49e..410823f 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -8,11 +8,9 @@ log. Canonical Iris `main` is **`ca2b4b2`** (#17, the headless rig). **#18 `split/18-position-chain`** is open in `/home/bob/repos/iris-pr18`; its local -head is **`8220a78`**, forty-nine commits, pushed to the fork. Built-in -alignment is **in the working tree at that checkout and not committed** -- see -"Built-in alignment, in flight" below before touching it. No PR reviews -were present when checked on 2026-09-15; the verification summary is posted on -the PR. +head is **`d3b0ebf`**, fifty commits. Built-in alignment is complete there; +see "Built-in alignment" below for the retained-layout details. No PR reviews +were present when checked on 2026-09-15. The current head completes LAYOUT.md ยง2's position chain and the requested `leftover` behavior. A child whose length is only `leftover` is not drawn when @@ -72,13 +70,20 @@ rewrite: `core/src/ui/holds.rs`, the retained tests, and the generated cold-layout oracle pin those rules. Seeds 10 and 86 are now in the ordinary generated set. -## Verification at `8220a78` +## Verification at `d3b0ebf` - `cargo fmt --all --check` +- `cargo build --workspace --all-features` - `cargo clippy --workspace --all-targets --all-features -- -D warnings` -- `cargo test --workspace --all-features`: 85 passed, 10 ignored -- `cargo test --release --test generated -- --ignored - a_long_run_of_seeds_agrees --exact`: 100 seeds passed in 67.6 s +- `cargo test --workspace --all-features`: 88 passed, 10 ignored +- The release generated cold-layout oracle passed 100 seeds in 65.5 s. +- The release shrinker passed both `repaint` and `resize` at 300 seeds each: + 26,001 widgets per case, largest tree 331 widgets. +- `tests/drift.rs` passed its 20,000-move exactness check in release mode. +- The seeded `random` example, live-resized from 1920x1200 to 1280x800, is + byte-identical to a cold 1280x800 render. Both PNGs hash to + `1d397c57b9914a2e596fa907029bc6aab4629e4d74deb0715e81325c703bcdb3`. + The run used the Venus adapter backed by the host RX 7900 XT. - `minimal`, `text` and `view` render byte-identical at 1920x1200 across `8220a78`. `tabs` differs only in the widget count it prints about itself, which is two wrapper types smaller -- so it is no longer a byte-identical @@ -90,7 +95,13 @@ pin those rules. Seeds 10 and 86 are now in the ordinary generated set. before and after replay. Every comparison had zero differing pixels. The live-resize image is also byte-identical to the cold 900x1200 image. -The final pre-submit review was run in four passes. The region-node review +The built-in-alignment pre-submit review was run in four passes. It caught +three distinctions the smaller tests had missed: an answer's validity must +include the drawing made in its final placed box; a region-node box is resolved +through its parent move rather than through its own move twice; and boxes with +equal dimensions but different positions still need the parent that placed +them. The 100-seed oracle and both 300-seed shrink cases pass after those +fixes. The earlier region-node review caught an index-reuse hazard when removing a node; its move entry now remains alive until every descendant has migrated. The generated oracle then exposed the exact `Span` threshold described above. The earlier retained-layout @@ -134,7 +145,17 @@ tighter figures. not, and `Scroll` never reasserts it while drawing. - The first box a parent asks about is the offer. A later box chosen from the child's answer is the final box, not another independent answer. Dirty - widgets are re-asked at the offer and only then drawn in the final box. + widgets are re-asked at the offer and only then drawn in the final box. An + offer composes through its ancestors' offers, not through their current + placed boxes. +- An answer is reusable only where both its measurement and the drawing made + in its final placed box remain valid. The final drawing's `Holds` interval + is translated back into lengths of the offered box and intersected with the + answer's interval. +- Equal box lengths do not imply equal placement. An ordinary widget whose + offered and current boxes differ in position must involve its parent again; + a region node can settle itself only when its own alignment, rather than a + container override, determines the final box. - A retained drawing can be reused only when its `Holds` interval contains the new pixel box on both axes, its parent node is unchanged, its region-node choice matches the retained structure, and the widget is clean. A valid @@ -176,13 +197,11 @@ tighter figures. being paid for. The generated oracle compares within 0.05 and `unsettled.rs` runs too few frames to reach it, which is why `tests/drift.rs` exists. -## Built-in alignment, in flight +## Built-in alignment -Uncommitted in `/home/bob/repos/iris-pr18` on top of `8220a78`: 87 tests, fmt -and clippy clean, the 100-seed generated oracle green, and the shrinker's -`resize` case green at 300 seeds. The shrinker's **`repaint` case is red**, for -the reason in "Placement cannot be applied after the fact" below. Its `reorder` -case is red at `8220a78` too, so that one is **pre-existing and unrelated**. +Committed as `d3b0ebf` in `/home/bob/repos/iris-pr18`. The shrinker's +`reorder` case remains red at its base `8220a78`, so that failure is +pre-existing and unrelated. What is in it: `align` is a widget property beside `region_node` and the size rule, `Aligned` is deleted, `.align()`/`.center()` set the property, and the @@ -192,10 +211,10 @@ runtime. Region nodes had **no generated coverage at all** before that. ### Alignment is two fractions, not four directions Decided 2026-09-15 (Bryan). A widget's alignment is one `f32` per axis, so a -quarter of the way along an axis is expressible. `AxisAlign`'s three cases -become named constants over that number, which is what every expression +quarter of the way along an axis is expressible. `AxisAlign`'s three familiar +positions are named constants over that number, which is what every expression already uses: the layout math only ever reads `AxisAlign::rel()`, so nothing -downstream changes shape. The in-flight tree still has the enum. +downstream changes shape. The default is **the middle on both axes**, because the two edges are the ones that assume a direction -- which edge is the near one depends on the writing @@ -220,7 +239,7 @@ state carries it. the top widget's `placed` is recomputed while an ancestor's shift carries the whole subtree. Measured 7,048,813 where a cold layout says 456. -The replacement, not yet written: alignment applies in the two places that +The replacement applies alignment in the two places that already exist and are exact. Where the size is known before drawing, from a rule, `declared_box` hands the child its aligned box directly -- one draw, no move. Where the size is only known after drawing, the widget is **re-asked in