0a0949ee7f069b3b588af7bd6a67336635ccae91
Three things, and the middle one is why the last commit's "zero flickers" was worth nothing. **The detector was measuring the wrong thing.** `covered()` compared what is on screen against `retained` -- which is the *intent*. Those came apart the moment the window started being recomputed during layout, because it is then updated before the frame draws: the check passed while the screen was still showing the spacers from the composition before. It now compares against what the list actually composed, recorded as it composes it. The first honest run found the open path still failing, and the counters named a branch of my own that did nothing at all -- position lost, scroll container reporting a measured position from a *different* session, so neither arm ran and the window was left empty. The whole transcript then composed as a single spacer. The test is no longer "is there a position to trust" but "is the window empty", which is the question that was actually being asked. **The scan was the lag.** Putting the recompute where the layout is made it correct and put a linear pass over every row into every frame with it -- 0.5ms to 1.1ms of placement at 190 rows, the same O(rows)-per-frame mistake this file exists to undo, reintroduced by its own fix. The tops are ascending by construction, so it is a binary search. **The button now does the subtraction.** The draw phase carries Compose's measurement as well as its recording, so "draw is high" never said which of three things was high, and the split was being worked out by hand in a conversation every time a report arrived. It reports the per-frame split directly: the transcript's own measure, placement and recording, and what is left, which is the framework's bookkeeping after a layout. The immediate window went from two screens to three, and from six rows to twelve. What the list has composed is always one frame behind what the window says -- the window is recomputed in layout and the rows it names are built by the next composition -- so the margin has to cover a frame of movement as well as the gap between recomputations. Measured down from misses of one and two rows. Verified: three cycles of open, fling up through a page load, fling back, close, on a 92-row session, plus reopens of a short one. Zero, with the detector that no longer flatters itself. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Languages
Rust
54%
Kotlin
43.6%
Shell
2.4%