mirror of
https://github.com/turnstonelabs/turnstone.git
synced 2026-08-12 23:12:23 -06:00
2d174cd71c
The remaining steady-state cost after the wedge-proofing pass was structural: every row of an unbounded transcript participated in every layout, and full re-renders rebuilt all of it. CSS pair (shared scroller + the ui/static duplicate): - The messages scroller is BLOCK flow, not a column flexbox — flex relayouts all items when the streaming row's height changes, O(rows) per token; block flow dirties only the tail. The old per-child flex-shrink pin (and the min-height:auto squish hazard it suppressed) goes with it; inter-row rhythm moves to a sibling margin. - overflow-anchor: none — the pane owns bottom pinning, and native anchoring kept re-selecting an anchor inside the innerHTML-replaced live bubble every frame. - content-visibility: auto with contain-intrinsic-size: auto estimates on .msg (80px) and .conv-batch (200px) rows, exempting the last two children so the live tail never toggles skip-state mid-stream. The `auto` keyword memoizes each row's rendered size, keeping scrollHeight and the bottom pin stable once painted. Transcript windowing (interactive pane): - Full re-renders paint the most recent 300 messages, cut FORWARD to a user-turn boundary so an assistant tool_calls message is never split from the tool results that anchor to it. Hidden content sits behind a "Load earlier messages" pager; each click grows the window a step and refetches, restoring the scroll anchor by scrollHeight delta (the rAF pin re-checks the near-bottom flag at fire time, so no suppression is needed). Rewind/edit turn math is tail-relative and unaffected — pinned as such. - Live appends are bounded at the idle edge: past 900 rendered rows the oldest rows are trimmed (again to a turn boundary), only while pinned to the bottom — a scrolled-up user is reading the rows a trim would remove. Trimmed content stays in /history and returns through the pager; detached agent-card entries are swept. The perf page gains ?window= (and the runner --perf-extra) so windowing and containment effects can be isolated. Measured (n=3000 history + 20-turn storm; baseline -> previous branch -> this change): full replay 1060ms -> 238ms -> 28ms windowed / 107ms with the window grown to the full transcript; longtasks during the run 6/1080ms -> 4/495ms -> none windowed / 4/205ms unwindowed; per-turn live-storm cost at n=3000 now equals n=300 (~220ms harness floor) even with all 25k nodes live — the transcript-size tax is gone. Known degraded-mode cost: the chunk path at a fully-grown window measures ~1017ms vs the 833ms floor; the shipped windowed config sits at the floor.