cmux NEXT model button: names the effort on the agent default (Opus 5.5 Medium), checks it in Effort, and draws no dot

Merged
TE
14d ago
+704-21

Leo, 2026-10-22: "this should just show the effort too right? and then the effort tells you what you're already currrently on?"

Leo, on the dot: "he sees a dot? idk why we dont see a dot", then "BRO WTF NO WE FUCKING HATE THE DOT".

Leo-ask: ask-fa17041e5d
Leo-ask: ask-4b7bcd11c2
Leo-ask: ask-24514f48c1

His two screenshots: the composer's model button read "Opus 5.5" alone, and the model menu's Effort submenu listed Low, Medium, High, Extra High, Max with no level checked. The chat was on a Claude Code profile (claude-sr2) running the agent's default effort. Effort already shows on the button and gets a check when the chat has picked a level explicitly (#23808). This chat was on the agent's own default.

What changes

  • The button names the level the chat actually runs on. A chat on the agent's default effort runs the model's own level. ComposerPickers already resolved that through catalogModel.defaultEffort, but a profile the catalog doesn't map gets its model rows from acpmux's probe, which names the model and not its default level. So effortValue came out undefined: no word on the button, nothing on the Effort row, no check. The default level now falls back to another catalog row for the same model (the same family, then any harness, the order catalogModel already searches). The button reads "Opus 5.5 Medium", the Effort row names Medium, and the submenu checks Medium with the menus' own check glyph. A level the chat picked still wins, and the catalogued claude harness behaves as before.
  • Only this chat's own release lends a level (Codex reviews at dfb97c7 and 03c55364). The levels and the default level come from the chat's own model row, else from the release it runs. That release is the model itself when the model catalog lists it, else the alias row's release, acpmux's row for it, or the release the session's model option reports (Claude Code's opus running claude-opus-5-5), once no switch or other pick is in flight. Other profiles' rows are searched only for that release, its pinned row first, never for a raw alias, because what opus runs depends on each installed harness. An alias with no known release names no level and Effort checks none.
  • No dot on the button. The one dot the button could draw was the agent-update dot (.acpmux-model-update-dot). It sits after the model name, so when Claude Code or Codex has an update it lands between the model and the effort. No label formatter writes "•" or "·". The dot is gone, and the update still shows in the button's tooltip and in the menu's and picker's Update row. These hunks match #24151's text exactly, so the two PRs combine cleanly in either order.

Look: menus stay glass, the check is the existing .ui-menu-check, the effort word is the existing secondary ink, and there's no new colour, dot or badge.

Before / after (gallery agent-pane.model-button-default-effort, Linux Chromium)

StateBefore (base eaccc63f581)After
claude-sr2 chat on the agent's default effort, at rest"Opus 5.5""Opus 5.5 Medium"
Its model menu, Effort openEffort row has no value; no level checkedEffort row names Medium; Medium checked
Same chat with a Claude Code update on offer"Opus 5.5 ●" (round dot after the name)"Opus 5.5 Medium", no dot
A level the chat picked (High)"Opus 5.5 High", High checkedunchanged
Catalogued claude chat on the default"Opus 5.5 Medium", Medium checkedunchanged

The shots are private and stay off this PR. The gallery advisory run shows the entry's variants at both builds.

Tests

The first commit adds the tests and gallery entry, the second the fix.

  • bun test test/model-button-default-effort.test.tsx (JSDOM, the real composer, catalog and menu): 2 fail / 2 pass on the tests-only commit (button has no "Medium"; Effort row empty and nothing checked). The alias tests fail on dfb97c7 (no level). The cross-profile tests (the catalogued claude profile folds opus into Opus 5.5 at Medium; this claude-sr2 chat's opus resolves to nothing, or to the unknown claude-opus-9-9) read "Medium" at 03c55364 and expect none. 11 pass on head f7f1539eb15.
  • CMUX_BROWSER_TESTS=1 bun test test/model-button-no-dot.test.ts (Chromium and WebKit, gallery stage): the button's text and drawn ::before/::after content carry no •, ·, ∙, ● or ⋅, the model reads straight into its effort, and no element in the button is a painted circle of 10 px or less, with and without an update on offer. With the dot span restored it's 2 fail / 2 pass (the update-pending variant in both engines). 4 pass on head.
  • Neighbours pass on head: model-menu-compact, HarnessHealthNotice, NoticeSlot, EffortPicker, reasoning-own-control, reasoning-chip-steps (45 tests), plus 480 tests across 109 model, effort, chip, composer and new-tab files.
  • bun run check passes, and so does scripts/cmux-next/prepush-gate.py.

Observed / Not run

  • Observed: the whole webview suite in one process is noisy on base and head alike (cross-file DOM state). I ran each file behind a head-only failure on its own. Eleven fail alone at head (changelog-pictures, diff-label-bundle, diff-worker-bundle, hairline-rims, lit-glyph-css, motion-scale, palette-eval, summary-background-work, theme-colors, tooltip-rules, ui-rules), and all eleven fail the same way at base eaccc63f581.
  • Not run: the native app (webview-only change).

No capture: webview-only change; gallery entry agent-pane.model-button-default-effort (variants profile-default, profile-default-effort-menu, profile-update-pending, profile-picked-high).

Overlap: #24151 removes the same dot with the same text; #24224 and #24169 touch these files without changing the default-effort resolution.

Changelog

  • Fixed: the composer's model button names the effort a chat runs on, also on the agent's default ("Opus 5.5 Medium"), its Effort submenu checks that level, and it never draws a dot between the model and its effort.

Agent session: cosmic-gray

🤖 Generated with Claude Code