Replace the step 3 plan with the one-ask protocol and record why it failed
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>
This commit is contained in:
1 parent
39978ff8ea
commit
ef324aa88b
3 files changed
+370
-319
No files matched your search
+106
-16
@@ -7,9 +7,91 @@ 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.
|
||||
|
||||
## The two open shrinker seeds on `wip/transparent-frames` (planner, 2026-09-18)
|
||||
## What the one-ask protocol found (planner, 2026-09-18, third session)
|
||||
|
||||
Both are the rule in `draw_inner` that decides which answer places a widget's
|
||||
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
|
||||
@@ -169,20 +251,24 @@ 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 |
|
||||
| --- | --- | --- | --- |
|
||||
| cold | 369/261/10.6 | 463/274/13.3 | 516/288/12.0 |
|
||||
| repaint | 1 | 1 | 1 |
|
||||
| many | 157/95/0.33 | 263/108/0.59 | 187/119/0.52 |
|
||||
| size | 16/12/0.018 | 3/3 | 3/3/0.010 |
|
||||
| scroll | 2/0.002 | 1 | 1/0.004 |
|
||||
| resize | 13/13/0.019 | 22/15/0.032 | 24/76/0.090 |
|
||||
| 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 |
|
||||
| --- | --- | --- |
|
||||
| cold | 1330/707/20.3 | 2940/982/28.3 |
|
||||
| many | 524/159/1.09 | 1091/423/2.39 |
|
||||
| resize | nothing drawn | 2215/510/6.56 |
|
||||
| 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:
|
||||
|
||||
@@ -268,7 +354,11 @@ is the padded share and fits. The clipped render records the current bug.
|
||||
`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; stop trying to define it.
|
||||
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
|
||||
|
||||
Reference in new issue
Block a user