cmux NEXT model picker: every harness its own tab again, Claude Code full model ids (carries #27692) + failed-probe launch tabs

Merged
TE
6d ago
+301-251

Carries austinywang's #27692 (his three commits, with his authorship) plus the fix its exact-head review asked for. #27692 stays his to close; this lands it without waiting, since it fixes a regression Leo reported.

Leo, 2026-10-30: "honestly we had a pretty good thing going with teh symbols of the harneses at the top :("

Commits

  • dd777ca95dd, be3e7a42342, ba7e7f57eb3: Austin Wang's #27692, cherry-picked unchanged onto the feat-cmux-next tip b69f04615cd. All three applied cleanly.
  • 724707fbf3d: fix: a launch entry whose probe failed keeps its launch tab. The Codex review of #27692 at a327a7cbde3e blocked on this (posted as #27692 issuecomment-6516293952). hasModels checked only each row's own disabled, and the harness-level HarnessChoice.unavailable (its probe failed) was dropped when the rail rows were built. So a launch entry whose probe failed, but whose catalog still listed models, became an ordinary tab that could start a stale model. That contradicts #27692's own body: "An entry with no pickable models (none listed, or its probe failed) still launches from its tab". The QuickFamily now carries unavailable, hasModels is false for it, and a jsdom test ("a new chat still launches a named harness entry whose probe failed from its tab") covers it.

Evidence

Captures

Gallery state agent-pane.model-picker/quick-menu-many-profiles (step 1, the picker open on Claude Code), rendered at device scale 2 by the gallery workflow at the merge base (before) and at the head (after): the Claude (work) launch entry gets its own harness tab with its mark again instead of a row under Claude Code's models.

LightDark
Beforebefore lightbefore dark
Afterafter lightafter dark

Viewed after image: yes
Visual verdict: Each harness sits in the left rail as its own tab with its mark, and the menu fits on screen without repeating a control per row, so it holds up next to ChatGPT or Claude.

Takes back #26969: on purpose; Leo asked for every harness to have its own tab again ("we had a pretty good thing going with teh symbols of the harneses at the top"), so this reverts its folding of harnesses into one tab.

The gallery workflow captures the picker's harness tabs before and after, for each state on this line:

Gallery: agent-pane.model-picker/quick-menu-many-profiles, agent-pane.model-picker/started-chat-fork, agent-pane.model-picker/started-chat-quick-menu, agent-pane.model-picker/quick-menu-rail

#27692's description

Summary

The model picker in cmux NEXT nightly (1.0.0-nightly.4093446036501, built from 6c1a5ab) regressed. This PR fixes two problems.

1. Custom harnesses lost their tab and their models. https://github.com/manaflow-ai/cmux/pull/26969 (merged 2026-10-29, 7f7d931) folded every harness that draws the Claude or Codex mark into one rail tab (canonicalHarnesses). A harness like claude-sr2, which is its own profile with icon = "claude", disappeared from the rail. It became a launch-only row inside Claude Code's tab and showed no models. Claude Code (acp), Claude Code (sr), Codex (work) and the other launch configurations went the same way.

This PR reverts https://github.com/manaflow-ai/cmux/pull/26969: the webview side is equivalent to git revert 7f7d9315343c (its two conflicts are context from later commits). It removes canonicalHarnesses, the variants block and the canonical-harnesses-* gallery states, and restores test/model-menu-started-chat-fork.test.tsx to its pre-#26969 version. The density (https://github.com/manaflow-ai/cmux/pull/27320), search grouping (https://github.com/manaflow-ai/cmux/pull/27303), Provider row (https://github.com/manaflow-ai/cmux/pull/27151), checkmark (https://github.com/manaflow-ai/cmux/pull/27232) and empty-search (https://github.com/manaflow-ai/cmux/pull/26962) fixes stay as they are.

The revert alone still left a launch configuration that is not the running harness (claude-sr2 in a Claude Code chat, Claude (work), Codex (work)) as a launch-only tab with no models, as before #26969. Now, when its own catalog entry has a model that can be picked, it is an ordinary tab that lists those models; picking one starts the chat on that harness, as picking a Codex model does. An entry with no pickable models (none listed, or its probe failed) still launches from its tab. In a started chat every other tab still forks; a launch configuration with models now shows "Fork to …" on its tab like any harness, instead of forking on the tab click.

2. GUI chats failed with "There's an issue with the selected model (opus-5-5)". A chat started with a versioned family name (opus-5-5) failed every turn, while the header still read "Claude Code · Opus 5.5". Claude Code accepts opus or claude-opus-5-5, but it sends opus-5-5 to the API exactly as typed. The session's own log shows [claude-code:unrecognized_model] {"model":"opus-5-5"} followed by a 404 model_not_found. The Claude stdio translator (claude_stdio::model_id) now passes Claude Code the full id at spawn (--model), on session/set_model and on the model config option. It does this only for opus-/sonnet-/haiku- followed by a digit. Aliases, full ids and other providers' names pass through unchanged. This applies the rule live_models::claude::launch_id already uses.

Testing

Remote runs only (rbx; this Mac had no agent-identity for cmux-ci and no Freestyle key):

  • webviews (bun 1.4.4 on rbx, lane picker-restore-27692-web, tree of c329f8d7a4): bun test test/model-menu-started-chat-fork.test.tsx test/model-menu-compact.test.tsx test/model-menu-providers.test.tsx test/signed-out-rail.test.tsx test/composer-footer-harness.test.ts: 82 pass, 1 skip (browser lane), 0 fail. New tests: claude-sr2 keeps its tab and lists its models, mounted on claude-sr2 and on claude (picking its model starts claude-sr2); a named entry with models starts from one of them; one with no models still launches from its tab; a second launch entry of one family lists its own models and starts itself.
  • Rust (rbx, lane picker-restore-27692, 51f9cccd6a): cargo test --locked -p acpmux --lib claude_stdio::tests: 19/19, including versioned_family_names_reach_claude_code_as_full_ids. Later commits touch only webviews.
  • vp fmt --check over webviews: clean.

Changelog

  • Fixed: the model picker shows every harness, including custom ones such as claude-sr2, as its own tab with its models again.
  • Fixed: a chat started with a model like opus-5-5 no longer fails every turn with "There's an issue with the selected model".

🤖 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

Fixes two model-picker regressions in the cmux NEXT nightly: every harness is back to its own rail tab, and versioned model names no longer fail every chat turn.

  • Each harness and launch configuration gets its own tab. A launch configuration (like claude-sr2, Claude (work), Codex (work)) whose catalog entry lists pickable models shows them on its tab, and picking one starts the chat on that harness; one with no models still launches directly from its tab. A harness whose probe failed keeps its launch tab and never shows its stale rows, in the tab, in Starred, or in search, unless the chat runs on it. Removes the canonicalHarnesses folding and the variants submenu; the density, search, provider, checkmark, and empty-search fixes from later PRs stay intact.
  • The Claude stdio translator now hands Claude Code full model ids (claude-opus-5-5) at spawn, on session/set_model, and on the model config option. Bare aliases (opus), full ids, and other providers' names pass through unchanged.

Agent session: agent session

🤖 Generated with Claude Code