Skip to content

Chief: your own turns never ask; approvals are opt-in, Stop ends the turn, and an approval is a card - #22640

Merged
teamleaderleo merged 5 commits into
feat-cmux-nextfrom
fix/chief-approvals-owner-turns
Oct 20, 2026
Merged

teamleaderleo merged 5 commits into
feat-cmux-nextfrom
fix/chief-approvals-owner-turns

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 20, 2026 •

Copy link
Copy Markdown
Collaborator

Since 10-20, every Chief turn waits for approval before each local effect. That includes the owner's own cmux chief -p on the same Mac and Chief's own tell and send. Four problems stack on top of that:

  • remote.autoApprove doesn't lift the gate.
  • cmux chief stop doesn't end a turn whose tool call is waiting.
  • The model re-asks for a denied tool in a loop (turns 146 and 167).
  • The approval shows up as a raw JSON bubble.

The CLI was not the cause. cmux chief -p sends as user_local over the owner socket, which is local origin. The cause is e68aa1b: it gates every turn that reads the Home view, and the host reads Home on every turn (host.rs always sets home_view). The bubble blamed "a paired device" only because that text is hard-coded. a4bbe5d had also made asking the default for paired devices.

What changes

  • Gates are opt-in. Two new per-Chief settings, both off by default:

    • remote.ask ("Ask before acting on requests from other devices")
    • home.ask ("Ask before acting on what Home shows")

    With both off, the owner's turns from this Mac, from the CLI and from their paired phone run like local turns. remote.autoApprove: true still wins over remote.ask. A turn where another participant wrote always asks, which covers the shared-Chief case. The host refuses to loosen a gate during remote-origin work.

  • Chief's own tools never wait. That covers send, reply, react, stop, sleep, profile and spawn. A child spawned in a gated turn still gets the ask floor. tell and control also run without asking when they carry no prompt. In a gated turn, a tell, or a control that carries a message or an answer, still asks unless the target subagent runs with ask itself. That way a gated turn can't pass a command through an approve-all subagent.

  • Stop means stop. chief.stop cancels the session even while a tool call is waiting for approval (Interrupt::request_hard). Every later request in that turn is denied at once. If the model repeats a request that was already denied, it is denied again without a new card; after three repeats the turn is stopped. A newer message no longer denies a pending approval.

  • A readable approval card. The approval is now a conversation question part. It shows "Allow Chief to run `rm -rf target`?", a reason line ("Started from Leo's iPhone: …"), the arguments pretty-printed as a monospace preview, and Allow once / Deny buttons. The owner answers with question.answer (only the owner's answer counts) or by typing allow or deny. If the owner refuses the part, the same two lines go out as plain text. No JSON appears in the chat.

Who added the gate

Lawrence added the paired-device gate on 2026-10-05 (0654733, default ask). He made auto-approve the default on 10-06 (8f1b5b9: "i dont want stuff to require my approval"). On 10-20 his security hardening made asking the default again (a4bbe5d) and gated every turn that reads Home (e68aa1b), because terminal titles, screens and URLs are text an attacker can write. All three went straight to feat-cmux-next; none came through a PR.

Takes back a4bbe5d and e68aa1b (both pushed directly on 10-20): together they put an approval in front of every effect of every owner turn, Chief's own messages included. Leo decided on 10-20 that the owner's own devices run like the Mac by default. Both protections stay available as opt-in settings.

Security analysis (risk:high)

  • Prompt injection through Home text is now an accepted default risk. With home.ask off, text in the Home block reaches every turn and runs with the owner's policy, as it did before 10-20. home.ask on restores the 10-20 behavior.
  • A stolen paired phone can act without approval until it is revoked. This matches the 10-06 default. remote.ask on puts approvals back.
  • Other participants still always ask, and only the owner approves. That holds for both the text answer and the card. A card answer is checked against the respondent the owner stamps, never against the request.
  • The newly allowed tools stay inside the conversation or the ask floor:
    • send, reply and react post only to the conversation that asked. send can attach local files, but in a non-owner turn the participant already reads that conversation (residual risk).
    • Children from spawn get the ask floor.
    • tell and control with a prompt still ask for approve-all targets.
  • No relay command, allowlist entry or socket method is added.

Tests

Command: cmux-ci run --class isolated --script scripts/ci/cmux-tui-rust-check.sh --arg optchat-chief --ref <sha> (optchat-chief tests, then clippy -D warnings, then fmt).

Red commit d5e3840 (new tests only, on base): 8 of the 10 new approval_gates tests fail. The two guard tests (home.ask on, remote.autoApprove wins) already pass on base.

The base also fails four existing tests:

  • crash::a_pending_turn_and_its_items_survive_a_restart_in_the_database
  • daemon_workspaces::a_subagent_workspace_is_located_by_its_public_id_and_session
  • turn_seal_binding::a_subagent_cannot_reach_owner_controls_on_the_shared_socket
  • remote_policy::a_non_owner_turn_asks_and_only_the_owner_may_approve_it

Head 30b89d3:

  • All of approval_gates (10) and remote_policy (9) pass.
  • clippy and fmt pass.
  • The same three base reds in crash, daemon_workspaces and turn_seal_binding remain. This PR doesn't touch them.

What tests/approval_gates.rs checks:

  • (a) An owner CLI turn with Home runs approve-all. home.ask turns the Home gate on. By default a paired-device turn runs like a local one.
  • (b) remote.ask gates a device turn, and remote.autoApprove wins over it. The gate can't be loosened during remote work.
  • (c) Chief's tools are allowed at once. A tell to an approve-all subagent still asks.
  • (d) Stop cancels a turn whose call is waiting, and later requests are denied at once. A denied request isn't asked twice, and repeats stop the turn.
  • (e) A newer message leaves a pending card alone. The test checks the card's line, reason, arguments and buttons, and that a question.answer allows the call.

tests/remote_policy.rs now runs with remote.ask on, and its default-case test is flipped.

Observed / Not run

  • Observed: the fleet runs above.
  • Not run: the Mac app and webview card. Mac Home still draws a question part as its prompt plus "1. Allow once / 2. Deny" text. MessagesLabHome/Cmux/HomeMapping.swift falls back to text, and AgentQuestionCardView isn't wired to the custom-row seam yet. The Claude-desktop-style card with buttons, the ⌘↵ hint and the answered receipt is a follow-up UI PR. This PR only emits the data that card needs.
  • Not run: a live repro. I didn't reproduce with an isolated --chief-home on a nightly build. Leo's Chief stays stopped.
  • Not run: other brains. The cloud brains (backend/) keep their own policy, and the shared corpus remote_auto_approve cases are unchanged.

Changelog

  • Fixed: Chief no longer asks for approval on your own turns (the cmux chief CLI, Home, your paired phone). Asking on requests from other devices or on Home text is now opt-in, Stop ends a turn that is waiting on an approval, and an approval shows as a short card instead of raw JSON.

🤖 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

Chief turns no longer wait for approval unless you opt in: your own turns from the CLI, from Home, and from your paired phone run like local turns by default, with remote.ask and home.ask off. chief.stop now ends a turn even while a tool call waits, a denied request isn't asked twice, and approvals appear as a card with Allow once / Deny instead of raw JSON.

Key changes

  • Two new per-Chief settings, both off by default: remote.ask gates paired-device turns, home.ask gates turns that read Home text; remote.autoApprove still wins, and another participant's turn always asks.
  • The Chief's own tools (send without attachments, reply, react, stop, sleep, profile reads, spawn, and tell/control without a prompt) never wait; in a gated turn, send with attachments, profile writes, and tell/control with a prompt still ask unless the target subagent runs with ask itself.
  • chief.stop cancels the session even while a tool call waits, and a request denied once is denied again without a new card; three repeats stop the turn with one line saying so.
  • Approvals are a question card with a reason line, pretty-printed arguments, and Allow once / Deny; only the owner's answer counts, a newer message never answers a pending card, and a card answered in a side conversation settles the approval posted there.
  • No gate can be loosened while a turn asks.
  • The Mac app and webview render the card as text for now; the button UI is a follow-up.
  • Security tradeoff: with gates off, Home text and a stolen paired phone act without approval; the 10-20 hardening is now opt-in.

Written for commit 27a018c. Summary will update on new commits.

View guided diff Turn on auto-fix

…how a card (red)

The owner's own turns (a cmux chief CLI message, a turn that reads Home, by
default a paired phone) must run without approvals; in a gated turn the
Chief's own tools never wait, a stop ends the turn, a denied request is not
asked again, a newer message leaves a pending approval alone, and the
approval is a readable card.

Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=e8f63fd9-94e8-4878-bcd4-704659aba1f2&host=cosmic-gray
…nce and ask with a card

Every Chief turn waited for approvals since 10-20: the host reads the Home view
on every turn and e68aa1b gated every such turn, so a `cmux chief -p`
message from the same Mac (user_local over the owner socket, local origin)
asked for each effect, Chief's own tell and send included, and
remote.autoApprove could not lift it. The text blamed a paired device.

- Home view and paired-device gates are opt-in settings, default off:
  home.ask ("Ask before acting on what Home shows") and remote.ask ("Ask
  before acting on requests from other devices"); remote.autoApprove true
  still wins. Another participant's turn always asks.
- The Chief's own tools (send, reply, react, stop, sleep, profile, spawn,
  tell/control without a prompt) never wait; tell/control with a prompt to
  an approve-all subagent still asks in a gated turn.
- chief.stop cancels the session even while a call waits for its approval
  and denies every later request of the turn at once. A request denied once
  is denied again without a new card; three repeats stop the turn.
- A newer message no longer denies a pending approval.
- The approval is a question card: "Allow Chief to run `rm -rf target`?",
  the reason and pretty-printed arguments, Allow once and Deny, answered by
  question.answer (owner only) or allow/deny text; two plain lines when the
  owner refuses the part.

Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=e8f63fd9-94e8-4878-bcd4-704659aba1f2&host=cosmic-gray
@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

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

Review 27a018c: 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=pr22640 -f ref=fix/chief-approvals-owner-turns -f platform=macos -f backend_mode=local

web and size checks: green

@blacksmith-sh

This comment has been minimized.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

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

…nt controls; side cards answer

Review fixes for the approval gates:
- In a turn that asks, send with attachments, profile writes or undo, and
  control actions other than status, interrupt without a message and answer
  deny ask, unless they target an ask-policy subagent.
- A card answered in a side conversation settles its approval there; a card
  answers only in the conversation it was posted in.
- Another participant's answer closes the card, so the owner gets a fresh one.
- A turn stopped for repeating a denied request posts one line saying so.
- No gate can be loosened while a turn asks.
- The card's line marks line breaks and cuts, its code span survives
  backticks, and the text form carries the arguments.

Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=e8f63fd9-94e8-4878-bcd4-704659aba1f2&host=cosmic-gray
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

cmux-next guards / defaults-snapshot is red on 13 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
teamleaderleo merged commit 63d1280 into feat-cmux-next Oct 20, 2026
@teamleaderleo
teamleaderleo deleted the fix/chief-approvals-owner-turns branch October 20, 2026 22:55
austinywang added a commit that referenced this pull request Oct 21, 2026
#22640 made the owner's turns never ask, the Chief's own tools never
wait, Stop end a waiting turn, and the approval a question card. On its
code this keeps only what it does not cover:

- An answer acpmux refuses (an allow without the person's proof: a chat
  reply, a card answered on another device) keeps the approval with a
  fresh card and says why; one acpmux no longer holds is settled, and a
  chat answer moves on to the next approval.
- A refused auto-allow of the Chief's own tool in an ask turn asks the
  person with a card instead of leaving the turn waiting.
- The Mac Home card (Allow / Deny, answered as the person) renders
  #22640's card as is: same item and option ids.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant