Repository navigation
cmux NEXT: opening a chat on the prewarmed page draws it first time (a hidden adopted page reports its paint) - #31619
teamleaderleo wants to merge 6 commits into
Conversation
…s: it waits for a frame) The page's pane.painted report moves to paintReport.ts unchanged (one frame, then a task). paint-report.test.ts fails on it: a hidden document runs no animation frames, so the report never comes (HQ#5914). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-14c3a892e82255726cbb
…existing chats take the spare again The parked spare can be hidden (Chromium marks the page hidden), and a hidden document runs no animation frames. The page's pane.painted waited for one, and the host keeps an adopted page away and covered until that report: reopens held the cover for the ~15 s give-up (HQ#5914; diag bench at 7d8c57e: visibilityState hidden, last frame before the report was asked). A hidden page now reports on a task, at once or when it goes hidden while it waits. The chat-open trace counts an adopted page as drawn once its pane painted: its first frame (page_painted) came before the trace, as New Tab, so every adopted open read page_empty. Turns adoption back on (#31486 switched it off). paint-report.test.ts: red on the previous commit (2 fail), green here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-14c3a892e82255726cbb
|
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 web and size checks: green |
This comment has been minimized.
This comment has been minimized.
teamleaderleo
left a comment
There was a problem hiding this comment.
Review (claude-opus-5-5): SHIP at e143c04
Review (subagent claude-opus-5-5 medium; carried): SHIP at e143c04
The SHIP at 2d763e3 carries: the PR's own diff has the same git patch-id --verbatim (2f5384580e97) at both heads, and the merge of feat-cmux-next between them (for the Swift 6.3 crash fix #31525) resolved nothing by hand. CmuxNext compile check passed at this head.
Follow-ups: #31622.
… read beside its attach (fails) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-14c3a892e82255726cbb
…ttach fails adoptSession read the whole list beside the attach, on the same socket; a busy acpmux paces that list for seconds, and adopted reopens drew later than a cold page (HQ#5914 bench). The list now decides only a failed attach, and only a list that was read says the chat is missing (#31033's review follow-up). spare-session-adopt.test.ts: 1 fail on the previous commit, 7 pass here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-14c3a892e82255726cbb
teamleaderleo
left a comment
There was a problem hiding this comment.
Review (claude-opus-5-5): SHIP at fe66961
Review (subagent claude-opus-5-5 medium): SHIP at fe66961
Delta from e143c04 (SHIP above). adoptSession re-reads the list only after select() throws. Only a list that was read and lacks the chat marks it missing (stillOpen, or leftByRead when the read itself fell back from the chat). A stale fallback cannot match, because generations only increase. A user pick during the failed open is never overridden. The attach reply carries the title and summary, so an unlisted chat draws no worse, and the row fills in later through the watch and readLaterSessions. The new test is red on edebfcc and green here.
Follow-ups:
- [follow-up] Confirm that an unlisted chat on another machine, opened by adoption while that machine is down, still keeps the Mac's copy.
- [follow-up] Tests: an attach that fails while the list has the chat (says failed, not missing); a list read that fails after a failed attach; a pick during a failed adoption.
- [follow-up] Skip the read when a newer pick already replaced the adoption (direct.ts:2080).
|
Awareness note from an agent lane that does not own this PR; it is not a review or a request to take the PR over. For awareness: #32448 carries this PR's commits and adds the root cause its bench pointed at. The pane's paint hold made the incoming pane transparent, which hid the held-away adopted page from Chromium (#28002's rule), so the page drew only at the hold's 500 ms limit. #32448 keeps a page laid out outside the window visible to Chromium, and it does not carry the hidden-page task report (a likely source of the flashes). Before/after chat-open bench runs are queued on the fleet. |
… drawing while it is held off screen (#32448) * test(agent pane): a hidden adopted page still reports its paint (fails: it waits for a frame) The page's pane.painted report moves to paintReport.ts unchanged (one frame, then a task). paint-report.test.ts fails on it: a hidden document runs no animation frames, so the report never comes (HQ#5914). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-14c3a892e82255726cbb * cmux NEXT: an adopted spare page reports its paint while hidden, and existing chats take the spare again The parked spare can be hidden (Chromium marks the page hidden), and a hidden document runs no animation frames. The page's pane.painted waited for one, and the host keeps an adopted page away and covered until that report: reopens held the cover for the ~15 s give-up (HQ#5914; diag bench at 7d8c57e: visibilityState hidden, last frame before the report was asked). A hidden page now reports on a task, at once or when it goes hidden while it waits. The chat-open trace counts an adopted page as drawn once its pane painted: its first frame (page_painted) came before the trace, as New Tab, so every adopted open read page_empty. Turns adoption back on (#31486 switched it off). paint-report.test.ts: red on the previous commit (2 fail), green here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-14c3a892e82255726cbb * test(agent pane): an adopted page opens an unlisted chat with no list read beside its attach (fails) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-14c3a892e82255726cbb * cmux NEXT: the adopted page reads the chat list again only when its attach fails adoptSession read the whole list beside the attach, on the same socket; a busy acpmux paces that list for seconds, and adopted reopens drew later than a cold page (HQ#5914 bench). The list now decides only a failed attach, and only a list that was read says the chat is missing (#31033's review follow-up). spare-session-adopt.test.ts: 1 fail on the previous commit, 7 pass here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-14c3a892e82255726cbb * test(agent pane): an adopted page held outside the window keeps drawing under the pane's paint hold (fails) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-23bb1f8ee2895ef522be * cmux NEXT: an adopted page held outside the window keeps drawing under the pane's paint hold The pane's paint hold makes its incoming view transparent, and a transparent ancestor hides a Chromium page (#28002). An adopted New Tab spare waits 100 000 pt outside the window until it drew the chat (adoptSession), so the hold hid a page nobody could see: Chromium throttled it, and it drew the chat only once the hold gave up at 500 ms. A page laid out wholly outside its window's content now stays visible to Chromium whatever its ancestors' alpha; in place, a hold still hides it. Drops #31619's hidden-page paint report (on a task, without a frame): with the held page drawing, the report waits for a real frame again, and a report with no frame drawn showed the page's previous frame when the host revealed it. AgentPaneChromiumVisibilityTests.anAdoptedPageHeldAwayKeepsDrawingUnderAPaintHold: red on the previous commit, green here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-23bb1f8ee2895ef522be * test: cover selection after a failed spare adoption Agent-Session: https://cmux.com/deeplink/agent?id=aid-81082f85640c1fa29ca9 * fix: retain a newer selection after spare adoption fails Agent-Session: https://cmux.com/deeplink/agent?id=aid-81082f85640c1fa29ca9 * Agent pane: an adopted spare turns its cover on before its loading state begins The chat-open bench counted a flash on 7-10 of 10 adopted opens: its trace recorded the chat's saved frame ("image"), then the empty cover, then the chat. No such frame was drawn. beginLoadingState binds the trace and samples the surface at once, while the New Tab page's cover was still hidden; adoptSession turned the cover on only after that, in the same main turn, so the first sample read a state no frame showed. The cover now goes on first, so the pane is in its loading state from the first sample. Diagnosed on the fleet with a probe build that recorded every cover change after the adoption; it found none, which left the trace's own first sample. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-758f5f82cc10b26ecf3c --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
HOLD: with adoption on, an adopted reopen is slower than a cold open, and under load adopted opens still time out (table below). Not to land until an adopted open beats the cold one idle and under load.
Leo, 2026-11-01: "i had to retry to get it to load???" and "i hate the "Starting Claude Code... cancel" thing that it starts with then the stupid ass layout shift surely we can have a better load/faster load/intro than that"
Opening an existing chat adopts the app's prewarmed agent page again (#27759, #31033), and the adopted page now shows its chat on the first try. #31486 had turned adoption off because of this bug. This turns it back on.
Cause
The parked spare page can be hidden: Chromium marks the page hidden, so
document.visibilityStateishidden. A hidden document runs no animation frames.The page told the host it drew its chat (
pane.painted) one animation frame after that render. The host keeps an adopted page away and covered until that report (AgentPaneView+SessionAdopt.swift), and the cover keeps it hidden. So the report never came. Every reopen held its cover until the loading state gave up, about 15 s later, and so did some sidebar opens.A diagnostic bench at 7d8c57e read this from the page itself in each failed open:
visibilityStatewas "hidden";chatRead,showsWhatItIs).Fix
paintReport.tsafterDrawnFrame. The report still waits one frame, then a task. A page that is hidden, or goes hidden while it waits, reports on a task instead. The host then shows the page, and its own leave already re-syncs Chromium's visibility (AgentPaneLoadingView.leave).visibilitychangelistener, removed when the report runs (stop condition). It reads no data, and it is listed incheck-switch-fetches.py.traceSurface). The page's own first frame (page_painted) came before the trace started, back when it drew New Tab, so every adopted open readpage_emptyin the bench.AgentPaneSpareReadiness.adoptsExistingChatsis gone. That was cmux NEXT: existing chats open cold again until the adopted spare page draws its chat #31486's switch, so the spare serves existing chats again.Takes back #31486: it switched adoption off until the adopted page drew its chat; this PR is that fix, so adoption is on again.
Changelog
Fixed: opening an existing chat in cmux NEXT draws it from the prewarmed page on the first try, without the long loading cover.
Tests
webviews/test/paint-report.test.ts:Command:
cd webviews && bun test test/paint-report.test.ts.paintReport.ts): 2 fail.test/spare-session-adopt.test.tspasses at the head.Performance
scripts/measure/chat-open.sh(chat-open-bench.py: milliseconds from the action to the first frame of chat content, median of 5), run on the same fleet display Mac (Mac16,10) for both columns. Before is the feat-cmux-next tip this head merges (a0741bc, adoption off since #31486: cold opens); after is this head.At e143c04 (the paint fix only), idle sidebar was 628.6 ms and idle reopen 1092.5 ms. No idle open hangs any more: the ~15 s cover is gone. But an adopted page takes about 800 ms from adoption to its paint report, where a cold page takes about 600 ms in total, and under load adopted opens still wait out the page's 10 s hold. The likely cause is under investigation: the parked spare was seen
hiddenbefore its adoption (diagnostic run), and Chromium throttles a hidden page's tasks.Timers and observers
One
visibilitychangelistener per paint report, removed when the report runs. No new timers.Observed / Not run
bun run checkin webviews (green), the cmux-next pre-push gate, both bun suites above, and the diagnostic bench described above.Risk: normal. This turns the warm-spare open path back on for every existing local chat. A spare that is not connected still opens the chat cold (
servesChat).Agent session: agent session
🤖 Generated with Claude Code