Model menu: a started chat's harness is fixed (no rail, no Add harness…)

Merged
TE
17d ago
+390-120

Summary

In a chat that already has a message, the composer's model menu drew a harness rail holding only that chat's harness (plus the Starred tab), then offered "Add harness…" as its last row. The chat can't switch harness (it has been locked since 2026-10-13), so a rail of one icon followed by an offer to add another read as a broken switcher.

Now a started chat's harness is fixed, as in Claude Code:

  • Started chat (turnCount > 0 or any row): the full picker is search, that harness's models and More models. No harness rail, no Starred tab, no Add harness…. The compact menu (agent.modelMenu) gets no rail either; its More models opens the same rail-less picker. Row stars still toggle, since they don't need the rail and feed the New Tab's Starred tab.
  • New Tab and chats with no message: unchanged. The rail lists every installed harness, and Add harness… stays.
  • Debug Settings toggle "Allow switching harness in a started chat" (agent.harnessSwitchInStartedChat, default off). On, a started chat keeps the whole rail, never just its own harness, plus Add harness…, and a pick in another harness starts a new chat there through the existing chat.new path. It reaches the page live as the composer event's harnessSwitch, beside plainFooter and modelMenu. It's a Debug Settings tunable, not a cmux.json key, so the settings lists don't change.

Mechanism: ModelPicker takes harnessFixed; ComposerPickers passes its existing locked value, which the toggle now overrides. If acpmux hasn't listed the chat's own harness yet (first open after launch), the picker keeps the old full rail rather than show an empty menu.

Testing

Ran on Huge Blue (bun test, per file):

  • Red on the tests-only commit c75798ddcc: 3 fail (the started-chat picker, the toggle-on rail, the started-chat compact menu).
  • Green on the head 15b21d38fb: test/model-picker-picks.test.tsx 22/22, test/model-menu-compact.test.tsx 19/19, test/signed-out-rail.test.tsx 4/4; model-picker-composition, model-picker-height, effort-picker-slider, gallery-coverage and gallery-manifest pass.
  • CMUX_BROWSER_TESTS=1 bun test test/agent-pane-harness-catalog.test.ts (Chromium) passes at 15b21d38fb. It opens Add harness… from a started chat, so it turns the started-chat harness switch on through window.cmuxAcpmuxComposer first, which tests the toggle end to end in the page.
  • bun run typecheck: no type errors in 1629 files. vp check (fmt + lint) is clean on the changed files.
  • test/ui-rules.test.ts fails the same way at the base ca13780ceb (+4 received); it isn't from this change.
  • A full bun test run in one process fails about 148 tests at the base from cross-file state; the only new failures at the head were the three new tests, which pass when run per file.
  • verify-local.py --affected: Swift syntax, test wiring, xcstrings and the other checks pass. localization fails with 35k parity errors in catalogs this PR doesn't touch (a base finding).

Observed / Not run

Not run: the Swift side (AgentPaneTunables.harnessSwitchInStartedChat, the composer event's harnessSwitch and the new AgentPaneHarnessSwitchTunableTests) is only syntax-parsed here. It follows the modelMenu wiring line for line. PRs into feat-cmux-next skip cmux-next swift test, and no native build ran on this Mac. I haven't checked the toggle in a tagged build.

Changelog

Changed: In a chat that has started, the model menu lists only that chat's harness's models, with no harness rail or Add harness….

Proof

Gallery fixtures agent-pane.model-picker started-chat (full picker) and started-chat-quick-menu (compact menu) let the Linux gallery advisory run capture both states. Screenshots will be added here from that run.

Checklist

  • Behavior changes have added or updated tests
  • Localization audited: no new user-facing strings (the Debug Settings label and help are English developer text, as for every tunable)

🤖 Generated with Claude Code