Problem
After a subagent (Task) card collapses, the chat viewport keeps a large block of blank space below the last message. The blank area grows with each subagent card event and only drains after the user manually scrubs the scroll wheel up and down repeatedly (each round trip consumes only the upward-scroll distance).
Screenshot: the failed "Explore" card is fully collapsed (compact header), the scrollbar thumb sits at the physical bottom, yet ~750-800px of empty background separates the message from the input bar.
Root cause
Collapse-intent pre-compensation adds provisional footer space (collapse.px) before the card shrinks. When the user is at the physical bottom, every settlement path dead-locks:
settleCollapseReservationForViewport / retained quiet settlement compute the geometric minimum for the current scrollTop, but the synthetic footer itself holds scrollTop at the physical bottom, so the minimum recomputes exactly the current reservation (e.g. 786px stays 786px).
- The scroll handler's per-scroll consumption never fires at the physical bottom because
scrollDelta is 0 (scrollTop is capped by the footer).
- The idle reservation audit settles to the same value, so it cannot help either.
Multiple collapse intents (e.g. several parallel subagent failures) stack provisional estimates on the unsettled reservation (base = current total compensation), which is why the whitespace grows monotonically during the session.
Fix
convergeBottomReservationsToContentBottom: when the scroller sits at the physical bottom (within 2px) with no preserving-element / pinned-item viewport owner and no active collapse intent, clear collapse + pin reservations in one synchronous footer update and fall scrollTop to the new content bottom (the delayed browser clamp compensation was hiding). Wired into:
- retained quiet settlement
- collapse-intent finalization (non-pinned strategies)
- idle reservation audit (bottom users only; mid-list readers keep their reading anchor)
- scroll handler: any scroll event at the physical bottom is bottom intent - one pass replaces per-scrub consumption
- collapse-intent accumulation guard: a new provisional estimate no longer stacks on a settled bottom reservation
- input-stack-shrink drain
Also: bound collapse-intent finalize retries (20) so a virtualized sticky-pin target cannot keep the intent active (and auto-follow suspended) forever; intent-window growth consumption is capped at the measured protection line so subagent growth no longer sediments into footer whitespace.
Verification
Component tests cover bottom convergence (finalize / scroll / audit), negative cases (mid-list, pinned-item, preserving-element must not clear reservations), non-stacking consecutive intents, growth protection during an active intent, and bounded force-settle at mid-list. Requires manual verification with a real streaming session.
Problem
After a subagent (Task) card collapses, the chat viewport keeps a large block of blank space below the last message. The blank area grows with each subagent card event and only drains after the user manually scrubs the scroll wheel up and down repeatedly (each round trip consumes only the upward-scroll distance).
Screenshot: the failed "Explore" card is fully collapsed (compact header), the scrollbar thumb sits at the physical bottom, yet ~750-800px of empty background separates the message from the input bar.
Root cause
Collapse-intent pre-compensation adds provisional footer space (
collapse.px) before the card shrinks. When the user is at the physical bottom, every settlement path dead-locks:settleCollapseReservationForViewport/ retained quiet settlement compute the geometric minimum for the currentscrollTop, but the synthetic footer itself holdsscrollTopat the physical bottom, so the minimum recomputes exactly the current reservation (e.g. 786px stays 786px).scrollDeltais 0 (scrollTop is capped by the footer).Multiple collapse intents (e.g. several parallel subagent failures) stack provisional estimates on the unsettled reservation (
base = current total compensation), which is why the whitespace grows monotonically during the session.Fix
convergeBottomReservationsToContentBottom: when the scroller sits at the physical bottom (within 2px) with nopreserving-element/pinned-itemviewport owner and no active collapse intent, clear collapse + pin reservations in one synchronous footer update and fallscrollTopto the new content bottom (the delayed browser clamp compensation was hiding). Wired into:Also: bound collapse-intent finalize retries (20) so a virtualized sticky-pin target cannot keep the intent active (and auto-follow suspended) forever; intent-window growth consumption is capped at the measured protection line so subagent growth no longer sediments into footer whitespace.
Verification
Component tests cover bottom convergence (finalize / scroll / audit), negative cases (mid-list, pinned-item, preserving-element must not clear reservations), non-stacking consecutive intents, growth protection during an active intent, and bounded force-settle at mid-list. Requires manual verification with a real streaming session.