perf(transcript): skip unchanged layout prefix (cmux#34383)
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 a1fba86c1ae9 | Head |
|---|---|---|
| streamed token main-thread time, run 1 | 6.764, 7.440 ms | 7.196, 7.234 ms |
| streamed tool-output time, run 1 | 9.139, 9.016 ms | 10.243, 10.594 ms |
| tool-output slope per 1,000 rows, run 1 | -0.192, 0.335 ms | 0.367, 0.617 ms |
| streamed token main-thread time, run 2 (noisier runner) | 11.05, 8.03, 13.63, 7.15 ms | 10.46, 8.64, 20.71, 12.35 ms |
| streamed tool-output time, run 2 | 15.80, 11.04, 15.14, 10.40 ms | 12.65, 10.70, 20.05, 12.01 ms |
| tool-output slope per 1,000 rows, run 2 | 2.74, -0.46, -0.65, 0.86 ms | 1.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