Commit Graph
6 Commits
Author SHA1 Message Date
iris-aiandClaude Opus 5 1b1378b05a Branch on a measurement, so a wrong one shows as a different tree
Comparing boxes catches a widget that moved. It does not catch one that
measured a child, was handed an answer a cold start would not have given,
and took the other branch -- the same defect, arriving where a pixel
comparison cannot see it. Branching on what the painter tells you is
something a widget is allowed to do, so the library owes the same answer
warm and cold; only a widget changing its own configuration is exempt.

`random::Branch` measures a child and draws one of two others on the
result, with both grown either way so the ids match whichever is drawn.
It joins the generator, which makes every existing scenario a control-flow
oracle as well as a geometric one. `tests/determinism.rs` is the same
widget by hand across eight thresholds, including either side of the
answer, and is the fast check -- the sweep is a fuzzer and confirms at the
end rather than being iterated against.

A span behind a branch nobody took is not drawn, so shuffling it cannot
move anything; `reshuffled` now treats that as vacuous, the way it already
treats a tree with no spans, rather than as a shuffle that had no effect.

Both new tests pass, and the sweep passes at depth 4 and 5 over 200 seeds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 00:06:54 -04:00
iris-aiandClaude Opus 5 7c50a3e51b Rename a length's abs component to px
`dp` is coming, and then `abs` says which of the two it is not. The
component has always been a pixel count, so the name that admits it is
the one that leaves room for a second unit beside it.

Mechanical: the field on `Len` and `UiScalar`, their constructors,
`to_abs`/`get_abs`, the matching WGSL struct member and the locals
composing it. Field order and types are unchanged, so the `Pod` layout
the shader reads is the same bytes. `f32::abs` is untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 22:29:20 -04:00
iris-ai 84f589e364 Settle dirty layout from the leaves upward 2026-09-14 15:56:44 -04:00
iris-aiandClaude Opus 5 cdec29351a Grow scrolling into the random trees
Scrolling is the one thing in these trees that reads the pixel length of its
box, and the one that hands its child a box longer than its own, so a warm
layout under it has to be rebuilt where the rest can be carried over. A
sixth of the nodes at each level is now a scroll over a subtree, on either
axis.

Four of a hundred seeds now grow nothing but wrappers, so `reshuffled`
returns early where there is no span to shuffle: a case with nothing to do
is not the same as a shuffle that had no effect, which is what the assertion
below it is for.

50 tests, and the ignored sweep over 100 seeds and eight scenarios, 800
comparisons.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 14:35:20 -04:00
iris-aiandClaude Opus 5 4178dfbff9 Grow padding into the random trees, and take children in and out of spans
Padding as a node, with each of the four sides its own number: a padding that
is the same all round hides anything that treats one edge differently from
another. Spans now hold two to four children, so a pattern of removals has
something to make a pattern out of.

Five ways of changing what a span holds, each a shape worth its own case
rather than one shuffle: every other child out, everything but the first out,
three on at once, the first out and three on, and one out of the middle with
one on the end. Each is applied to every third span, and the cold tree is
grown holding exactly what the warm one was left with.

Three spare leaves are grown beside every span whether they end up in it or
not, so a tree that leaves them out makes the same widgets in the same order
as one that puts them in -- otherwise the two trees' `ids` stop lining up at
the first difference and every comparison after it is against the wrong
widget. Attaching one moves it, since a widget belongs to one parent;
`upgrade` is for a weak handle that was never added, not a second share. The
detached children are held until the comparison is over for the same reason:
dropping the last share of one frees its id for the next widget to be given.

Each case asserts the tree actually changed before comparing, so a shuffle
that quietly did nothing fails rather than passes.

All of it agrees: 49 tests, and the ignored sweep over 100 seeds and eight
scenarios, 800 comparisons.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 13:48:42 -04:00
iris-aiandClaude Opus 5 86a7e8dfc3 Grow random trees, and check them against building the same tree cold
`iris::random` grows a seeded tree -- spans in every direction, stacks,
rects with varying opacity, text both wrapping and overflowing, a declared
size over half of it -- and `tests/generated.rs` grows each seed twice: once
and then mutated, once with the mutation built in. Every widget's box has to
match. `examples/random.rs` draws one, and `IRIS_SEED`/`IRIS_DEPTH` pick it.

It found the defect in the commit before this one immediately: a reuse that
marked a descendant for redraw escalated to that descendant's size reader,
which re-placed the child, which marked it again. `try_reuse` now asks
whether anything under the widget would have to be drawn again *before*
keeping the drawing, and drops the whole thing if so, which terminates
because it adds no marks.

It also found one older and larger than this branch, which
`a_wrapping_child_of_a_row_settles_somewhere_else_each_time` reproduces and
documents: a wrapping text on a span's own axis is shaped twice against two
different widths, so where it settles depends on how many passes it has had.
7 of 90 cases diverge on `db1751f` and 30 do here, because a placed child
reaches the second shaping more often. It is the same defect either way, and
it belongs where the two draws meet -- LAYOUT.md §4 -- not in the chain. The
six seeds the live tests use are ones that agree.

`forget_ref` goes with the subtree rewrite that used it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 13:06:09 -04:00