Repository navigation
perf(agent pane): a streamed chunk re-renders only its row; each frame is parsed once - #34004
Conversation
…ane or the composer (red) HOT PATH AUDIT area F, row W1 (handoff/hot-path-audit/stream/AUDIT.md). The real pane on the in-page mock daemon, 700 seeded turns, one text-only turn streamed at full speed. acpmuxPerf.render counts renders per component (pane, composer, row:<id>); loadRun reports them. On base: 111 chunks, 112 pane renders and 112 composer renders (cloud VM, Chrome 156). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HOT PATH AUDIT area F, row W2. The wire log and the direct client each parsed every frame: 100 JSON.parse calls for 50 streamed chunks on base. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e is parsed once HOT PATH AUDIT area F, rows W1 and W2 (handoff/hot-path-audit/stream/AUDIT.md; lead decision 1 in STATUS). Every streamed chunk re-rendered the whole pane (AcpmuxPane, about 20 memos over all rows), the Composer and Session Info. - streamingRow.ts: when a snapshot from the direct client differs from the one the pane drew only in the streaming reply's text (textOnlyRow: one pointer compare per row; summary, subagentViews and catalog, which the client rebuilds each time, compared by value), the pane keeps its snapshot and the row's newer text goes to a one-value store. The live VirtualTranscript reads it (useSyncExternalStore) and draws that row with its newer text and version. Any other change draws through the pane as before and clears the store. - The fast path runs only while the transcript draws the reply's row as the client holds it (drawsAsIs), so rows that turns.ts reshapes take the old path. - rowsRef and snapshotRef take every snapshot, so copy, menus and other imperative readers see the newest text. - direct.ts parses each frame once and gives the parse to the wire log. Measured on the cloud VM (Chrome 156, dev React; render counts, times directional): 202 chunks rendered the pane 5 times and the composer 5 times (base: 111 chunks, 112 and 112); the reply's row 197 times. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…aming row Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
Gallery before/after for
States
Pick other states with a Passes: CI passes on CI passes on Written by web and size checks: green Shipped in:
The feat-cmux-next push run https://github.com/manaflow-ai/cmux/actions/runs/41705329702 at 872f40c failed: cmux-next Release compile (fleet fast check). |
|
Review (hpa-stream-reviewer-3): FIX at 34d8a58 The store and substitution mechanics are sound: the end of a stream ( Two things that still read the pane's
Smaller:
Tests: the render test discriminates (base 112 pane renders per 111 chunks, head 5 per 224, threshold 12) and the parse test discriminates (base 100 per 50, head 50). Neither checks what the person sees during the stream: the live line and the final reply text in the DOM. I did not run the browser lane here. |
|
Thanks, hpa-stream-reviewer-3: all four findings are valid. I am fixing them, each with a test. (1) The live line's quietKey moves with the substituted reply version; a regression check will hold is-quiet over the whole stream. (2, 3) While an artifact or Find is open, chunks draw through the pane (setStreamingRowBlocked). (4) streamedOnly gets no per-chunk serialization. Smaller items: only the live transcript subscribes, and the row counter does no work when measurement is off. |
…ts and Find stay exact, no per-chunk serialization hpa-stream-reviewer-3 (FIX at 34d8a58): 1. The live line under a streaming reply came back once the pane stopped re-rendering per chunk (its quietKey names the reply's id and version). The transcript's substitution moves the THINKING row's quietKey with the reply's newer version. The render test now samples every frame: 0 frames with the live line shown (164 of 264 with the move turned off). 2, 3. While an artifact is open or Find is open, chunks draw through the pane (setStreamingRowBlocked), so the panel follows an open fence and Find finds new text. 4. streamedOnly no longer serializes anything: the client keeps the published summary (keyed on its summary and usage), the subagent views record and the empty catalog as the same objects while they do not change, so every field compares by identity. Smaller: only the live transcript subscribes to the store; the row counter does nothing when measurement is off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
|
Review (hpa-stream-reviewer-3): SHIP at 9c159b9 Re-review of 34d8a58..9c159b9, and the whole PR where it matters. The four findings from the FIX at 34d8a58 are fixed:
The whole PR again:
Tests: Non-blocking follow-up: if Find opens, or an artifact is opened, during a pause in a reply while a chunk is held, Find searches the pane's older rows. Find then misses the reply's newest words until the next event, and the next event always comes, at the latest at the turn's end. To make it exact at once, the block could draw the pane once when the store holds a row (for example, |
|
Receipt: #34004 (comment) (SHIP at 9c159b9). No red check on this head; the rest are pending (merge rule: pending checks do not hold it). The reviewer's non-blocking follow-up (Find opened while a chunk is held searches the older rows until the next event) gets its own PR with a red test. |
This comment has been minimized.
This comment has been minimized.
Independent performance review — HQ #6236 brief 22 (streamed replies at 3,000-row load)Exact code reviewed: this PR at Fresh source finding
Executed A/B probe, deliberately syntheticDraft HQ PR #6643 contains the standalone script and source/evidence report. Run:
16 synthetic assertions pass across both arms, including multi-chunk coalescing, a changed field, stream end, a changed second row and divergent sibling appends. This is a V8 loop result, not WebKit/CEF performance, first-keystroke latency or a claim about the 3,055 sessions in HQ #4878. Safe follow-up proposal for your ownershipHave the reducer record a private Add a real regression at the owned head: 3,000 transcript rows, 244 KiB streamed reply, coalesced frames, sibling divergence, reconnect/restore, row re-shaping, Find opened during a stream, artifact panel and live-line quiet state. Measure the native page and typing-to-echo on the same worker and workload before claiming an end-user benefit. If the improvement disappears in the real app, keep the conservative guard. The larger alternative is a typed changed-row append delta, removing the remaining whole-row scan but requiring more cross-component review. Ownership / status: independent review and synthetic prototype only. No production test, signed build, real daemon, WebKit, or native input capture was run. #34004 remains the existing owner's implementation and review decision; this finding is a candidate follow-up, not a merge/block directive. |
…am-page-row # Conflicts: # webviews/src/agent-session/acpmux/App.tsx
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
|
Merged feat-cmux-next (after #33945 landed) at 41364af. One conflict, in App.tsx: feat now seeds the pane's rowsRef from |
|
Review (hpa-stream-reviewer-3): SHIP at 41364af This is a new review of the merge head 41364af (origin/feat-cmux-next 59d01c7 merged into 9c159b9, my SHIP head before), not a carried verdict. PR diff unchanged:
Conflict resolution:
Feat changes between the two bases that touch these files:
None of them changes the snapshot listener, Checks here:
I did not run the browser lane. I rely on the author's VM runs at this head: The earlier non-blocking Find follow-up (Find opened during a held chunk) is draft #34072. |
Summary
HOT PATH AUDIT area F, rows W1 and W2 (AUDIT.md; lead decision 1). Every streamed chunk re-rendered the whole agent pane (
AcpmuxPane, with about 20 memos over all rows), the Composer and Session Info. And each frame from acpmux was parsed twice.streamingRow.ts): when a snapshot from the direct client differs from the one the pane drew only in the streaming reply's text, the pane keeps its snapshot. The row's newer text goes to a one-value store.textOnlyRowis one pointer compare per row.summary,subagentViewsandcatalog, which the client rebuilds every time, compare by value.VirtualTranscriptreads the store (useSyncExternalStore) and draws that one row with its newer text and version.drawsAsIs), so rows thatconversation/turns.tsreshapes take the old path.rowsRefandsnapshotRefstill take every snapshot, so copy, menus and other imperative readers see the newest text.direct.tsparses each frame once and hands the parse to the wire log (wire.tsreceived(text, parsed)).acpmuxPerf.render(name)counts renders while measuring.loadRunreports them.To the page owner (Leo): this touches
App.tsx(pane listener,VirtualTranscriptpropstreams),direct.ts(receive) andwire.ts.Testing
Red tests first (on this cloud VM, Chrome 156; render and parse counts are deterministic, times are directional only):
test/agent-pane-stream-renders.test.ts: the real pane on the in-page mock daemon, 700 seeded turns, one text-only turn at full speed.test/agent-pane-session-change-coalesce.test.ts, "a streamed chunk's frame is parsed once": base 100JSON.parsecalls for 50 frames (fails); head 50.Existing tests with the fix (VM Chrome; WebKit not installed here, so only Chromium ran): agent-pane-streaming-no-flicker, agent-pane-stream-jitter (10), streaming-parked-still (4, 1 skip), transcript-row-renders, agent-pane-session-change-coalesce (8), composer-stream-typing, streaming-markdown-forming (38), chat-find: all pass.
bun run checkpasses.Observed / Not run
Changelog
No capture: no visible change.
Open in [code]smith · Need help on this PR? Tag
@codesmith-botwith what you need.Summary by cubic
Fixes the agent pane so each streamed chunk re-renders only the streaming reply's row instead of the whole pane (~20 memos, the Composer, and Session Info), and parses each acpmux frame once instead of twice.
streamingRow.ts) read only by the live transcript; any other change draws through the pane and clears the store, and chunks draw through the pane while an artifact or Find is open.rowsRefandsnapshotRefstill take every snapshot.direct.tsparses each frame once and passes the parse to the wire log.Written for commit 41364af. Summary will update on new commits.