Skip to content

Chief engine picker: chief.engine.get offers every acpmux harness with what it supports - #23945

Merged
teamleaderleo merged 12 commits into
feat-cmux-nextfrom
chief-engine-picker
Oct 26, 2026
Merged

teamleaderleo merged 12 commits into
feat-cmux-nextfrom
chief-engine-picker

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

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 static ChiefBackend.official (claude, codex, unii).

Summary

  • chief.engine.get (and the answer to chief.engine.set) now carries offered.harnesses[]. The brain reads acpmux's _acpmux/harnesses on each answer, so a harness installed or signed in since is listed at once. New file Native/OptChat/optchat-chief/src/brain/harness_offer.rs; engine_control.rs adds the field.
  • Each row:
    {"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"}
    • family is acpmux's family (opencode, unii, ...). chiefFamily is the layout the Chief gives the harness (claude, codex or other), and the speeds follow it.
    • capabilities.efforts is the profile's declared list, else every effort a set accepts. speeds uses the same check a set runs (fast only on codex). The profile's own resume, permissionModes and fast pass through.
    • The unavailable reason is acpmux's own (a missing launcher) or the harness gate's refusal (a Claude ACP adapter that is not acpmux's claude-stdio), so the picker never offers a harness whose turn would be refused.
    • displayName is the profile's name, else the curated catalog's, else the id. When two profiles share one name (claude and claude-sr are both "Claude Code"), each name gets its id in parentheses.
    • Terminal-only harnesses are left out, since they never run a Chief turn. Rows are ordered by id.
  • On the Mac, ChiefEngineHarnessOffer (new file Packages/macOS/CmuxNext/Sources/CmuxNextDaemon/Chief/ChiefEngineHarnessOffer.swift) decodes offered.harnesses from the whole answer. A brain that sends no list gives an empty one, a row the app cannot read is skipped, and pickerOrder puts 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:

  1. Picker UI, after cmux NEXT: Chief Settings as grouped glass cards, names not ids, a styled last turn #23883 lands: add harnesses to ChiefEngineReport.Offered (or decode ChiefEngineHarnessOffer next to it), then build the Chief Settings harness picker and New Chief from pickerOrder instead of ChiefBackend.official, keeping the static list as the fallback for an older brain.
  2. Capability badges: show each row's efforts, speeds (Fast), resume and permission modes. An unavailable row is greyed out, shows its unavailableReason, and offers login as the fix.
  3. Switch confirmation: picking another engine says it applies from the next turn (the running turn finishes on the old one) and what changes (model and effort reset to the new harness's own). Then show the brain's answer, or its typed refusal.
  4. Follow-up: a harness acpmux added after the Chief started is listed as available, but chief.engine.set still refuses it with unknown_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 unii harness (UniiChat through uniichat-agent). The Chief's own memory is already a tree, so no new memory backend is added here. unii is offered like any other harness (family unii, chief family other), and the compactor stays on Claude.

Testing

Observed / Not run

  • Observed: the fleet steps above.
  • Not run: the Swift suite (blocked by the base red above), a live brain against a real acpmux, and any app build. There is no UI in this draft.

Proof

No capture: draft, no UI yet

Changelog

none

🤖 Generated with Claude Code
Agent session: cmux16

teamleaderleo and others added 3 commits October 21, 2026 17:26
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
@teamleaderleo teamleaderleo added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 22, 2026
@teamleaderleo

teamleaderleo commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator Author

Owned by chief-fix-4 on huge-blue · session

@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Review 3c54c4a: HQ feed card · gallery before/after runs · checks

App of this head (app.zip, kept 1 day)

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr23945 -f ref=chief-engine-picker -f platform=macos -f backend_mode=local

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

@blacksmith-sh

blacksmith-sh Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Found 5 test failures on Blacksmith runners:

Failures

Test View Logs
nightly.yml prepare-nightly-variant: 'Package prepared app for signing' is not in list/
nightly.yml prepare-nightly-variant: 'Package prepared app for signing' is not in list
View Logs
test_secret_free_prepare_fallback_does_not_requeue_a_retry_on_the_side_pool (main.N
ightlyOffBlacksmithTests.test_secret_free_prepare_fallback_does_not_requeue_a_retry_on_
the_side_pool)/
test_secret_free_prepare_fallback_does_not_requeue_a_retry_on_the_side_pool (main.N
ightlyOffBlacksmithTests.test_secret_free_prepare_fallback_does_not_requeue_a_retry_on_
the_side_pool)
View Logs
test_the_nightly_is_covered (main.NightlyStepCoverageTests.test_the_nightly_is_cove
red)/
test_the_nightly_is_covered (main.NightlyStepCoverageTests.test_the_nightly_is_cove
red)
View Logs
test_the_repository_highlights_validate (main.DigestTests.test_the_repository_highl
ights_validate)/
test_the_repository_highlights_validate (main.DigestTests.test_the_repository_highl
ights_validate)
View Logs
the architecture gate covers the helper's executable/
the architecture gate covers the helper's executable
View Logs

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

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Bead cx-o8dhi created for this PR by the cmux coordinator.

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
@teamleaderleo
teamleaderleo marked this pull request as ready for review October 25, 2026 21:17
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
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

cmux-next guards / nightly-guards / workflow-guard-tests / release-notary is red on 15 of 18 fresh feat-cmux-next PR heads in the last 2:00:00, so it is most likely a base red, not this PR's. The base-reds lane has it; a base merge picks up the fix once it lands.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

cmux-next / cmux-next checks (god files, concurrency, crash safety, l10n) › Script tests (pin-cmux-tui, embed-cef, ensure-cef store) is red on 12 of 15 fresh feat-cmux-next PR heads in the last 2:00:00, so it is most likely a base red, not this PR's. The base-reds lane has it; a base merge picks up the fix once it lands.

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
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

cmux-next fast gates / post cmux-next/fast-gates is red on 12 of 17 fresh feat-cmux-next PR heads in the last 2:00:00, so it is most likely a base red, not this PR's. The base-reds lane has it; a base merge picks up the fix once it lands.

… 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
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

high-signal review link / link is red on 12 of 13 fresh feat-cmux-next PR heads in the last 2:00:00, so it is most likely a base red, not this PR's. The base-reds lane has it; a base merge picks up the fix once it lands.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6-astra xhigh): SHIP at d7ea6df

  • harness_offer.rs:133: Correct predicate: identical trimming and case-sensitive comparison. Default/DEFAULT remain names; default, default , and whitespace-only IDs become unavailable. No introduced panic, borrow issue, or interaction with display-name disambiguation.
  • harness_offer.rs:169: Non-blocking diagnostic limitation: “restart” can hide the permanent rename requirement. Availability remains correctly false, but restarting alone cannot make default selectable.
  • harness_offer.rs:197,213: Efforts exclude default; speeds legitimately include it as the accepted reset operation. The guard affects harness IDs only. Compactor/classifier harnesses share that sentinel rule; this row does not enumerate model choices.
  • engine_offer.rs:521: The availability assertion fails without the fix because the fixture is admitted and present in startup families. The subsequent no-error/unchanged assertion documents the preserved clearing contract, rather than requiring selection of the unreachable profile.
  • harness_offer.rs:131: Non-blocking documentation correction: clearing an existing override can restore a different startup harness; it does not universally keep the running harness unchanged.
  • harness_offer.rs:227: Verification gap: the supplied packet proves availability metadata, not picker selection enforcement. No picker handler is supplied, so disabled selection remains unverified; this delta itself adds no setter rejection.

Reviewer: Codex gpt-6-astra at xhigh on cmux16 (codex exec --sandbox read-only), a
different model from the author. It was given the fix commit's diff, the full current
harness_offer.rs, the set path of engine_control.rs and the test fixture, and was
told to judge the fix for the one FIX FIRST finding of the previous round
(89a8af8: a profile acpmux calls default was offered as a choice a set reads as
"clear the choice").

Evidence it was given: on the fleet at this commit,
scripts/ci/cmux-tui-rust-check.sh optchat-chief ran the whole crate.
tests/engine_offer.rs: 9 passed, 0 failed. The one crate-wide failure was
acpmux_peer_moved::moved_peer_the_kernel_signature_of_a_peer_whose_executable_is_gone_is_read_and_checked
("its CMS signature does not verify"), a signing-fixture failure that has nothing to do
with this change.

The two non-blocking points I am not acting on here: the "restart the Chief" wording can
read as if a restart would make a default-named profile selectable (it names the rename
in the same sentence), and the picker's own refusal of an unavailable row is not in this
PR — the Swift side has isAvailable/pickerOrder and no picker view consumes it yet, so
there is nothing here to enforce it in. Both belong with the picker UI.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

cmux-next / cmux-next checks (god files, concurrency, crash safety, l10n) › Package dependency lists sorted is red on 6 of 9 fresh feat-cmux-next PR heads in the last 2:00:00, so it is most likely a base red, not this PR's. The base-reds lane has it; a base merge picks up the fix once it lands.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review carry: SHIP at dbbbd46

The receipt above (Review (codex gpt-6-astra xhigh): SHIP at d7ea6dfb8911dcace9137efe92b60bd20fd361b3) carries to this head. dbbbd46f23 is d7ea6dfb89 plus one clean merge of origin/feat-cmux-next — no conflicts, no hand-written merge content — and the PR's own patch is byte-identical at the two heads:

git diff origin/feat-cmux-next...dbbbd46f23   # 6 files
git diff $(git merge-base d7ea6dfb89 origin/feat-cmux-next)...d7ea6dfb89
# the two diffs are identical, byte for byte

The merge was taken to pick up #25180 (the web/data/next-docs generated-files baseline) and #25272 (the release-notary highlight fixtures).

Own tests at this head, on the fleet

  • ChiefEngineHarnessOfferTests (the Swift side of this PR): passed, 10s — cmux-ci step 68ca28a8bb9d96ed63f71605 on aws-m4pro-3, scripts/ci/package-test-lane.sh suite Packages/macOS/CmuxNext ChiefEngineHarnessOfferTests at dbbbd46f23.
  • cargo test --locked --manifest-path Native/OptChat/optchat-chief/Cargo.toml --test engine_offer (the Rust side): 9 passed, 0 failed on the 32-vCPU Linux fast-check lane at dbbbd46f23.

The remaining reds are base reds, none of them this PR's

  • rust workspaces / Native/OptChat/optchat-chief — memory_sync_state::tests::a_running_host_is_refused_without_stop_host panicked on the HostLock::take().unwrap(). It is in a file this PR does not touch, rust workspaces is green on the base tip (714af0ca098a, run 40271692320), and the same test passes at this head on the Linux lane (1 passed; 0 failed). A flake in a test that probes a real lock's pid and mtime; it cleared on the rerun here too.
  • cmux-next checks — scripts/cmux-next/tests/release-compile-derived-data.test.sh (1 of 112), red at the base tip.
  • cmux-next fast gates, high-signal review link, Familiar intent review warning — base reds; base-red-watch posted its findings for the first two on this PR above.
  • cmux-next daemon tests, cmux-next generated files — both failed in Set up runner, before any of this PR's code ran: runner infrastructure. The daemon suite's own content is covered by the fleet run above; cmux-next generated files is the ActionContractTests base red that #25220 fixes (cmux open-remote's one-word CLI verb), red on every PR that runs the job.

@teamleaderleo
teamleaderleo merged commit d024fca into feat-cmux-next Oct 26, 2026
143 of 156 checks passed
@teamleaderleo
teamleaderleo deleted the chief-engine-picker branch October 26, 2026 06:12
teamleaderleo added a commit that referenced this pull request Oct 26, 2026
…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
@github-actions

github-actions Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

This branch was previously deployed

1 inactive deployment
ci-routing-agent — dbbbd46f Deployed Oct 26, 2026 by teamleaderleo via namespace autofix #3027
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant