diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index 898cf9d..36c133b 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -8,7 +8,7 @@ log. Canonical Iris `main` is **`ca2b4b2`** (#17, the headless rig). **#18 `split/18-position-chain`** is open in `/home/bob/repos/iris-pr18`; its local -head is **`4febabf`**, seventy-one commits. Built-in alignment is complete there; +head is **`98d4e98`**, seventy-two commits. Built-in alignment is complete there; see "Built-in alignment" below for the retained-layout details. No PR reviews were present when checked on 2026-09-15. @@ -89,6 +89,53 @@ rewrite: `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. +## A warm frame moves a scrolled span, and the rigs that found it + +**The defect.** Marking one widget for redraw moves a span inside a scroll by +24px against a cold tree of the same state. Five widgets, in +`tests/cases/unsettled.rs` as +`redrawing_one_widget_does_not_move_what_scrolls_around_it`, **ignored until +it is fixed** so the suite stays honest rather than red: + + Scroll(Y) { Span(DOWN) { Scroll(X){ text } as a region node, rect 87x24 } } + +`git bisect` names **`95fb4f9`**, which made `Masked` report `Size::LEFTOVER` +rather than its inner's size. `Scroll` clips through a `Masked`, so what the +outer scroll is told its content measures now depends on whether the inner +subtree was redrawn this frame. The reasoning in that commit is sound -- a +widget that clipped its contents cannot honestly ask for more than its box -- +so the fix is not to revert it but to stop the outer scroll's content +measurement from going through the clipped report. Not attempted yet. + +24px is exactly the sized child's height, so this is a whole child's worth of +layout rather than a rounding. + +**The rigs.** `98d4e98` splits `iris::random::grow` into `plan(seed, depth, +&edits)` and `build(rsc, &plan)`. A seed cannot be made smaller, which is why +a failure the oracle found could never be handed to the shrinker; a `Plan` +can, and `tests/scenario/` holds the fifteen cases both rigs now run over the +same trees. So any failing seed reduces directly: + + SHRINK_SEED=18 SHRINK_DEPTH=6 SHRINK_CASE=repaint-some \ + cargo test --release --test shrink -- --ignored --nocapture + +That took seed 18's 277 widgets to 5. The oracle prints the command to reduce +whatever it failed on. Seeds still mean the trees they meant: the 1000-seed +depth-6 run gives the same three failures with the same boxes before and after +the split, which is the check to repeat if the generator is touched again. + +**Unreduced leads**, from the shrinker at 400 seeds of depth 5 -- a level it +had never run, since it used to grow its own trees and know five cases: +**seeds 2 and 288 on `region-node`**, and **174 and 175 on `repaint-some`**. +The `repaint-some` pair is probably the defect above. The `region-node` pair +is not obviously the same thing and is the next one to reduce. + +**Depth is what finds these.** Nothing fails at 100 seeds of depth 4, which is +all the oracle has ever routinely run. Seeds 18, 190 and 326 fail at depth 6 +of 1000. Before fixed point, `5ed9e87` fails that same run on seed 40 after a +resize and on nothing else, so the partial-repaint family is new since then +and the resize one is older than all of it. + ## Verification at `394d514` `4febabf` on top of it -- `Fixed` wrapping rather than saturating -- passed @@ -102,11 +149,12 @@ against `394d514`. The measurements are under "Performance". - `cargo test --workspace --all-features`: 105 passed, 10 ignored - The release generated cold-layout oracle passed 100 seeds in 9.4 s, and a shrinker case at 300 seeds in 3.5 s: both run a thread per core but one. -- The release shrinker passed **all five** cases -- `resize`, `repaint`, - `resize-repaint`, `reorder`, `size-change` -- at 300 seeds of depth 5: - 26,001 widgets per case, largest tree 331. It passed 1000 seeds of depth 6 - (159,024 widgets per case, largest tree 587) at `5ed9e87`, before fixed - point; re-run that before quoting it again. +- The release shrinker passed **all five** cases it had then -- `resize`, + `repaint`, `resize-repaint`, `reorder`, `size-change` -- at 300 seeds of + depth 5. **That claim does not carry to `98d4e98`**, which runs fifteen + cases over the oracle's trees rather than five over its own, and fails at + 400 seeds of depth 5. Neither does the 1000-seed depth-6 line that used to + sit here: re-run, `5ed9e87` fails it too, on seed 40 after a resize. - `tabs`, `view`, `minimal`, `text` and `random` render byte-identical at 1920x1200 across the whole fixed-point sequence, and across this session's renames and arithmetic changes on top of it. @@ -621,9 +669,16 @@ cargo test --release --test generated -- --ignored a_long_run_of_seeds_agrees `tests/generated.rs` compares a warm incremental tree with a cold tree of the same state. `IRIS_GENERATED_SEED`, `IRIS_GENERATED_SEEDS`, and -`IRIS_GENERATED_DEPTH` select failures. `tests/shrink.rs` reduces a failing -tree; use it to turn a seed into a readable regression rather than leaving the -seed as the only record. +`IRIS_GENERATED_DEPTH` select what it covers. `tests/shrink.rs` reduces a +failing tree, over the same fifteen cases and the same trees since `98d4e98`, +so a seed the oracle fails on goes straight to it -- the failure prints the +command. `SHRINK_SEED`, `SHRINK_SEEDS`, `SHRINK_DEPTH`, and `SHRINK_CASE` +(a case name, or `all`) select what it tries. Turn what it finds into a test +of its own rather than leaving a seed as the record. + +The fifteen cases live in `tests/scenario/mod.rs`, which both targets include +by `#[path]`. Adding one there gives it to both rigs at once; a case that only +one of them knows is how the two drifted apart in the first place. **The float head is checked out at `/home/bob/repos/iris-float-cmp`**, at `5ed9e87` with `394d514`'s `Edits::fixed_branches` cherry-applied to