The sweep is Iris `97fca76`. Its two largest findings are both costs per thing rather than per frame: a container's draw searched a list once per child and so cost the square of its children, and a mask's rectangle was resolved once per fragment rather than once per instance. Both now have a rig that can see them -- `children_cost.rs` is the only one here that varies width, and `chain_cost.rs` has a masked fixture beside its two-pixel quads. The sweep also settles the `Fixed::div`-versus-`ratio` item this file listed, by deleting the operation nothing performed, and adds to the pre-gate review's waiting list the `Arc<AtomicU32>` every `StrongWidget` allocates for a count it can never raise.
463 lines
28 KiB
Markdown
463 lines
28 KiB
Markdown
# Handoff
|
||
|
||
Where the work in flight stands. The settled layout design, the vocabulary
|
||
and the measurement method are in `docs/LAYOUT.md`; what the review of #19
|
||
found is in `docs/LAYOUT_LOG.md`.
|
||
|
||
## A sweep for allocation, and the cost per child, is on #19
|
||
|
||
The fourteenth sweep landed at `97fca76`. It read the whole branch again,
|
||
aimed first at what allocates and then at whatever the reading turned up.
|
||
Eight findings, the largest two both about cost per thing rather than per
|
||
frame:
|
||
|
||
- **A container's draw was quadratic in its children.** Five per-child steps
|
||
each asked "have I done this one already?" by searching a list; at 1,600
|
||
children 70% of the redraw was those searches. A draw now takes a `DrawId`
|
||
and leaves it on every widget it asks about, which answers the question in
|
||
one read -- one note per widget suffices because the handle a container holds
|
||
a child by cannot be cloned. `tests/children_cost.rs` is new and is the only
|
||
rig here that varies width: 3.680 ms to 0.811 ms at 1,600 children, and flat
|
||
per child at every count. A transcript is exactly this shape.
|
||
- **A mask's rectangle was resolved once per fragment**, a walk up to 64 links
|
||
long, although it is the same rectangle for every fragment of an instance.
|
||
Resolved in the vertex stage now, which takes `masks` and `move_offsets` out
|
||
of the fragment stage entirely. GPU timestamps over one screenful of clipped
|
||
rows: 838.4 us to 95.7 us at chain depth 64, 141.2 to 69.4 at depth 8, and
|
||
unchanged at depth 1. `chain_cost.rs` could not see this -- its instances are
|
||
two pixels wide so vertex work dominates -- and has a masked fixture now.
|
||
|
||
The other six: `TextBuffer::shape` copying its attrs before the check that
|
||
would not need them, which was one allocation per named-family text per frame
|
||
and 800 to 0 over a measured hundred frames; `cargo test --release` failing,
|
||
because a `#[should_panic]` test asserts a `debug_assert` release does not
|
||
compile; `Fixed::div` and `Div for Fixed`, reached only by their own test,
|
||
whose deletion also settles the `div`-versus-`ratio` zero-divisor item this
|
||
file used to list; `Moves::remove` re-uploading an array it cannot have
|
||
changed; and two comments this branch's own commits falsified -- one saying
|
||
`widget_trait!` takes no attributes after `76aaf06` taught it to, one saying
|
||
`Scroll` clips when masking is a capability a caller opts into with `Masked`.
|
||
|
||
It also left five things, with the numbers. Text allocates about six times per
|
||
re-broken paragraph per frame, and removing all of them is **0.07%** of
|
||
instructions (3.7107B to 3.7081B over 500 sweeping resize frames of 40
|
||
paragraphs), so the count is recorded rather than the machinery built --
|
||
allocation count and cost are not the same quantity, and this is where they
|
||
part. `StrongWidget` carries an `Arc<AtomicU32>` it can never use, since it has
|
||
no `Clone`; that and seven dead items older than #19 are listed under the
|
||
pre-gate review below rather than fixed here.
|
||
|
||
`docs/LAYOUT_LOG.md` has all of it. Verified: format, clippy both ways, 190
|
||
ordinary, 193 diagnostic and 189 release tests, the cold dump byte-identical to
|
||
`cbccfb6` across all 34,986 boxes, all three seed scans, the three deferred
|
||
corpora, and `mask_clip.rs` reading the clipped pixels back off the GPU to the
|
||
same 6,000 in the same bounds before and after the shader change.
|
||
|
||
## Built-in bounds replace `MaxSize` on PR #19
|
||
|
||
Bryan clarified that the built-in attribute should replace the wrapper.
|
||
Work is in `/home/bob/repos/iris-deferred`, on `layout/one-ask`, at
|
||
`cbccfb6` (pushed). Bounds now constrain the offer in `Placing::ask`;
|
||
declarations are retained as resolved window lengths, so movement does not
|
||
decide the bound again.
|
||
`.max_width`/`.max_height` now edit the same widget, like the minimum
|
||
helpers, and `MaxSize` is removed. Preferred lengths and bounds are
|
||
independent, so either order of `.width` and `.max_width` works. The app pin
|
||
is unchanged.
|
||
|
||
The review fixed three cases: `window_holds` erasing an ask's bound crossing
|
||
(seed 104, depth 5), inactive bound edits failing to update the parent's offer
|
||
dependencies (seed 144, depth 6, reduced further to three widgets), and a
|
||
fractional intrinsic cap being read against its already-allocated slot.
|
||
The latter has an absolute 75-in-300 geometry check; warm/cold agreement
|
||
alone cannot catch it.
|
||
|
||
206 ordinary and 210 diagnostic tests pass, with warning-clean Clippy both
|
||
ways (207 and 211 at `2ac0843`, less the duplicate test the sweep merged).
|
||
All three layout scans pass (400 depth 5, 1,000 depth 6, 2,000 depth 4), as
|
||
do 400 depth-5 trees in each of three deferred-request corpora. All 34,986
|
||
unbounded cold boxes match `4cb6f68`; bounded offers intentionally change.
|
||
The final unbounded-path optimization preserves every bounded cold box too.
|
||
|
||
Five-run release instruction medians show capped rows using 21.77–29.64%
|
||
less CPU work than the wrapper. Plain/fixed-width retained resizes differ by
|
||
under 0.05%; forced redraws improve about 0.3%. Text workloads differ by at
|
||
most 0.13%. Independent preferences and bounds cost 32 extra inline bytes per
|
||
widget; all three allocation fixtures remain allocation-free after warm-up.
|
||
`docs/LAYOUT.md` records the contract and measurements. Artifacts are in
|
||
`/tmp/attribute-bounds/`.
|
||
|
||
## A request is held in the arena its nodes are allocated in
|
||
|
||
`05e6ced`. `SizeRequest` was a second expression shape beside the one
|
||
`RequestArena` already had -- the same three operators, the same constant fold
|
||
written twice, and an `import` that walked a pointer tree rebuilding it as
|
||
arena nodes. Bryan settled it on 2026-09-20: one node type, an arena, and no
|
||
refcount, since nothing shares a request and nothing outside widget code holds
|
||
one. A rule's expression now holds a small arena of the pass's own node type,
|
||
and `import` grafts those nodes across. A plain length stays inline, so
|
||
`size_of::<SizeRule>()` is 40 either way. `bounds_cost` `MODE=cap` is -1.34%
|
||
and `revision_cost` resize -0.79% in release instruction medians.
|
||
`docs/LAYOUT_LOG.md` has the full entry. Verified with all three seed scans,
|
||
the three deferred corpora, 206 and 210 tests, and the cold dump byte-identical
|
||
to `2ac0843` across all 34,986 boxes.
|
||
|
||
## A full sweep of #19 is submitted on it
|
||
|
||
The thirteenth sweep landed at `cbccfb6`. Unlike the twelve before it, it
|
||
read the whole branch rather than one commit, and it is the first review of
|
||
`f48e04e`. Seven findings, the largest being that a length of zero printed as
|
||
the empty string: `Display for LayoutLen` leaves out each part that is zero,
|
||
and `f48e04e` had just pointed `Debug` at `Display`, so a request of zero
|
||
printed as nothing in the only place requests are compared -- and
|
||
`scenario::describe` printed a `.width(0)` rule as `-`, which is what it
|
||
prints for a widget with no rule at all. Also: `Fixed::ceil_from_f32` stepped
|
||
past the top of the grid and wrapped, so the largest measurement came back as
|
||
the most negative length; `Moves::depth` walked the move chain a second way
|
||
with its own copy of `CHAIN_LIMIT`; `Harness::set_len` claimed to set a
|
||
length "the way `.width()` sets one" and dropped any bound beside it; three
|
||
rigs each spelled "one seed, or a range of them" by hand; `diag::outside`
|
||
could count a refused reuse and explain it with nothing; and
|
||
`cases/deferred` sat outside `suite.rs`'s alphabetical list.
|
||
|
||
It also re-measured a recorded hole. `Sow::bound` grows bounds in pixels
|
||
because two depth-5 trees once disagreed warm against cold with fractions in
|
||
them -- named by seed, and `generated.rs` says in its own comment that seeds
|
||
stopped naming those trees when the leaves grew images. 600 depth-5 trees
|
||
over all sixteen cases now agree with every bound a fraction (93.18s). The
|
||
generator still grows pixels, because `deferred_generated.rs` already varies
|
||
that dimension and growing fractions here would move every box in the cold
|
||
dump; the comment says that instead of describing an open defect.
|
||
|
||
`docs/LAYOUT_LOG.md` has all seven, with the four things the sweep looked
|
||
at and left and the one it withdrew. Verified: format, clippy both ways,
|
||
208 and 212 tests, and the cold dump byte-identical to `f48e04e` across all
|
||
34,986 boxes.
|
||
|
||
## A quality sweep of the request arena is submitted on PR #19
|
||
|
||
The twelfth sweep landed at `f48e04e`. It is the first review of `05e6ced` --
|
||
one node type for a request, an arena per owner, and `Widgets::edit_bound`.
|
||
Four findings, the largest being that the one path copying a request into
|
||
another arena was run by nothing: all 206 tests passed with a `panic!` in it,
|
||
because every expression in the corpora and in the cases compares an
|
||
expression against a plain length. A fixture with absolute geometry and a
|
||
fourth arm in the deferred corpus cover it now, and the path was already
|
||
correct. The other three: `Debug` on a request still printed the derived
|
||
arena its own new `Display` was written to avoid, a method asked nothing of
|
||
its receiver, and a closure parameter shadowed the rule it was called on.
|
||
`docs/LAYOUT_LOG.md` has all four, with the four rules it tripped and left.
|
||
Verified: format, clippy both ways, 207 and 211 tests, the cold dump
|
||
byte-identical to `05e6ced` across all 34,986 boxes, and 400 depth-5 trees in
|
||
each of the three deferred corpora in 200.95s.
|
||
|
||
## A quality sweep of the bounds work is submitted on PR #19
|
||
|
||
The eleventh sweep landed at `ea1f836`. It is the first review of `2ac0843`
|
||
-- `SizeRule` from an enum to a preferred length beside an independent bound,
|
||
the offer constrained in `Placing::ask`, and `MaxSize` removed. Eight
|
||
findings, the two largest being a hint read that answered "cannot say" above
|
||
the diagnostics and so counted as no read at all, and five spellings of
|
||
reading one environment variable across the rigs, which is the class the
|
||
tenth sweep found one commit earlier -- there is now one `env` in
|
||
`tests/rig/`, used by all six. `bounds_cost` also verified 128 regions inside
|
||
its measured loop; the check moved out, and the 0.65% it measured is written
|
||
beside it. `docs/LAYOUT_LOG.md` has all eight, with the two rules it tripped
|
||
and left. Verified: format, clippy both ways, 206 and 210 tests, 400 depth-5
|
||
trees warm against cold in 64.19s, and the cold dump byte-identical to
|
||
`2ac0843` across all 34,986 boxes.
|
||
|
||
## A quality sweep of the deferred system is submitted on PR #19
|
||
|
||
The tenth sweep landed at `4cb6f68` in `/home/bob/repos/iris-deferred`. It is
|
||
the first review of `76aaf06` through `0e838e9` -- the bound rules,
|
||
the deferred request path and the invalidation fix. Ten findings, the largest
|
||
two being a bound's held length looked up a second time through a value that
|
||
could not promise the end it named (`Bound::at` `expect`ed it), and a bound in
|
||
pixels pinning the rel base it is not read against. `docs/LAYOUT_LOG.md` has
|
||
them all, with the two rules it tripped and left and two corpus questions for
|
||
Bryan. Verified: format, clippy both ways, 197 and 201 tests, all three seed
|
||
scans (69.02s, 174.87s, 330.39s), and the cold dump byte-identical to
|
||
`0e838e9` across all 34,986 boxes.
|
||
|
||
## Performance sweep is submitted on PR #19
|
||
|
||
The performance fix is `0e838e9`. It keeps dependencies only for size requests the allocator uses, and borrows
|
||
`Widgets::size_rules` rather than cloning both axes at every lookup. Used
|
||
hints retain their reader dependency; adding a cap after layout is checked for
|
||
both hinted and measured shares. Ordinary text measurement still tracks its
|
||
own dependencies. The app's Iris pin is untouched.
|
||
|
||
Repeated release instruction counts reproduce the reported +5.9% resize cost
|
||
and find a larger defect: an unchanged paragraph repaint invalidated its span,
|
||
turning 0.376B instructions into 3.010B in a 40-paragraph, 2,000-frame fixture.
|
||
The fix is 0.370B. Text resize is now +0.99% against `de1eb7e`, the width sweep
|
||
+1.23%, and edits/scroll are within 0.3%. `docs/LAYOUT_LOG.md` has the full
|
||
performance sweep, including deep-tree regressions, RSS, upstream-base
|
||
comparison, and GPU timestamp measurements. This is not a universal speedup.
|
||
|
||
Passed: format, workspace Clippy with and without diagnostics, all 197 ordinary
|
||
and 201 diagnostic workspace tests, zero steady allocation fixtures, 400
|
||
depth-5 / 1,000 depth-6 / 2,000 depth-4 trees, 400 depth-5 expression trees,
|
||
400 relative-bound trees, GPU recording and move-chain rigs, and identical
|
||
cold geometry for all 34,986 bounded boxes against `8780b40`. Code and comments
|
||
were reviewed separately; the final test/comment refinements passed their
|
||
focused checks. Artifacts are under `/tmp/pr19-perf/`.
|
||
|
||
## Deferred comparisons are submitted on PR #19
|
||
|
||
The deferred-comparison implementation landed at `8780b40`, before the
|
||
performance correction above.
|
||
`/home/bob/repos/iris` remains on `layout/bounds`; that branch's `de1eb7e` was
|
||
fast-forwarded into #19 before this work. The app's Iris pin is untouched.
|
||
|
||
The new request path composes min/max/clamp before assigning leftover slots,
|
||
while ordinary widgets keep returning `Size`. Known requests skip provisional
|
||
painting; measured leaves can complete a nested expression. The implementation
|
||
uses a reusable expression arena and retained draw buffers. The allocation rig
|
||
checks zero allocations after warm-up for unchanged plain and clamped trees.
|
||
Generalized cross-axis maxima are deferred because hidden main-axis shares
|
||
must not contribute to them. The current design is in `docs/LAYOUT.md`.
|
||
|
||
## The Iris layout repair is submitted
|
||
|
||
**Iris PR #19** (`layout/one-ask`) replaces closed #18. Before the deferred
|
||
comparison work above, its review rounds past `cadfba0` are described in
|
||
`docs/LAYOUT_LOG.md`:
|
||
|
||
- **The repair**, `add6774` and `84dad21` -- collapsed-share placement,
|
||
retained mask ownership, a redraw-on-reparent defect, and repeated work in
|
||
the test harness.
|
||
- **The vocabulary and the container API**, `5642f20` through `58ce74d`.
|
||
- **Naming**, `55df32a` through `40b89c1`.
|
||
- **A sweep over the logic those names exposed**, `8d2b7a5` and `6c84b6f`.
|
||
- **A clarity sweep**, `3da1c71` through `1ebd4d3` -- naming the pairs layout
|
||
returns, `Span::slot`, a diagnostic that printed the rel base while calling
|
||
it the box, and `in_parent` matching a place's own cases.
|
||
- **A quality sweep**, `aea0387` through `69ba915` -- a kept contract judged
|
||
against the placed box rather than the box asked about, two things nothing
|
||
read, three reuse rejections the diagnostics could not see, and a fuzz case
|
||
that ran only in the long scan.
|
||
- **A sweep over the renderer, the text store and the retained path**,
|
||
`d8d5122` through `1096c31` -- a contract kept where the new window left
|
||
it out, a surface configured under its own texture, a counter naming the
|
||
wrong contract, things nothing reads, and a question asked through a value
|
||
rather than a reference.
|
||
- **A sweep over the shader boundary and the position widgets**, `b7b8d09` --
|
||
two constants the shader and the CPU both count in written twice, and a
|
||
`Scroll` positioning content the framework positions, which cost a redraw
|
||
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.
|
||
- **A sweep over the tests and the seventh sweep's own fix**, `77ed7a2` --
|
||
four shrunk fixtures naming one widget under three names and counting
|
||
each in the list of boxes the case compares, seven copies of a helper
|
||
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.
|
||
- **A leftover as a minimum, and images in the trees**, `b295c8b` and
|
||
`2dba90b` -- a share under a parent that divides nothing losing the
|
||
overflow its pixels asked for, said as the place the parent gives and
|
||
sharing one comparison with the span; and `Image` grown in the generated
|
||
trees, which is the only widget here whose hint is a length in pixels.
|
||
- **One ask, the root's included**, `0d03267` -- the root had a layout path
|
||
of its own, so a rule that reads the box it is offered reached every widget
|
||
but that one. `Placing::WINDOW` is the box nobody drew and `Placing::ask`
|
||
the one place a box is decided; the root's own path is now the bookkeeping
|
||
a widget with no parent keeps. Bryan asked for this rather than a widget
|
||
above the root, which would pin the tree to pixels.
|
||
- **The deferred request system's own sweep**, `4cb6f68` -- described at the
|
||
top of this file and in full in `docs/LAYOUT_LOG.md`.
|
||
- **The bounds work's own sweep**, `ea1f836` -- the same, over `2ac0843`.
|
||
- **One node type for a request**, `05e6ced` -- the expression a rule holds
|
||
and the expression the pass allocates were two shapes of one thing, with
|
||
the fold written twice and an `Arc` where nothing shares.
|
||
- **The request arena's own sweep**, `f48e04e` -- the join that copies one
|
||
request's nodes into another's, which nothing in the suite ran.
|
||
- **A full sweep of the branch**, `cbccfb6` -- a length of zero that printed
|
||
as nothing, a ceiling that stepped off the top of the grid, and a harness
|
||
setter that dropped the bound beside the length it set.
|
||
- **A sweep for allocation and the cost per child**, `97fca76` -- a
|
||
container's draw quadratic in its children, and a mask's rectangle resolved
|
||
once per fragment. Described at the top of this file.
|
||
|
||
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
|
||
over 2026-09-17 to 19; it is current, not frozen.
|
||
|
||
The core design remains sound. Round-to-nearest is still unchanged.
|
||
|
||
The Iris worktrees share Git storage. `ai-app-2/iris` stays on `app-pin` at
|
||
the app's `32f6ad8` pin until the integration below is ready.
|
||
|
||
### The branch layout, and the trap that used to be here
|
||
|
||
`main` is PR #19's base and tracks `upstream/main` (`iris/iris`), so the
|
||
obvious commands are the right ones:
|
||
|
||
git merge-base main layout/one-ask # ca2b4b2, the PR's base
|
||
git diff main...layout/one-ask # exactly what #19 changes
|
||
|
||
It was not always so. Until 2026-09-20 `main` tracked `origin/main` -- the
|
||
*fork's* line, which carries the app's 45 commits, is not an ancestor of
|
||
upstream's main, and sat four merged pull requests behind it (#10 parley
|
||
text, #12 pointer routing, #16 draw/size merge, #17 headless rig). Diffing
|
||
against it showed all four as this branch's work, which is how the parley
|
||
migration's undo path kept being reported as #19's; the sixth sweep lost
|
||
half a session to it before Bryan caught it, and the fourth and fifth
|
||
sweeps deleted `Painter::text_data` partly on the same false reading.
|
||
|
||
Bryan asked for the setup fixed rather than documented. What changed:
|
||
|
||
- The app's line is now the branch **`app-pin`** (`32f6ad8`), pushed to
|
||
`origin`. That is the name to use for it; it is no longer called `main`.
|
||
- `main` points at and tracks `upstream/main`.
|
||
- `.gitmodules` pins the `iris` submodule to `branch = app-pin`, so
|
||
`git submodule update --remote` follows the app's line and cannot drag
|
||
the pin onto upstream.
|
||
- `git iris-base` and `git iris-diff` are still configured and still
|
||
correct; they now agree with plain `main`.
|
||
|
||
`origin/main` on the fork was force-pushed to match, and `origin/HEAD`
|
||
follows it, so a fresh clone of the fork with no `upstream` remote gets the
|
||
right base from the plain commands too -- verified by cloning one. The
|
||
fork is managed entirely by agents and nobody works from it directly, so
|
||
rearranging its branches is always safe where it improves the workflow
|
||
(Bryan, 2026-09-20); that licence does not extend to the `iris/`
|
||
repositories, which are what pull requests target.
|
||
|
||
The submodule's recorded commit is unchanged at `32f6ad8` and stays
|
||
reachable through `app-pin`, so nothing about the app's pin moved -- only
|
||
the name it is reachable by.
|
||
|
||
### How to check a round
|
||
|
||
**Always**, because they cost nothing: format, workspace clippy under
|
||
`-D warnings` with and without `layout-diagnostics`, the workspace tests, and
|
||
the **cold dump**. `layout_dump` over 400 depth-5 trees is **34,986** boxes
|
||
since `76aaf06` grew bounds in the trees, and is unchanged through `f48e04e`.
|
||
It was 34,571 from `2dba90b`, which grew the images, and 34,488 before those; the fourth through eighth sweeps all
|
||
repeated 34,492, which is a `wc -l` of the whole run rather than of its box
|
||
lines, so count the lines that are a box (`grep -cE '^[0-9]+ [0-9]+ '`). It is the only thing that catches two
|
||
same-typed values being swapped, which is the failure mode of a rename or a
|
||
move. The repair moved 650 of those boxes, all from the collapsed-share
|
||
correction. Subsequent review-only changes preserved cold geometry through
|
||
`4cb6f68`; the offered-bound change in `2ac0843` intentionally changes bounded
|
||
geometry while preserving all unbounded boxes.
|
||
|
||
**Only when the change can alter what layout computes**: the three seed scans
|
||
-- 400 at depth 5, 1,000 at depth 6, 2,000 at depth 4. They cost about a
|
||
quarter of an hour and they exist to find logic that is wrong on some tree
|
||
shape, so a rename has nothing for them to find (Bryan, 2026-09-19). Never
|
||
start one and then edit the tree: cargo rebuilds mid-flight and exits 1 from
|
||
a compile error, which reads exactly like a fuzzer failure.
|
||
|
||
## What is next, in order
|
||
|
||
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
|
||
whatever each one touched. The ordinary oracle does not replace absolute
|
||
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.
|
||
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. 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.
|
||
The twelfth adds a fourth, about coverage rather than about a defect: a
|
||
branch only a combination of the public API reaches can be reached by
|
||
nothing and still look covered, since every test around it passes. Put a
|
||
`panic!` in the arm and run the suite; it costs one build and it answers
|
||
the question the test names cannot.
|
||
The thirteenth adds a fifth, about the record rather than the code: a
|
||
comment describing an open defect by the seed that found it stops being
|
||
true the moment the generator changes, and `generated.rs` says so in its
|
||
own comment. Re-run the measurement before repeating what one says.
|
||
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
|
||
written, so all of that code went in unreviewed and none of the sweeps
|
||
above covered more than the layout branch. It wants a pass of its own
|
||
(Bryan, 2026-09-20). The surface-texture defect in `02048ea` is the
|
||
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
|
||
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.
|
||
`Align` now has `Index<Axis>`, so the `if let Some` each
|
||
`partial_align` writes twice collapses when that sweep reaches them.
|
||
The fourteenth sweep adds more: `StrongWidget` allocates an
|
||
`Arc<AtomicU32>` refcount it can never use, because it deliberately has no
|
||
`Clone`, so every widget pays a heap allocation and two atomic
|
||
read-modify-writes for a count that stays at zero, and `StrongWidget::refs`
|
||
has no caller; and `Size::to_uivec2`, `Size::rel`, `Size::leftover`,
|
||
`Len::to_uivec2`, `Vec2::with_x`, `Vec2::with_y`, `Vec2::ceil` and
|
||
`Layers::iter_orderless_mut` are all dead.
|
||
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
|
||
must survive the integration. What has to survive is those capabilities,
|
||
not the calls the app makes today: the app is to be largely rewritten
|
||
against the new API rather than ported call by call, so nothing in Iris
|
||
is kept alive for the app's sake (Bryan, 2026-09-20).
|
||
4. **Round-to-nearest**, CPU and shader together as one verified change.
|
||
Bryan approved it on 2026-09-17 and neither half has landed; the
|
||
derivation, the form to use and what to re-check are in `docs/LAYOUT.md`
|
||
under "Rendering the grid (pending)".
|
||
|
||
Wanted but not started, recorded in `iris/TODO`: transforms on a move entry,
|
||
so a whole subtree scales or rotates with one buffer write and no redraw.
|
||
Compose-style stretch at the end of a scroll area is the use that prompted
|
||
it. A move entry only translates today, and composing through one scales the
|
||
`rel` part while `px` passes through untouched, so fixed-size content and
|
||
glyphs do not follow a shortened entry.
|
||
|
||
## Smaller layout items, none urgent
|
||
|
||
- Nested spans pass `leftover` weight up, so three leftover children in one
|
||
inner span beside one in another get three quarters to one quarter. No
|
||
other layout system does that; confirm it is wanted.
|
||
- A span can overflow itself without bound, so boxes of negative length reach
|
||
children and nothing states what a widget may assume about one.
|
||
- A `leftover` under a parent that does not divide is a minimum size --
|
||
`max(box, px + rel*box)` (Bryan, 2026-09-20), which is a `SizeRule::Min` of
|
||
`rel(1.0)` and shares `Painter::longer_than` with the span. Done in
|
||
`b295c8b`, and in `0d03267` for the root as well, which used to read it the
|
||
old way.
|
||
- `docs/LAYOUT.md` §4, §5 and the density section name `Painter::place`,
|
||
`Painter::region()`, `SetSize`, `desired_width`, `apply_rest`, `Len::dp`,
|
||
`Aligned`, which no longer exist; `MaxSize` now exists with the bounds API.
|
||
Do not restore `OnResize::Translate` or `OrthoSize`.
|
||
- **`layout/bounds` (`de1eb7e`) is included in #19** for the deferred
|
||
comparison implementation. A rule holds what a widget answers
|
||
(`SizeRule::{Min, Max, Clamp}`); a widget holds the box (`MaxSize`, which
|
||
`.max_width`/`.max_height` build). Bryan settled the split on 2026-09-20
|
||
after four readings were measured; the reasoning is in `docs/LAYOUT_LOG.md`.
|
||
Bryan superseded that split with the built-in bounds work above. The
|
||
deferred-request work fixes stale relative-bound reuse by tracking the
|
||
input base and comparing resolved bounds, with a reduced natural-size-hint
|
||
regression and 400 depth-5 relative-bound trees. Intrinsic bounds remain
|
||
`Len`s; comparisons involving leftover use `SizeRequest`. Ordinary generated
|
||
trees retain pixel bounds; `deferred_generated` supplies relative bounds and
|
||
expressions in separate corpora.
|
||
- `LazySpan`.
|
||
- `Scroll` taking a direction rather than one axis.
|
||
|
||
Other product work is in `docs/PLAN.md` and the focused documents it links.
|
||
Do not mix it into the Iris layout branch.
|