Chief: approvals as an Allow/Deny card in Home; no more stuck turns
Summary
The Chief got stuck on any approval asked from Home. It asked "Reply allow or deny" as text, but acpmux accepts an allow only with the person's proof, which a relayed chat reply never carries. The refused allow was then dropped silently, so the turn waited forever and later messages (including "stop") had nothing to deny. The same refusal also hung turns on the Chief's own read-only memory tools (zoom, date, version), which the Chief tried to auto-allow.
This PR makes the approval a real Allow / Deny card in Home and stops every path that left a turn waiting with no way out.
Chief (Native/OptChat/optchat-chief)
- Asks each approval as a
questioncard (harnesschief, session, permission, options Allow / Deny). A chatdenystill works. - A card answer settles the approval. The Mac app answers acpmux itself first, so the Chief only confirms it is settled. If acpmux still holds the request (an Allow from a paired device or another person, without proof), the approval stays and the Chief says why.
- A refused answer never drops the approval: it stays, the person hears why, and
denyor a newer message still settles it (the original hang). - A card settled another way (chat reply, newer message, turn end) is moved to cancelled.
- A refused auto-allow of a memory tool is denied at once, so the turn goes on.
- The card states the real reason the turn asks (paired device, another participant, Home terminal text). It used to always say "paired device".
Mac (Packages/macOS/CmuxNext, Packages/Shared/CmuxMessagesLab)
- Home draws a Chief approval card: the prompt, then Allow (accent) and Deny buttons.
- A click answers the Chief's acpmux as the person through the same proof the agent pane uses: new
AcpmuxApprovalAnswerer.answer(session:permission:allow:), which enrolls the person key first so Allow works with no Chief tab open. Then it answers the card. - A refused Allow shows its error under the buttons and they work again. Answered and cancelled cards collapse to one line ("Allowed", "Denied", "Allowed on ", "Skipped").
- Vendored MessagesLab
CustomRows.swiftandWindowView.swiftpass the click point to a drawn row's tap handler, so the card can tell Allow from Deny. The patches are regenerated and listed in the README patch table.
No acpmux rule is loosened: an allow still needs the person's proof.
Testing
Regression, committed red before the fix (dbfd815e): cargo test --locked --test remote_policy an_allow_acpmux_refuses_keeps_the_approval_and_says_so in Native/OptChat/optchat-chief. It fails on the base behavior (the approval is dropped, no reply, the newer message denies nothing) and passes on this head.
Observed / Not run
Observed:
- rbx, optchat-chief at this head's tree:
cargo fmt --checkandcargo clippy --locked --all-targets -- -D warnings: pass.cargo test --locked --test remote_policy: 18 passed, 1 failed (base red, below). New tests: the card's fields, cutting long input, Deny on the card, Allow settled by the app, Allow acpmux refuses keeps the approval, chat deny cancels the card, memory-tool refusal is denied with nothing asked, the card's reason.- Full
cargo test --no-fail-fast: every other suite passes except the base reds below.claude_shimfailed once and passed 3/3 on rerun (flake).
- Base reds, failing identically on clean
origin/feat-cmux-next2cd6ae16 on rbx (CI skips the optchat-chief job on feat-cmux-next, so they had not surfaced):remote_policy::a_non_owner_turn_asks_and_only_the_owner_may_approve_it,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. - Fleet, on a commit whose
Packages/tree is identical to this head's:- MessagesLab step
7edd6239ea8fe7f433232955: passed, including 9 newHomeApprovalCardTests(mapping, hit-testing, decision path with a fake decider). - CmuxNext step
aa3ccbc0dcfd1e2d12879e7c: passed (package builds for tests;ApprovalBannerTests,AgentPaneRelayedAllowTests).
- MessagesLab step
scripts/cmux-next/prepush-gate.py: no new violations.- Diagnosis on a live Chief: the 2026-10-20 host log shows the refused allow (
not delivered: … accepted only from the cmux app) and the refused memory-tool allow, each followed by a turn left waiting.
Not run yet:
- A tagged build with a real Chief: clicking Allow and Deny on the card end to end, plus the visual review captures.
AcpmuxApprovalAnswerer.answer(session:permission:allow:)andChiefApprovalDecideragainst a live acpmux (no unit tests; they need a daemon).- Cards in Chief side conversations: side conversations still ignore message updates, so a card answered there does nothing. Chat replies work there.
Changelog
Fixed: The Chief no longer gets stuck waiting on an approval in Home; approvals show as an Allow / Deny card.
🤖 Generated with Claude Code
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Fixes the Chief getting stuck on approvals from Home. Approvals are now an Allow/Deny card that answers acpmux as the person; loosening an approval gate is only the owner's to do.
- A Home click answers through the same proof the agent pane uses; if acpmux still refuses, the approval stays, the person hears why, and a later deny or newer message settles it.
- The card shows the full question (chip, headline, reason, arguments); only the owner settles it, and a refused answer re-posts a fresh card.
- A refused auto-allow of a memory tool is denied at once, and an approval acpmux no longer holds is dropped as settled.
New Features
approvals.autoApproveAll(default off): no turn asks, whatever started it; the brain refuses to loosen any gate during a remote, participant or asking turn.- Owner-only
chief.settings.get/chief.settings.setforward to the brain's settings tool, and the Chief settings card gains an Approvals section. - A loosening setting is forwarded only from the verified app connection, never from an agent-bound connection or an unknown peer; the brain trusts the owner daemon socket it recorded, and tightening a gate also stops subagents.