Repository navigation
Chief engine picker: chief.engine.get offers every acpmux harness with what it supports - #23945
Conversation
The engine picker needs offered.harnesses[]: each acpmux harness a Chief turn can run on, with displayName, family, capabilities and why it is unavailable. Tests only; the brain does not answer it yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cfc-5b39-7e21-bc3d-02eb374c820c&host=cmux16
…es (red) Tests only; ChiefEngineHarnessOffer does not exist yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cfc-5b39-7e21-bc3d-02eb374c820c&host=cmux16
…it supports offered.harnesses[] lists each acpmux harness a Chief turn can run on (_acpmux/harnesses, read at each answer): id, displayName, acpmux's family and the Chief's layout family, kind, capabilities (efforts, speeds, the profile's resume/permissionModes/fast), unavailable with the reason (acpmux's own, or the harness gate's refusal), and description, icon and login when the profile has them. Terminal-only harnesses are left out. ChiefEngineHarnessOffer decodes it on the Mac; the picker UI wires it in after #23883. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cfc-5b39-7e21-bc3d-02eb374c820c&host=cmux16
|
Owned by chief-fix-4 on huge-blue · session |
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
red: cmux-next checks (god files, concurrency, crash safety, l10n); no finished base run on 87770f7 to compare; do not merge until green; see lane-rules |
|
Found 5 test failures on Blacksmith runners: Failures
|
|
Bead |
Conflict in optchat-chief/tests/engine_offer.rs: base appended the_engine_set_takes_the_reaction_classifier where this branch appended the_engine_answer_offers_every_harness_with_what_it_supports. Both tests are kept. Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a174e4-3868-7f51-8f3d-21b34ad270b2&host=huge-blue
The review (codex gpt-6-astra xhigh at 0a245f4) found two ways the picker could offer an engine chief.engine.set answers with an error: - Discovery read acpmux's live catalog while a set checks the families the brain read at its start, so a harness acpmux gained since showed as available and then answered unknown_harness. Such a row is now listed unavailable with that reason ("restart the Chief"), which is step 4 of the PR's remaining work. - A profile may declare an effort acpmux takes and a Chief set does not ("none"), which answered invalid_effort. Declared efforts are now filtered to the ones EFFORTS accepts; none left falls back to the full list. Test: an_offer_never_names_a_harness_or_effort_a_set_would_refuse asserts both rows and then asserts the set answers the refusal each row names. Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a174e4-3868-7f51-8f3d-21b34ad270b2&host=huge-blue
|
|
|
|
The second review (codex gpt-6-astra xhigh at cc42a8b) found two more ways the offer could name something chief.engine.set refuses: - Speeds came from the live profile family while a set checks the start-up family (Brain::family_of): a harness acpmux re-families keeps its row available and offers Fast, and the set answers invalid_speed. offered_harnesses now takes the set's own family_of and filters with it. - The id disambiguation could still collide: two profiles named X and a third already named "X (a)" leave two rows called "X (a)". A name still shared after that pass falls back to the id, which is unique. Tests: an_offer_never_names_a_harness_effort_or_speed_a_set_would_refuse (renamed) adds the speed case and asserts the set answers invalid_speed for it; no_two_rows_leave_the_picker_with_the_same_name covers the collision, with a claude-vm profile in the fake that is already called "Claude Code (claude)". Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a174e4-3868-7f51-8f3d-21b34ad270b2&host=huge-blue
|
|
… needs the_engine_answer_offers_every_harness_with_what_it_supports ran with an empty start-up families map, so with speeds now following the set's own family_of (the previous commit) it read every harness as Other and codex offered no Fast. The test gives the brain the families acpmux's fake reports, which is what a real brain reads at its start, and codex offers Fast again. Local: cargo test --test engine_offer, 9 passed, 0 failed. Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a174e4-3868-7f51-8f3d-21b34ad270b2&host=huge-blue
The third review (codex gpt-6-astra xhigh at 9b77d6b) found the two-pass disambiguation still produced duplicates: rows (a, X), (b, X), (c, "X (a)"), (d, "a") left two rows called "a", because the fallback to the id did not check the ids against names no pass had touched. disambiguate() is now one walk in id order over a taken set. A row wants its name, or "name (id)" when another row wants the same name; if that is taken it gives up the name for its id, and that for "id (2)", "id (3)"... So every row leaves with a name no other row has, whatever acpmux calls its profiles. An empty catalog name is also no longer a name, so no row leaves blank. Tests: no_two_rows_leave_the_picker_with_the_same_name builds exactly that four-row shape out of claude, claude-sr, claude-vm ("Claude Code (claude)") and claude-box ("claude"), asserts every final name and that none repeats or is blank, and a nameless profile with an empty catalog name falls back to its id. Red without the source change (cargo test --test engine_offer: 7 passed, 2 failed), green with it (9 passed, 0 failed). Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a174e4-3868-7f51-8f3d-21b34ad270b2&host=huge-blue
…as a choice
Review finding (codex gpt-6-astra xhigh): `chief.engine.set` reads an empty
harness value, or the literal `default`, as "clear the choice" (the `set`
closure in engine_control). A profile acpmux actually calls `default` is a
legitimate harness, so the picker offered it as runnable; selecting it refuses
nothing and switches nothing, and when its family takes Fast, a set of
`{harness: "default", speed: "fast"}` is checked against the harness the Chief
already runs and can come back `invalid_speed`.
The rule this PR is about is that a row never offers what a set would refuse, so
such a row is now unavailable, with the reason naming what to do (rename the
profile in acpmux). The fix is in the offer, not in the sentinel: changing what
`default` means in a set would break every caller that clears a field with it.
Covered by an_offer_never_names_a_harness_effort_or_speed_a_set_would_refuse,
which also asserts the dead set: no refusal, and the harness does not change.
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a174e4-3868-7f51-8f3d-21b34ad270b2&host=huge-blue
|
|
|
Review (codex gpt-6-astra xhigh): SHIP at d7ea6df
Reviewer: Codex gpt-6-astra at xhigh on cmux16 ( Evidence it was given: on the fleet at this commit, The two non-blocking points I am not acting on here: the "restart the Chief" wording can |
|
|
|
Review carry: SHIP at dbbbd46 The receipt above ( The merge was taken to pick up #25180 (the Own tests at this head, on the fleet
The remaining reds are base reds, none of them this PR's
|
…engine-row One generated-file conflict, not hand-merged: the settings source stamp (schemas/settings-export/settings-schema.sources.sha256) is left at this branch's value; the fleet settings export rewrites it in the next commit. The two web/data/next-docs references are regenerated by web/scripts/export-next-docs.ts from the merged schema (count 495). Picks up #23945 and #25180. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-24ce2bb1cc47fb95fa20
|
Shipped in:
|
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Leo #89 item 3, "Chief backends: choose the engine": people pick the engine the Chief runs on (Claude Code through sr2, Codex, OpenCode, the other acpmux harnesses, UniiChat), each engine says what it supports, and a switch applies from the next turn.
chief.engine.set {harness}already does the switch: it saves engine.json and the next turn reads it. What was missing is a picker list that comes from acpmux. Today the app's list is the staticChiefBackend.official(claude, codex, unii).Summary
chief.engine.get(and the answer tochief.engine.set) now carriesoffered.harnesses[]. The brain reads acpmux's_acpmux/harnesseson each answer, so a harness installed or signed in since is listed at once. New fileNative/OptChat/optchat-chief/src/brain/harness_offer.rs;engine_control.rsadds the field.{"id": "opencode", "displayName": "OpenCode", "family": "opencode", "chiefFamily": "other", "kind": "acp", "capabilities": {"efforts": ["low", "high"], "speeds": ["default"], "resume": true, "permissionModes": true}, "unavailable": true, "unavailableReason": "acpmux's opencode is unavailable: opencode is not on PATH", "description": "...", "icon": "opencode", "login": "opencode auth login"}familyis acpmux's family (opencode, unii, ...).chiefFamilyis the layout the Chief gives the harness (claude, codex or other), and the speeds follow it.capabilities.effortsis the profile's declared list, else every effort a set accepts.speedsuses the same check a set runs (fastonly on codex). The profile's ownresume,permissionModesandfastpass through.unavailablereason is acpmux's own (a missing launcher) or the harness gate's refusal (a Claude ACP adapter that is not acpmux'sclaude-stdio), so the picker never offers a harness whose turn would be refused.displayNameis the profile's name, else the curated catalog's, else the id. When two profiles share one name (claudeandclaude-srare both "Claude Code"), each name gets its id in parentheses.ChiefEngineHarnessOffer(new filePackages/macOS/CmuxNext/Sources/CmuxNextDaemon/Chief/ChiefEngineHarnessOffer.swift) decodesoffered.harnessesfrom the whole answer. A brain that sends no list gives an empty one, a row the app cannot read is skipped, andpickerOrderputs the available engines first.Scope
This draft covers the wire and the decode model only. No UI changes, and none of the files #23883 touches (
ChiefBackend.swift,ChiefNames.swift,ChiefControlClient.swift,HomeChiefModelMenu.swift,HomeChiefSettingsView*).Remaining steps:
harnessestoChiefEngineReport.Offered(or decodeChiefEngineHarnessOffernext to it), then build the Chief Settings harness picker and New Chief frompickerOrderinstead ofChiefBackend.official, keeping the static list as the fallback for an older brain.unavailableReason, and offersloginas the fix.chief.engine.setstill refuses it withunknown_harness, because the set checks against the families the brain read at start. Either mark such a row unavailable ("restart the Chief") or let the set admit it live.Interpretation: I read "UniiChat-style tree memory" as acpmux's existing
uniiharness (UniiChat throughuniichat-agent). The Chief's own memory is already a tree, so no new memory backend is added here.uniiis offered like any other harness (familyunii, chief familyother), and the compactor stays on Claude.Testing
Native/OptChat/optchat-chief/tests/engine_offer.rsthe_engine_answer_offers_every_harness_with_what_it_supports, against a fake acpmux with claude-sr, claude, a refused claude-acp, codex, an unavailable opencode, unii and a terminal aider. Fleet stepcmux-tui-rust-check.sh optchat-chief, with no filter:Tests/CmuxNextDaemonTests/ChiefEngineHarnessOfferTests.swift. Fleet steppackage-test-lane.sh suite Packages/macOS/CmuxNext ChiefEngineHarnessOfferTests:ContextSection.cssmissing from build-agent-pane-web.sh; fixes in ci: the agent pane page ships the Inspector's ContextSection.css (base red since #23789) #23935 and cmux NEXT: fleet app builds pass the web step again (agent pane bundles ContextSection.css) #23939). Rerun after one of those fixes lands.scripts/cmux-next/prepush-gate.pyfound no new violations.Observed / Not run
Proof
No capture: draft, no UI yet
Changelog
none
🤖 Generated with Claude Code
Agent session: cmux16