Update alignment handoff

This commit is contained in:
iris-ai committed 2026-09-15 23:13:40 -04:00
1 parent 085fc97334
commit 76334cd5aa
1 file changed
+40 -21
+40 -21
View File
@@ -8,11 +8,9 @@ log.
Canonical Iris `main` is **`ca2b4b2`** (#17, the headless rig). **#18 Canonical Iris `main` is **`ca2b4b2`** (#17, the headless rig). **#18
`split/18-position-chain`** is open in `/home/bob/repos/iris-pr18`; its local `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 head is **`d3b0ebf`**, fifty commits. Built-in alignment is complete there;
alignment is **in the working tree at that checkout and not committed** -- see see "Built-in alignment" below for the retained-layout details. No PR reviews
"Built-in alignment, in flight" below before touching it. No PR reviews were present when checked on 2026-09-15.
were present when checked on 2026-09-15; the verification summary is posted on
the PR.
The current head completes LAYOUT.md §2's position chain and the requested 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 `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 `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. 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 fmt --all --check`
- `cargo build --workspace --all-features`
- `cargo clippy --workspace --all-targets --all-features -- -D warnings` - `cargo clippy --workspace --all-targets --all-features -- -D warnings`
- `cargo test --workspace --all-features`: 85 passed, 10 ignored - `cargo test --workspace --all-features`: 88 passed, 10 ignored
- `cargo test --release --test generated -- --ignored - The release generated cold-layout oracle passed 100 seeds in 65.5 s.
a_long_run_of_seeds_agrees --exact`: 100 seeds passed in 67.6 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 - `minimal`, `text` and `view` render byte-identical at 1920x1200 across
`8220a78`. `tabs` differs only in the widget count it prints about itself, `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 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 before and after replay. Every comparison had zero differing pixels. The
live-resize image is also byte-identical to the cold 900x1200 image. 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 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 alive until every descendant has migrated. The generated oracle then exposed
the exact `Span` threshold described above. The earlier retained-layout the exact `Span` threshold described above. The earlier retained-layout
@@ -134,7 +145,17 @@ tighter figures.
not, and `Scroll` never reasserts it while drawing. not, and `Scroll` never reasserts it while drawing.
- The first box a parent asks about is the offer. A later box chosen from the - 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 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 - 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 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 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` 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. 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 Committed as `d3b0ebf` in `/home/bob/repos/iris-pr18`. The shrinker's
and clippy clean, the 100-seed generated oracle green, and the shrinker's `reorder` case remains red at its base `8220a78`, so that failure is
`resize` case green at 300 seeds. The shrinker's **`repaint` case is red**, for pre-existing and unrelated.
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**.
What is in it: `align` is a widget property beside `region_node` and the size 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 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 ### Alignment is two fractions, not four directions
Decided 2026-09-15 (Bryan). A widget's alignment is one `f32` per axis, so a 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 quarter of the way along an axis is expressible. `AxisAlign`'s three familiar
become named constants over that number, which is what every expression positions are named constants over that number, which is what every expression
already uses: the layout math only ever reads `AxisAlign::rel()`, so nothing 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 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 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 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. 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 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 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 move. Where the size is only known after drawing, the widget is **re-asked in