cmux NEXT composer: keep Send enabled during trust loading (carries #25287)
Carries #25287 (its owner lane is gone); #25287 closes once this lands.
The branch keeps #25287's commits and authorship (head 4a6bb3c6), adds 2df94b36 (the fix for the first P2 below), merges current feat-cmux-next, and adds the regression test and fix for the second P2.
Review fixes
Both findings are from the Astra review of #25287 at 4a6bb3c6.
- P2 #1:
blockSendread the pane-widefolderConfirming. While one chat's folder confirmation was saving, switching to another chat left that chat's composer disabled with no question shown. Fixed in2df94b36(App.tsx):folderConfirmingnow records the chat whose Use was clicked ({ sessionId }), andblockSendand the folder ask'sconfirmingstate useconfirmingHere, which is true only when that chat is the one shown. I re-read it at the merged head. Every reader of the flag (blockSend,confirmPendingFolder's guard,NewTabFolderAsk confirming) and every writer (confirm, its resolve and reject paths, cancel) uses the new shape. - P2 #2: a deferred retry was released without rechecking live ownership. In
useFolderTrustAsk.ts, the effect that replays a held Trust retry after the folder choice completes calledheld.again()without thesameAuthority()/sameVisit()checks thatsave()runs before it replays. If the client had already selected another chat or connection while React still showed the old snapshot, the render-time checks passed and the held draft could reach the newly selected chat. Fixed: the effect now checkssameAuthority() && sameVisit()(after the existingmounted.currentcheck) right before replay, and both are in its deps. When either check fails, nothing is replayed and the callback stays held. The next render, for the chat now selected, drops released callbacks that belong to other chats, as before.
Red/green
Regression test (committed alone before the fix): webviews/test/trust-refusal-asks-without-read.test.tsx, "a released trust retry is not sent once the live visit|authority has moved to another chat". It holds a retry behind a pending folder choice, saves Trust, and then completes the folder choice in the same render where the live visit() (or authority()) getter moves to another chat. A layout effect makes that move after React commits and before the hook's effects run.
- Red at the test commit:
bun test test/trust-refusal-asks-without-read.test.tsx→ 9 pass, 2 fail (both new cases:Expected: 0, Received: 1; the held retry was sent). - Green at the fix: the same command → 11 pass, 0 fail.
bun test test/classic-new-tab-trust.test.tsx test/composer-queued-placeholder.test.tsx→ 20 pass, 0 fail. This includes "a start refused after a while…", which also passes here.- Each other
test/*trust*file run on its own: all pass. vp fmt --checkandvp linton the 7 changed files: clean.
Observed / Not run
- Observed: the local focused
bun testruns above on the merged head, plus fmt and lint of the changed files. - The package check (
bun testof the touched files plusbun run check) runs through the broker on this PR. Its result is reported separately. - Not run: new before/after captures. The P2 fixes change no rendering, and the captures below are #25287's. No Swift build is involved (webviews only).
An empty folder-trust block currently disables the composer while the trust read is still in flight, so clicking Send or pressing Return silently leaves the draft in place.
Leo: "then when i sent a message to that other chat again with a screenshot of the .heif, it first said Not sent. requires your own click or whatever... that seems weird and unncessary."
This addresses the separate trust-loading stall found while adopting the stale composer send PR.
The composer now holds the draft only for a concrete trust reason. A pending New Tab folder choice remains a separate hard stop, including the trust-resend callback. The daemon still authorizes each prompt, and remote read-only sessions still render no Send button. Queue/steer hints and prompt suggestions use the same distinction.
This replaces the stale implementation in #24478.
Validation
At 3302da2c5e6083c0ede417b5d501e0e094e7d159 (tree 32202df08a9c4f1ceab6de0ca87df4894586ba5f), managed run 40264592718 passed:
- The committed red regression at
7d5a13a6231acf87c3605c923f21ddec70d83bdf: 4 expected failures, then 14/14 passing tests at the final head. - 19 neighboring send-hold, prompt-suggestion, trust-switch and transport-refusal tests; changed-file formatting and typecheck.
- Twelve Chromium/WebKit before-and-after captures across loading, a concrete trust question, and a pending folder choice. The latter two remain byte-identical before/after.
Full web lint has the same four warnings on base de67de98d170fc1dc413965689d198877d9a09ac and the final head: three in conversation/DynamicWorkflow.gallery.tsx and one in test/chat-environment-scroll-stability.test.tsx. The command compares diagnostic paths, codes, severity and messages; it found no added diagnostics and zero errors.
A focused post-send comparison confirmed that WebKit’s “ResizeObserver loop completed with undelivered notifications.” also occurs after a successful send on untouched base. Chromium reported no page errors on either version.
A four-state gallery health comparison reproduced the PR’s four gallery failures on untouched base and the final head, using each source’s own static gallery and the unchanged health runner, with no source or fixture overlays. All three attempts matched: Claude/Codex permission-pill states could not find .acpmux-access-trigger; composer#file-not-attached and new-tab#attachment-menu both timed out in the play’s 5000 ms waitFor. The new trust-loading gallery states passed.
Independent Codex gpt-6-astra xhigh review of the final head: SHIP. The reviewer reconciled the enclosing remote-send render guard and reported no remaining correctness findings.
Captures
The gallery uses the real Composer with its pane material tokens and public sample drafts.
| Trust read loading: before | After |
|---|---|
![]() | ![]() |
| Concrete trust question still blocks | Pending folder choice still blocks |
|---|---|
![]() | ![]() |
Changelog
Fixed: Keep Send usable while folder trust loads, while preserving actual trust and pending-folder blocks.
Agent session: agent session
🤖 Generated with Claude Code



