f497d1f3ade4a2de4b83a07ce3411f13858654e7
Two things committed in9052e5fand94b1509were never wired up. The row count was tracked and nothing observed it, so the fix for the gap when sending was inert; `standUpSome` and `growing` were written and nothing called them, so the window still jumped in one step. Both are connected now, which is what the last two commit messages already claimed. The third thing is what the counters were pointing at all along. The frame's draw phase sat at a flat 7.7ms -- p99 only 1.8x p50, so not shaping, which is spiky -- while the transcript's own recording ran 43 times in 3638 frames and accounted for a fortieth of it. A stood-down row drew nothing but was still a layout node, and the framework's per-frame bookkeeping after a scroll walks live nodes rather than visible ones. So the cost grew with the conversation instead of with the screen, which is exactly the step the reader felt at each page loaded. The rows outside the window are now two spacers rather than one per row, which they can be because the window is a range: what is not in it is one run before and one run after. Live nodes are bounded by the window now. The gaps between collapsed rows are added into the spacer, because `spacedBy` puts a gap between children and a run drawn as one child is short by all of them -- which would not read as a spacing bug, it would move everything under the reader. Measure and place are timed separately now. The draw phase carries Compose's measurement, so "draw is high while nothing is recorded" could not say which half, and the two have different fixes. Diagnosis from the ai-app-2-6d session: foundation already places scrolled content with `placeRelativeWithLayer`, so the scroll is a layer transform today and re-placing children was never the cost -- the node-count walk is, and collapsing the runs is the experiment that settles it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Languages
Rust
54%
Kotlin
43.6%
Shell
2.4%