Session Info: a streamed update paints nothing in it
Leo, 2026-11-03: "im still getting these weird layout shift things when im already scrolled up due to idk tool calls shifting/messages coming in maybe that should not happen / likewise i don't want to see flickers of the session info or anything when there's random re-renders EVER"
This PR is the Session Info half: an update that changes nothing Session Info shows (a streamed token, a tool call that makes nothing, a usage tick) draws nothing in it. Keeping the transcript in place while scrolled up is another lane's work.
Root causes
SessionInfo.tsxSessionInfoPresence: every acpmux snapshot is made of new objects, so the lists it derives (summary,made,links,documents,agents,transcriptTasks,processes,waiting,snapshot.sessions,peerKinds, grants, the started chats and workflows) were new on every token, and the<SessionInfo>sections, which were not memoized, all rendered again on every update. They now keep their identity while they hold the same data (newsession-info/sameValue.ts,useSameValue), the sections render through a memoizedSessionInfoSections, and the App's callbacks go throughuseStableCallback.- The same component computed the chat column with
chatColumnOf(...), which returns a new object when the display sets its own width or the page is zoomed. The placement layout effect depends on it, so on every render it rewrote the shell's--acpmux-session-info-columnand--acpmux-header-roomand dispatchedCHAT_LAYOUT_EVENT. The column is now kept by value. - The rows given to the sections (for the chat's gallery) stay the same until a row is added, the gallery count changes or the panel lists another agent. The tool-call ids a remote chat's background task row looks up are read in Presence, so a call added to an existing row still shows up (this was the review's BLOCK at d922a51a70c).
Tests
webviews/test/session-info-no-flicker.test.tsx(jsdom): mounts the pane with Session Info open, then applies 50 updates: a token at a time, a tool call every fifth, and a usage tick with new objects holding the same values. It asserts no DOM mutation inside the panel (MutationObserver,takeRecordsincluded), the same section elements, an unchanged frame style, and zero renders ofOutputsSection/EnvironmentSectionafter mount (both spies count at mount as a positive control). Red at 95aa73309cd (all 50 updates renderedOutputsSection), green here. A second test (a remote chat's task row gets its Show call when the call lands in the existing activity row) is red at d922a51a70c and green here.webviews/test/session-info-no-flicker-browser.test.ts+test/browser/session-info-no-flicker.harness.tsx(Chromium): 50 updates at 1400/1000/600 px (float, push, overlay). It asserts no mutation in the panel, no layout-shift entry with a source inside it, and one bounding rect throughout.
Performance
| metric | before | after | delta | harness or run link |
|---|---|---|---|---|
| Session Info sections rendered per streamed update (jsdom, 50 updates) | 50 / 50 | 0 / 50 | -100% | test/session-info-no-flicker.test.tsx |
| ui.session_info.cls (open play, Chromium) | 0.0000 | see the follow-up comment | bun bench/ui-stability.bench.ts 3 --only session_info, base run https://github.com/manaflow-ai/cmux/actions/runs/41593125846 |
ui.session_info.cls was already 0.0000 at this PR's base (the 0.028 reading predates it). That bench measures the open play, not streaming, so this change is not expected to move it.
No new timer, observer or animation loop.
Observed / Not run
- Ran locally:
bun teston everytest/session-info*.test.tsx,background-task-arrival,native-session-info-toggleandchat-header-every-tool-acts, all green exceptsession-info-dead-clicks("every line of the tray…"), which fails the same way at base.scripts/web-lint.shis clean exceptcheck:design-tokensoncloudStart.css, a base red. - The browser test ran on the fleet at 95aa73309cd: green at 1400 px. At 1000/600 px it recorded one style write on the card, caused by the harness itself: its dock's live line first appeared inside the observed window. The harness now starts that line before it observes. A fleet run of the browser test and the bench at f8af7fba is in progress.
- Native panel: unchanged. It already sends its model only when the JSON changes.
Agent session: agent session
🤖 Generated with Claude Code