fix(next): Cmd-Return steers in agent chats on the first press

Merged
AU
16d ago
+78-26

Summary

In cmux next, Cmd-Return in an agent chat did not steer the running turn. It showed "this workspace has no todo items" instead.

The cause: toggleChecklistItemComplete has a default of Cmd-Return. That default was added with workspace checklists in a7a60a35dbb84. It applied everywhere except a terminal and the code editor, so the native key router took Cmd-Return before the composer's keyDown (webviews/src/agent-session/acpmux/Composer.tsx, commandReturn && steerable) saw it. The toggle then found no current item and refused.

The fix adds KeyBindingDefaults.yieldsToWebContent and notWebContent, next to the existing yieldsToTerminal and yieldsToCodeEditor. Where surfaceKind is agent (agent chat, including the agent new-tab page) or page (a browser page), the toggle's default binding steps aside and the key goes to the page. The Todo page and the sidebar checklist keep Cmd-Return. A user's own binding keeps its own when.

  • schemas/defaults/cmux-next.json is regenerated with defaults_guard.py snapshot (new steps-aside.yieldsToWebContent).
  • The toggleChecklistItemComplete entry in docs/configuration.md now says where the default steps aside.

Second fix: a steer shows as sent from the first press

Takes back #20391: the steer label made the first Cmd-Return look like it had not sent (https://github.com/manaflow-ai/cmux/pull/20391). It removes that PR's "Steering · The agent reads it when its current step ends" label under a steer the agent has not read yet. With it, the first Cmd-Return looked like it had only entered a steer mode, so people pressed again. "Steered · the agent read it" (the read confirmation that PR added) and "Sent now · stopped the turn" stay.

Evidence, from the dogfood chat on tag 22616-cmd-return-steer-fix (acpmux event log):

  • The first Cmd-Return's steer was written to Claude Code 0.2 s after the press.
  • Claude Code answered steered 2.2 s later, at the end of its current step.
  • Delivery was already immediate; only the label made it look like it was not.

Change:

  • SteerMark draws nothing for a waiting steer.
  • The dead [data-steer="waiting"] CSS rule is removed, and the gallery note is updated.
  • steer.waiting and steer.waitingDetail stay in the catalog, unused.

Tests:

  • webviews/test/agent-pane-queue-steer.test.ts: a steer has no mark while the reply it went into streams, then reads "Steered" once the agent's next message starts. A second steer in the same step leaves both unread until then.

Changelog

Fixed: Cmd-Return in an agent chat steers the running turn again, instead of refusing with "this workspace has no todo items", and the steer shows as sent from the first press.

Testing

Packages/macOS/CmuxNext/Tests/CmuxNextAppTests/ChecklistCommandReturnKeysTests.swift sends Cmd-Return through KeyRouter.decide, the dispatcher's real decision path:

  • agent chat → deliver
  • browser page → deliver
  • Todo page → toggleChecklistItemComplete

Command: cmux-ci run --class light --script scripts/ci/run-swift-testing-suites.sh --arg=Packages/macOS/CmuxNext --ref <sha> (Xcode 26.6, AWS Macs).

Observed / Not run

  • Merged feat-cmux-next again at 69683eb899ab. Base now shows no steer line under another agent's message, so this PR's incoming-card height and agent-pane-suggested-tasks changes were dropped in favor of base.

  • Observed: the red and green results above.

  • Observed: the same 17 other suites fail at both the red base and the head; this PR does not cause them.

  • Observed: 7 suites failed only in the green run. They are timing and load failures on shared hosts, unrelated to key routing:

    • ExtensionsMenuPerformanceTests: 20 ms against a 16 ms budget
    • SidebarHoverCardDwellTests: manual-clock wait
    • BranchStateResourcesTests: daemon timeout
    • MotionMasterSwitchSurfaceTests
    • MoveToNewWindowTests
    • GhosttyImageStorageLimitTests
    • VideoWallpaperTests

    Four of them passed in the red run.

  • Observed: python3 -I scripts/ci/defaults_guard.py check-fresh passes on the head.

  • Not run: a tagged build with a live Cmd-Return steer in a running agent turn.

  • Merged with feat-cmux-next at 1946aae41d61, after https://github.com/manaflow-ai/cmux/pull/22711 landed. That PR is the general rule: a default on Return, Cmd-Return, Shift-Return, Option-Return, Esc or Up steps aside while a text field has the keyboard. It covers internal pages such as the diff viewer's comment composer, which this PR left as a follow-up. The two rules are complementary and both are kept: this PR also yields Cmd-Return on a browser page or agent chat when no native text field is focused. A Chromium page's form field does not set textInputFocus, so only this PR's page rule hands it Cmd-Return. The only conflict was the adjacent declarations in KeyBindingDefaults.swift.

Review

A correctness review subagent returned FIX for one finding: the defaults snapshot was stale. That is fixed in 39f222797b58. Its other findings confirmed:

  • No other Cmd-Return binding wins in an agent or browser page.
  • The Quick Composer is a separate panel and was never affected.
  • The Todo page keeps the toggle.

This PR changes a defaults snapshot, so it needs an approval from a defaults-call owner (.github/defaults-call-owners.json).

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Cmd-Return in an agent chat steers the running turn again instead of hitting the checklist toggle and refusing with "this workspace has no todo items", and a steer now shows as any sent prompt until the agent reads it.

The checklist toggle's default Cmd-Return binding applied everywhere except terminals and the code editor, so it won in agent chats and browser pages before the page's own handler saw it. Its default now also steps aside when the surface kind is agent or page; the Todo page keeps Cmd-Return, and user-defined bindings are untouched. A steer the agent has not read renders as any sent prompt, with no status label; once read it shows "Steered · the agent read it".

The branch also carries in merged CI fixes: the web-bundles job no longer runs self-tests that could skip the Mac tiers, the fast-tier cache seed runs once per pool (hosted and Blacksmith) in its own lane, and a newer merge no longer replaces a pending media tour run.

Migration

  • The defaults snapshot in schemas/defaults/cmux-next.json adds steps-aside.yieldsToWebContent, so this needs approval from a defaults-call owner.