perf(transcript): skip unchanged layout prefix (cmux#34383)

Closed
TE
8h ago
+162-21

HOLD: the same-harness before/after below shows no improvement (tool-output slope and token p95 are worse at the head); not landing until the regression is explained or fixed.

Use the transcript snapshot's earliest changed-row hint when placing a streamed tool-output update. The layout pass jumps directly to the changed suffix after checking the hint boundary, the quiet-thought projection translates raw-row indexes into projected-row indexes, the turn view's reuse boundary is carried onto the display rows, and the render-page index reuses its unchanged prefix. Structural and filtered projections keep their conservative full-prefix behavior.

Leo's parity ask is the reason for this budget: "It should just never, ever, ever, ever be acceptable for there to be linear time growth degradation in performance just because the app somehow has tons of workspaces, thousands, whatever workspace... you're only seeing at any given point one screen at a time... important bits get extreme priority and everything else goes by the wayside."

This includes #36116's commit (the first transcript projection fix) and addresses the remaining tool-output slope from cmux#34383. It also fixes forward the finding from #36116's review: a reused live projection kept the old last row's streaming shape after a row was appended, so a short streaming thought stayed visible. Reuse now stops one row short in that case, so the old last row is shaped again (thoughts.ts, with a regression test).

Adopted by the cloud-demo lane after the opening lane went away.

Performance

The shared transcript-replay harness (bun bench/transcript-replay.bench.ts --turns 600 --only long, Linux headless Chromium), with base (feat-cmux-next a1fba86c1ae9) and head runs alternated on the same runner in one job. The p95 of each run is shown; the earlier "before" column (from #36116's run against an older base) is dropped because the base has moved since then.

Metric (p95 per run)Base a1fba86c1ae9Head
streamed token main-thread time, run 16.764, 7.440 ms7.196, 7.234 ms
streamed tool-output time, run 19.139, 9.016 ms10.243, 10.594 ms
tool-output slope per 1,000 rows, run 1-0.192, 0.335 ms0.367, 0.617 ms
streamed token main-thread time, run 2 (noisier runner)11.05, 8.03, 13.63, 7.15 ms10.46, 8.64, 20.71, 12.35 ms
streamed tool-output time, run 215.80, 11.04, 15.14, 10.40 ms12.65, 10.70, 20.05, 12.01 ms
tool-output slope per 1,000 rows, run 22.74, -0.46, -0.65, 0.86 ms1.06, 0.31, 5.06, 0.84 ms

Medians: tool-output slope 0.07 → 0.49 ms (run 1) and 0.20 → 0.95 ms (run 2); token p95 7.10 → 7.22 ms and 9.54 → 11.40 ms. The change does not reduce the slope it targets on this harness.

The replay budget stays advisory in CI; no runtime feature is disabled.

Tests: bun test webviews/src/agent-session/acpmux/conversation/thoughts.test.ts webviews/src/agent-session/acpmux/orderedRowsPerf.test.ts (8 pass at the head; the new live-projection test fails against the pre-fix thoughts.ts, run 1), and bun run check passes (run 2).

Changelog

none

Agent session: agent session