The handoff now describes wip/one-ask at 3091fb8: a widget draws once in the box it is asked in, its answer is placed by re-expressing the drawing, and nothing is drawn again in a box an answer chose. The log records what the step 3 plan got wrong -- it kept the second draw whose measurement bit could not be defined, demanded a contract of the answer box, and narrowed frames by region -- and what fuzzing the new protocol found: the Part::Of composition dropping a pinned length, and a local redraw that must be put back in the parent's answer box. The Pad frame rule is left as a decision for Bryan with its cost stated. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
409 lines
24 KiB
Markdown
409 lines
24 KiB
Markdown
# Layout findings log
|
|
|
|
What the sessions working on Iris's retained layout found, planner and
|
|
worker alike, kept so that nothing here is rediscovered. Each entry says who
|
|
found it and when. **Delete this file when transparent frames lands**; what
|
|
must outlive it (settled design, the measurement method) is already in
|
|
`docs/LAYOUT.md`, and the current plan is in `docs/HANDOFF.md`. Commit ids
|
|
are in `/home/bob/repos/iris-layout-experiment` unless said otherwise.
|
|
|
|
## What the one-ask protocol found (planner, 2026-09-18, third session)
|
|
|
|
Branch `wip/one-ask` at `3091fb8` over `4328eac`. The change is described in
|
|
`docs/HANDOFF.md`; this is what building and fuzzing it turned up, in order.
|
|
|
|
- **The step 3 assertion was the wrong check.** At the worker's stopping
|
|
point every generated case stopped on `assert_eq!(extent, info.part)` in
|
|
`place`, on a `Text` asked at 45 px in `Within(All)` whose longest word is
|
|
89.5 px: its drawing holds for `[45, 45]` and its answer box is 89.5 px
|
|
wide. No leaf can promise its drawing holds for a box chosen from its
|
|
answer; the plan's sentence "every drawing must hold for the answer box it
|
|
supplies" was the defect, not `Text`. Removing the check *and the redraw
|
|
it guarded* -- placement is re-expression, unconditionally -- made the
|
|
suite green including both decided-box pins, with nothing else changed.
|
|
- **Six retained tests encoded the paired draw** (`settled + 2`, "drawn
|
|
once" expecting two): they now expect one draw. Two more failed because
|
|
`Stack` had been changed to ask non-sizing children with `Fill(All)`; they
|
|
need `Within(All)` so a smaller answer sits inside the stack, with the
|
|
frame narrowed to the sizing answer. That is the whole of the suite churn.
|
|
- **Seed 2 at depth 4 (`repaint-some`, `region-node`)** shrinks to a `Stack`
|
|
sized by one text. A local redraw of the text asked it again in the box
|
|
the stack was *asked* in and left it filling that; cold puts it in the box
|
|
the stack's *answer* chose. A local redraw must put the fresh drawing
|
|
back at the retained place of the parent's answer box, always, not only
|
|
when the asked and placed places differ.
|
|
- **Six seeds at depth 5** (60 `repaint`, 308 `every-size`, 20
|
|
`region-node`, 248, 384, 162 `reorder`) shrink to one shape: a zero `Pad`
|
|
round a widget reading `extent_len` (a span with a share rect, or a
|
|
`Branch`), as a share child of a span beside a fixed sibling. Seed 248
|
|
reproduced: **cold** was wrong. In the measuring pass the inner span read
|
|
`far = rel 1 - 17.6px` and wrote slots in it; in the placing pass the pad
|
|
got a narrowed frame of the same pixel length, was reused (its contract
|
|
had no pin, because `in_parent`'s `Part::Of` arm drops a child's
|
|
`extent_len`), and its child was re-expressed into a frame where `rel 1`
|
|
means 17.6 px less. The inner span's own record had the pin; only the
|
|
composition lost it. Composing the pin through `Of` -- exactly where the
|
|
part is the whole box less pixels, `pinned - part.px`, and pinning the
|
|
parent's own length otherwise -- fixed all six; the planner's earlier
|
|
version of the same experiment broke seeds under the old protocol because
|
|
the second draw was still there.
|
|
- Cost after the fix (widget draws / distinct widgets, depth 8): seed 1
|
|
cold 331/288, many 78/78, size 3/3, scroll 1, resize 40/15; seed 13 cold
|
|
1179/982, many 335/333, resize nothing drawn. The table with the baselines
|
|
is in the handoff. `many` at seed 13 touches 333 distinct widgets because
|
|
a share child is asked twice per span draw, and `resize` at seed 1 draws
|
|
40 because positive-direction spans without shares pin `far`; both are
|
|
step 4 there.
|
|
- The 1000-seed depth-6 oracle and the 2000-seed depth-4 scan were started
|
|
at `3091fb8` and had not finished when this was written; the next session
|
|
reads `/tmp/oracle-1000-6.log` and `/tmp/shrink-2000-4.log` if they still
|
|
exist, or reruns them.
|
|
|
|
## What the step 3 plan got wrong (planner, 2026-09-18, third session)
|
|
|
|
Three things, each a different kind of mistake:
|
|
|
|
1. **It kept the question the machinery could not answer.** The offer bit
|
|
existed because a widget was drawn twice, once where asked and once where
|
|
its answer placed it, and something had to say which draw was the
|
|
measurement. Both plans of 2026-09-18 tried to define that bit better
|
|
(the worker's four rules, the planner's two experiments). The repair is
|
|
to have one draw, after which the bit has nothing to name. A plan that
|
|
proposes bookkeeping for a distinction should first ask whether the
|
|
distinction has to exist.
|
|
2. **It stated the contract at the wrong box.** "Every drawing must hold for
|
|
the answer box it supplies" reads as a tightening and is actually
|
|
unsatisfiable for any widget that reads its box. The right statement is
|
|
the opposite: the answer box is never a question, so nothing is demanded
|
|
of the drawing there; `Holds` is about the ask box only.
|
|
3. **It narrowed frames by region.** The worker's share frames were
|
|
`extent_part(axis, From(span))`, a position in the span's frame; a frame
|
|
given as a position does not move when its part moves, and `re_ask` had
|
|
no way to put it back. A narrowed frame must be a *length* of the
|
|
parent's frame, placed into the part by alignment on every placement,
|
|
exactly as a declared rule already was.
|
|
|
|
The requirements did change under the plans (transparent frames, then
|
|
shares narrowing frames, then `Scroll`'s content length), which explains
|
|
some churn but not the three above; those were reasoning errors that a
|
|
five-widget reproduction with the retained records printed would have
|
|
caught in minutes. Print the records before proposing the next rule.
|
|
|
|
## The two shrinker seeds that were open on `wip/transparent-frames` (planner, 2026-09-18) -- closed by `wip/one-ask`
|
|
|
|
Both were the rule in `draw_inner` that decided which answer places a widget's
|
|
box: `measured = if info.offer() { fresh } else { retained }` with
|
|
`offer() := place == offer_place`. That bit is meant to say "this ask is a
|
|
measurement" and has no consistent value once a container's body runs in
|
|
more than one box.
|
|
|
|
- **Seed 2, `repaint`, depth 5** shrinks to `Stack{sized by child 0}[x:
|
|
leftover] > [OneLine, Pad{0} > Span{DOWN}[Rect, OneLine], Branch]`. The
|
|
stack's box is one text line tall. The span measured in the stack's room
|
|
has leftover room, draws the rect and reports `leftover` across; in the
|
|
stack's box it has none, undraws the rect and reports the text's width.
|
|
Cold kept the first because the stack's placing evaluation reused the
|
|
pad's retained answer, which looked valid only because `in_parent`'s
|
|
`Part::Of` arm drops the span's `extent_len` pin. Warm re-asked the span in
|
|
the box and got the second. The correct layout is the second: the stack's
|
|
non-sizing children belong in the stack's box, and the measuring pass drew
|
|
them in a box the stack never has.
|
|
- **Seed 108, `reorder`, depth 5** shrinks to `Span{LEFT} > [Span{LEFT} >
|
|
[Span{DOWN} > [Branch{probe Rect, wide Rect, narrow Wrapped, 483},
|
|
Wrapped], Rect], Rect]`. A draw trace shows the `DOWN` span evaluated at
|
|
900, 450, 600 and 300 px across in one cold layout (room of the outer
|
|
span, slot inside the inner span's measuring pass, slot of the inner span,
|
|
slot inside that), the wrapping text re-shaped at each. In the last one
|
|
the branch's narrow text is drawn in its real 300 px box and answers 286
|
|
px; `draw_inner` discards that for the retained 438.9 px answer from the
|
|
450 px evaluation because the branch's `below` place embeds
|
|
`extent_len(Y)`, which moved when the sibling text's height changed, so
|
|
`place != offer_place`; the text is redrawn 438.9 px wide in a 300 px box.
|
|
Warm does the same with a different stale answer (286 px from the first
|
|
frame). The same shape is `unsettled::a_widget_under_a_region_node_is_asked_in_the_box_that_node_was_offered`.
|
|
|
|
Two one-line experiments, both reverted, run at `49cec82` with the fast
|
|
oracle, the debug suite and the shrinker at 400 seeds of depth 5:
|
|
|
|
| change | seed 2 | seed 108 | else |
|
|
| --- | --- | --- | --- |
|
|
| compose the pin through `Part::Of` (`len.px - part_len.px` where `part_len.rel == ONE`) | fixed | fails | seed 220 `reorder` and one suite test fail |
|
|
| `measured = answer.0` always, and every ask records `answer`/`offer_part` | fails | fixed | seeds 184, 246, 292, 372 and two suite tests fail, all under `Scroll` |
|
|
|
|
The second experiment's failures are the case the gate was secretly holding
|
|
up: a container re-drawn in a box its own answer derived. The specific one
|
|
is `Scroll`, whose `apply_leftover` content length adds a whole viewport on
|
|
top of the content's pixels (a row reporting `600 px + leftover` in a 900 px
|
|
viewport gets a 1500 px content box), so the wrapping text inside is offered
|
|
900 px more room than it was measured in and re-wraps; the layout drifts one
|
|
iteration per evaluation, and warm and cold differ by how many they ran.
|
|
Bryan: that content length was always wrong; a share is the room left in the
|
|
viewport, so `content = max(viewport, px + rel*viewport)`. With it the
|
|
content box never re-offers room and `Scroll` needs no own-answer
|
|
evaluation.
|
|
|
|
The `many`/`resize` cost is the same mechanism in miniature: four
|
|
evaluations of a ten-widget subtree in one cold layout, from `Within(cursor..
|
|
far)` measuring rooms followed by `Fill(slot)` placing, with the span's
|
|
`extent_len` pin forcing a redraw at every box length.
|
|
|
|
## Original step 3 plan, preserved verbatim (planner, 2026-09-18)
|
|
|
|
This is the original idea from ai-app-2 `280fad7`, kept here even as the
|
|
handoff's stop notice changes with new evidence:
|
|
|
|
> ### 3. Evaluate children in parent-decided boxes
|
|
>
|
|
> Change placement so an answer-derived box never runs a container body.
|
|
> Placing becomes reuse-or-translate. Remove the offer/measurement gate and
|
|
> its retained bookkeeping only as each caller stops needing it; do not leave
|
|
> a parallel old path.
|
|
>
|
|
> Three current widgets must stop depending on measuring boxes they will never
|
|
> own:
|
|
>
|
|
> - `Span`: do not read `extent_len` unconditionally. Slots depend on `far`
|
|
> only when shares exist (the decided slot fills its part) or for negative
|
|
> direction; compute negative-direction slots from `total`. Pin the extent
|
|
> length only in those cases.
|
|
> - `Stack`: draw non-sizing children in `From(0..size)` on an axis where the
|
|
> sizing child's answer is `px`/`rel`, and `All` where it is `leftover`,
|
|
> instead of drawing them in `All` of the measuring room.
|
|
> - `Branch` in the random rig: express "the rest of my box" as
|
|
> `Of(40px..FULL)` rather than reading `extent_len(Y)`.
|
|
>
|
|
> Every drawing must hold for the answer box it supplies. The two focused tests
|
|
> from step 1 and the existing region-node and decided-box tests must pass here.
|
|
|
|
The surrounding seven-step implementation plan remains in `docs/HANDOFF.md`;
|
|
the quoted text above is the part whose interpretation is now blocked, so it
|
|
must remain available even if the live handoff is rewritten.
|
|
|
|
## Step 3 experiment after verifying the tests (worker, 2026-09-18)
|
|
|
|
The initial 17 suite failures mixed obsolete expectations with real defects.
|
|
`ReadsBox`, `ReadsWidth`, and `Measured` used pixel reads to compute only their
|
|
answer; failing `Counted` cases installed a box dependency without drawing
|
|
anything. Those fixtures encoded the old second draw inside the answer box.
|
|
Giving the painter answer-only pixel reads preserved their invalidation and
|
|
geometry claims while removing the obsolete paired-draw count. Separate
|
|
fixtures still exercise actual drawing dependencies with ordinary pixel
|
|
reads.
|
|
|
|
The experiment then reached a green 114-test suite. Its substantive findings:
|
|
|
|
- answer validity and final-drawing validity must remain independent all the
|
|
way through `draw_inner`; returning their old combined contract falsely
|
|
pinned wrappers such as `Pad` to provisional boxes;
|
|
- a repeated ask of one child replaces that child's provisional final-drawing
|
|
contract rather than intersecting both drawings forever;
|
|
- provisional `Span` asks fill their measuring room; `Within` immediately
|
|
tries to put the child in its own answer before the span decides anything;
|
|
- only an actually allocated share pins final span slots to `far`; an
|
|
overfull row with a requested but unallocated share uses fixed slots;
|
|
- a narrowed share frame is `Part::From(slot)` of the span's *extent*, not a
|
|
zero-based span in the forwarded frame;
|
|
- a local redraw re-asks the question that produced the retained answer and
|
|
marks the parent to restore any distinct final placement.
|
|
|
|
The release fast oracle then failed all eleven ordinary cases during cold
|
|
layout. Most failures were `Text`: its provisional drawing was pinned to a
|
|
pixel width that did not include the smaller box its answer selected. A
|
|
generated `Stack` showed the same class through a symbolic `FULL` pin. This
|
|
is stronger evidence than the original suite failures: the generated tests
|
|
are not asserting draw counts or the old two-draw protocol, and no warm state
|
|
exists yet. The open decision is whether the guarantee quoted above applies
|
|
to every leaf and how `Text` can establish it, or whether answer-derived leaf
|
|
evaluation remains legal while container evaluation does not.
|
|
|
|
## What the plan of 2026-09-18 got wrong (planner, same day)
|
|
|
|
The plan claimed the retained model rests on the frame's length being the
|
|
same on every ask. Answers depend on the *part* (the box offered), not the
|
|
frame, for every widget that reads its box, so transparent frames fixed
|
|
fraction resolution and moved the second geometry from the frame to the
|
|
extent rather than removing it. Step 6 ("redraw without the deferral; both
|
|
seeds must pass now because the frame no longer changes under the widget")
|
|
followed from that claim and was false for the same reason. The plan also
|
|
carried `offer_place` ("from the first ask of the parent's draw at the
|
|
offer") forward from the old protocol without noticing it is undefinable
|
|
once a container is evaluated in several boxes, and its `Inset` sketch
|
|
assumed a narrowed frame and a placed extent could coexist, which
|
|
`frame_and_extent` collapses ("a narrowed frame is its own extent") and which
|
|
the extent-in-frame-coordinates representation cannot express in general
|
|
(`(0.5*B - 20)/(B - 20)` is not `rel + px`). The plan also wrote "a frame is
|
|
narrowed only by a declared length, an inset or the root" and the code's
|
|
`declared_lens` filters `leftover` out; Bryan: a share was always meant to
|
|
narrow the frame like the other two.
|
|
|
|
The worker executed the plan as written, found exactly this ("whether a
|
|
given draw is a measurement cannot be recovered from the ask"), tried four
|
|
bookkeeping rules (first-ness alone, place matching the retained offer place,
|
|
box matching the measured box, a retained flag on the drawing), each fixing
|
|
some seeds and breaking others, restored the deferral and stopped. That was
|
|
the right call. The plan as handed over is at ai-app-2 `152bed7` if the
|
|
wording is ever needed.
|
|
|
|
## What the worker measured on `wip/transparent-frames` (worker, 2026-09-18)
|
|
|
|
Widget draws / distinct widgets / update ms from `tests/layout_diagnostics.rs`
|
|
at depth 8; draw counts are deterministic so these are single runs.
|
|
`e44dea3` is #18's head, `34cafb6` the commit the branch starts from,
|
|
`49cec82` its head.
|
|
|
|
| seed 1 | e44dea3 | 34cafb6 | 49cec82 | 3091fb8 |
|
|
| --- | --- | --- | --- | --- |
|
|
| cold | 369/261/10.6 | 463/274/13.3 | 516/288/12.0 | 331/288/12.3 |
|
|
| repaint | 1 | 1 | 1 | 1 |
|
|
| many | 157/95/0.33 | 263/108/0.59 | 187/119/0.52 | 78/78/0.13 |
|
|
| size | 16/12/0.018 | 3/3 | 3/3/0.010 | 3/3/0.008 |
|
|
| scroll | 2/0.002 | 1 | 1/0.004 | 1/0.001 |
|
|
| resize | 13/13/0.019 | 22/15/0.032 | 24/76/0.090 | 40/15/0.096 |
|
|
|
|
| seed 13 | e44dea3 | 49cec82 | 3091fb8 |
|
|
| --- | --- | --- | --- |
|
|
| cold | 1330/707/20.3 | 2940/982/28.3 | 1179/982/24.6 |
|
|
| many | 524/159/1.09 | 1091/423/2.39 | 335/333/0.62 |
|
|
| resize | nothing drawn | 2215/510/6.56 | nothing drawn |
|
|
|
|
The `3091fb8` column is single runs from the third session; its `cold` and
|
|
`resize` at seed 1 are after the `Part::Of` pin composition, which added
|
|
18 and 19 draws respectively over the version without it.
|
|
|
|
Three findings, each applied on the branch:
|
|
|
|
- **Lazy `Within` placement costs more than it saves.** Leaving a child's
|
|
answer to be placed at the end of the parent's draw puts the drawing in the
|
|
part first and in the answer's box after; where it does not hold for both
|
|
that is two drawings. Seed 1's resize went from 391 widget draws to 29
|
|
with it removed.
|
|
- **An inset said in frame lengths makes a container read its own box.**
|
|
"Less eleven pixels at the end" needs the length, and a container whose
|
|
box is its own answer then depends on its own answer: `Pad` drew
|
|
sixty-four times in one resize frame at seed 13. `Part::Of` says it as a
|
|
part of the box and composes without a length.
|
|
- **Pin one axis at a time.** `extent_len` pinning both axes made a span
|
|
dividing one hold for one length of the other.
|
|
|
|
`Part::Of`'s `in_parent` arm drops a child's `extent_len` pin (only `All`
|
|
composes one), which is what let seed 2's pad reuse an answer for a box its
|
|
span had never been measured in.
|
|
|
|
Reference renders at `49cec82` against `34cafb6`: `view` and `minimal`
|
|
byte-identical; `tabs` 2,332 pixels; `text` shows `wtext(..).width(rel(1.0))`
|
|
inside `.pad(16)` at the window's edges. Under the decided share rule that
|
|
example is correct as written: once the share narrows the frame, `rel(1.0)`
|
|
is the padded share and fits. The clipped render records the current bug.
|
|
`random` moves where nested spans do.
|
|
|
|
## Defects landed before transparent frames, and their lessons (2026-09-17 review)
|
|
|
|
- **A report is a fraction of the containing widget** (`ffd79f3`). A
|
|
report used to come back composed through the box it was offered, and a
|
|
span offers each child the room from its cursor, so a nested span taking
|
|
half of what it was given took a quarter of a row whose first half was
|
|
spoken for. Consequence: a span can overflow itself without bound, so its
|
|
`fixed <= 0` branches are ordinary and boxes of negative length reach
|
|
children; nothing yet states what a widget may assume about one.
|
|
- **An answer is not an answer while anything under it is dirty**
|
|
(`0e0d4af`, superseded by `a0693ac`). `dirty_size_under` was what made an
|
|
answer an answer until the settling walk made the state it guarded against
|
|
unreachable. Found at seed 564, depth 6, `shuffle-every-other`.
|
|
- **A frame settles strictly bottom-up** (`a92c6ac`, `a0693ac`). A widget
|
|
that cannot settle defers to its parent rather than drawing the parent
|
|
from inside itself; `update` marks the root for a resize instead of
|
|
drawing it top-down, and only where the new output falls outside what the
|
|
root's answer holds for. Sound by induction on depth. Bryan: "then that
|
|
entire category of issue can't even occur."
|
|
- **A text is handed back a box its own line fits in** (`4bd8607`): report
|
|
`ceil` of the shaped size; the two tolerances that were holding it
|
|
together (`BREAK_EPSILON_PX`, a nearest-step `Holds` start) both went.
|
|
- **A subtree that changes hands is recorded on both sides** (`e44dea3`):
|
|
the old parent's child list is repaired and the subtree's depths re-walked
|
|
where its top moved. The fuzzer never re-parents, which is why nothing
|
|
generated reached it.
|
|
- **A span's leftover boundary is its own inverse** (`53b00c6`): forty
|
|
lines became twelve and one `div` left layout.
|
|
- **`wip/stack-fraction-twice`** (a stack sized by a child reporting
|
|
`rel(0.5)` applied it twice) is closed by transparent frames:
|
|
`placed_extent` takes the reported length *from* the part rather than
|
|
composing it into the part. No oracle could see it; the branch's test pins
|
|
it. **`wip/padding-outset-and-inset`** is superseded: padding goes outside
|
|
what it pads, and its `rel(0.5)`-under-inset failure was the same second
|
|
application.
|
|
- Four findings from the frame/extent prototype still shape the code: an
|
|
answer and a drawing each retain their dependencies (`answer_under` and
|
|
`under`); a wider contract does not invalidate an existing guarantee; no
|
|
measurement is different from a measured zero (`ActiveData::answer` is
|
|
optional); the settling walk takes the deepest mark from a `BTreeSet`
|
|
keyed by depth and what ends it is the mark set.
|
|
|
|
## Failed hypotheses worth not repeating
|
|
|
|
- **The placement pin was blamed for the `many` gap and is not the cause**
|
|
(2026-09-17). Disabling it still redrew 487 distinct widgets a frame at
|
|
seed 13 against 159; the per-widget trace showed local redraws deferring
|
|
to their parents in chains to the root because a span handed its children
|
|
its own placement as their frame. Transparent frames fixed that part.
|
|
- **`wip/local-reask`** re-asked a dirty widget at its offer instead of
|
|
deferring, under the old protocol, and diverged at seeds 532 and 398 of
|
|
depth 6. Superseded; the branch can be deleted.
|
|
- **An offer composed back up the move chain** fell back to `FULL` under a
|
|
region node and was resolved against that node's *placed* box, so
|
|
everything under a `Scroll` was re-asked at the content's width. Pinned by
|
|
`unsettled::a_widget_under_a_region_node_is_asked_in_the_box_that_node_was_offered`.
|
|
- **Four bookkeeping rules for "which draw is a measurement"** (worker) and
|
|
**the two experiments above** (planner): each fixes some seeds and breaks
|
|
others. The bit is undefinable; `wip/one-ask` removes the second draw, so
|
|
there is nothing left for it to name.
|
|
- **Asserting that a placed drawing holds for its answer box** (worker's
|
|
step 3): unsatisfiable for `Text` and for any widget that reads its box
|
|
and reports something else. The answer box is not a question.
|
|
- **Choosing between a fixed and a relative child in pixels at the span's
|
|
current width** admits multiple self-sizing fixed points; seed 13 settled
|
|
differently warm and cold under it. The same circularity is what a cap
|
|
containing `leftover` would put into `SizeRule::Max`.
|
|
- **A tolerant endpoint on the span's leftover split** retained zero-height
|
|
children at seed 16. Pinned by
|
|
`unsettled::a_box_that_only_rounds_past_its_fixed_children_leaves_nothing_over`.
|
|
- **`Scroll` returning the final placed answer** rather than the first
|
|
box's advanced one fixed-point iteration (seed 86). Moot once the content
|
|
box never re-offers room, but keep the test until that is measured.
|
|
- **A `git bisect`** once named a commit that could not be the cause; read
|
|
the tree rather than the bisect when that happens.
|
|
|
|
## Fuzzer coverage
|
|
|
|
- Seeds 1121 and 1839 at depth 4 failed on every commit before `4bd8607`
|
|
and nothing routine reached them: the fast oracle takes ten seeds, the
|
|
shrinker 400 at depth 5, the long oracle 1000 at depth 6. The scan that
|
|
found them (2000 seeds at depth 4 over all fifteen cases, 261 s) should be
|
|
run after any layout change. `Rng::new` is `seed | 1`, so an even seed and
|
|
the odd one above it are one tree.
|
|
- The shrinker panics per thread at the first failing seed, so a run
|
|
reports at most one seed per chunk of about 58; a seed listed as new after
|
|
a change may have been hidden behind another in the same chunk. Check a
|
|
single seed against the unpatched code before calling it new (seed 220
|
|
was checked this way and is new under the pin experiment).
|
|
- Depth finds things and so does breadth; widen one axis at a time and
|
|
record which.
|
|
|
|
## Smaller open items (carried from the old handoff)
|
|
|
|
- An undrawn `leftover` child still contributes its gap, so a vanished
|
|
child leaves a double gap.
|
|
- 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.
|
|
- `Fixed::div` by zero answers `MIN`/`MAX` while `ratio` answers `ZERO`;
|
|
both are caller bugs under `debug_assert`, but the fallbacks differ.
|
|
- The comment on the `local == UiRegion::FULL` shortcut in `widget_at` says
|
|
composing through `FULL` "is not quite the identity in f32"; on the grid
|
|
it is exact and the shortcut is performance only.
|
|
- `docs/LAYOUT.md` §4, §5 and the density section name `Painter::place`,
|
|
`SetSize`, `desired_width`, `apply_rest`, `Len::dp`, `Aligned` and
|
|
`MaxSize`, none of which exist. Do not restore `OnResize::Translate` or
|
|
`OrthoSize`.
|
|
- `tabs` changed twice across `d3b0ebf` with nobody looking; take the oracle
|
|
as the reference and the five renders as a spot check.
|