Repository navigation
acpmux: a session an agent starts inherits its starter's policy and mode, never wider - #25155
Conversation
… and mode (red) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16c8c-54ec-71b2-a0fa-125d1404dd72&host=big-red
…ode, never wider A start with no policy and no mode from a proven agent session (its caller token: acpmux new|run from its shell, a local dispatch) now gets that session's own policy (or a narrower family/preset default) and its mode; on another family the same level (full access, asking, or the harness's own). Sub-sessions of another family now map the parent's level too, instead of the person's start level (under a full-access parent) or no mode at all. A start with no proven caller is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16c8c-54ec-71b2-a0fa-125d1404dd72&host=big-red
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16c8c-54ec-71b2-a0fa-125d1404dd72&host=big-red
… is now, clamp a wider family default, leave remote-origin starters out Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16c8c-54ec-71b2-a0fa-125d1404dd72&host=big-red
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16c8c-54ec-71b2-a0fa-125d1404dd72&host=big-red
… child to it (red) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16c8c-54ec-71b2-a0fa-125d1404dd72&host=big-red
…r's as it is when the child has started Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16c8c-54ec-71b2-a0fa-125d1404dd72&host=big-red
…rness Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16c8c-54ec-71b2-a0fa-125d1404dd72&host=big-red
…'s stack Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16c8c-54ec-71b2-a0fa-125d1404dd72&host=big-red
|
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)
web and size checks: green |
|
Owned by hb-acpmux on big-red · session |
|
Bead |
|
Found 14 test failures on Blacksmith runners: Failures
...and 4 more test failures. View all on Blacksmith
|
|
|
…to it (red) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16c8c-54ec-71b2-a0fa-125d1404dd72&host=big-red
|
|
…ound, and refuses one that keeps changing Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16c8c-54ec-71b2-a0fa-125d1404dd72&host=big-red
…agent caller is one binding Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a16c8c-54ec-71b2-a0fa-125d1404dd72&host=big-red
lane_reports test: the stand-in harness variants are of the starter's family, so a lane a fake parent starts inherits its mode (base #25155 refuses a start that would ask less). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a175c0-3004-7a40-8069-4149d94915ab&host=huge-blue
The merge dropped its only declaration with the saved-pick code; #25155's inherit path still skips a toggle mode such as plan. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a175da-93d2-7310-8727-b20e1d89afe5&host=huge-blue
… a running chat (#24021) * Test: a mode picked in a chat does not become the new-chat default Leo 2026-10-21: "i think starting in full access is ok cuz the default for new chats != whatever u just temp set in an existing running chat"; "u can set default in other ways". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d18-a2fb-7ac0-84d5-432ffea53b8b&host=Air-Blue * New chats start at the default, not the last mode picked in a running chat A pick applies to its own chat. Settings, MDM/team values and config.json defaultPermissionMode still set the default. A leftover lastPermissionMode from an older build is ignored. Leo-ask: ask-16f0b7197f Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d18-a2fb-7ac0-84d5-432ffea53b8b&host=Air-Blue * Regenerate the settings exports for the per-chat mode help text The help text change in AgentDefaultsSettingsSchema.swift and its catalog was never exported, so the defaults stamp failed. These are the files the PR autofix regenerated on the Mac for this head (its patch step came out empty, so it pushed nothing); every stamp entry matches the head sources. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * acpmux: keep TOGGLE_MODES for the sub-session inherit path base added The merge dropped its only declaration with the saved-pick code; #25155's inherit path still skips a toggle mode such as plan. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a175da-93d2-7310-8727-b20e1d89afe5&host=huge-blue * Regenerate the settings stamp and docs settings reference The stamp comes from the PR autofix patch (run 40256227863), applied with pr-autofix-apply.sh; the docs reference from export-next-docs.ts --settings, which also picks up base's agentPane.display.environment.placement row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a175da-93d2-7310-8727-b20e1d89afe5&host=huge-blue --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Leo, word for word (2026-10-25): "Also, like, why would you, like, it shouldn't sub-agents just inherit the bypass permission situation? I don't know. I feel like it should. Maybe is that Claude, or is that us doing it wrong, or doing it kind of strangely?"
Is it Claude or us?
Partly each. For the prompt Leo saw, mostly Claude Code's own guard plus our default policy. This PR does not stop that exact prompt.
cosmic-gray/claude-sr2/whats-new-video(a sub-session ofcosmic-gray/claude-sr2) ran inbypassPermissions, the same as its parent.settle_subsessioncopies a same-family parent's mode (hub/subsession_guard.rs).can_use_tool, sent while inbypassPermissions:"decision_reason": "Dangerous rm operation on statically-unresolvable target: /private/tmp/claude-501/.../scratchpad/f/*", "classifier_approvable": false, forcd ~/scratch/whats-new-video/reel && rm f/*; ffmpeg ... sheet.jpg. The//Users/leoli/scratch/...rule in the card is Claude Code's own absolute-path syntax, from itspermission_suggestions.ask.handle_permission_formaps that to no auto-answer (hub/permissions.rs:503,PermissionPolicy::Ask => None). The parent was also null/ask, so it would have been asked the same thing. The app's "Full access" sets only the harness mode, never the acpmux policy.acpmux new|runfrom its shell, or with a localdispatch, did not inherit the agent's level.keeps_harness_mode(hub/start_gate.rs) gave it the person's start level, or the harness's own mode under an asking caller. It never got the caller's policy:NewRequest.policywas only the request's value or the family default (hub/lifecycle.rs,policy.or(defaults.policy)). A sub-session of another family got no mode at all under a parent that was not in full access (settle_subsession,else { None }).approve-all. Overriding the harness's own destructive-operation guard is a security decision for Leo; the coordinator is raising it.Claude Code's own Task subagents already run in the parent's permission mode. Our launcher's children now do too.
Behaviour now, per starter
A session an agent starts with
acpmux new|runfrom its shell, a local_acpmux/dispatch, orstart_session/acpmux new --parent. The starter is the session proven by its caller token (Conn::caller). Each field the start leaves out is inherited on its own.approve-allapprove-allask)ask, as the starter runs)approve-editsapprove-editspolicy_mode.rs)ask(own), or an asking modeask(own), or nullremote_origin)Hub::hold_to_starter). An inherited policy laxer than the starter's live policy is narrowed to it. The starter is read again after each round: one that changed meanwhile is held to again, and after 4 rounds of change the start is refused (start.starter_changing). The harness is held byHub::hold_mode, the harness half ofhold_subsession, split out unchanged: it is moved to an asking mode, or the start is refused and the child closed.planis not copied. Full access is not copied whilesessions.allowFullAccessis off, andforward_asrefuses that switch anyway.--modethat is wider is refused. This was already enforced:start_gate.rsstart_mode_checkreturnspermission.person_requiredunless the starter runs that exact mode. Tested.--policythat is wider is NOT clamped (open gap). An agent caller can still pass--policy approve-allfrom anaskstarter, becausesession/newwith a policy other than ask or deny-all has been open to the unix socket since before the mode gate (hub/person.rs:218). Measured on 2026-10-25 withacpmux ls --json: about 1,300 live depth-0 sessions runapprove-all(472 on huge-blue, 362 on cmux16, 158 local, 153 on cosmic-gray, plus the GCP lanes). Many were started that way by coordinators whose own policy is null. Clamping would move them toaskand send their asks to Leo, so it is left to a separate decision.remote_guard's rules are unchanged (acpmux: a paired machine's Full-access setting (off by default) lets it drive a remote chat in Full access #25038, acpmux: a refused remote new says why and names the command that works #25049 read): a Web or peer connection has no caller, and a remote-origin chat is never a starter.Changes
hub/start_mode.rs: the "Inherit" rule (module docs),start_mode_for,inherited_mode,inherited_policy,own_policy, andinherited_full_access_is_cmux_only_if_theirs. An inherited full access counts as cmux's own (trust_wait.rs) only when the starter's does.hub/lifecycle.rs:NewRequest.starterpicks the policy and the start mode, thenhold_to_starterchecks against the live starter. Both steps areBox::pinned sonew_sessionfits a debug build's 2 MiB stack.hub/start_gate.rs:starter_of;keeps_harness_modedefers to inheritance for a starter.server/requests.rs:session/newpasses the proven caller as the starter. A localdispatchgoes through the same path.hub/subsession_guard.rs: another family's sub-session uses the parent's level (start_mode_for) instead of the person's start level or none;hold_modesplit out ofhold_subsession.skills/acpmux/SKILL.md: the rule, and why some requests still ask in full access.tests/person_auto_start.rs: an asking caller's child now starts in the caller's own asking mode (strict) instead of the harness's (normal).Tests (red, then green)
tests/inherit_start.rs, on the fleet (cmux-ci test).left: "approve-all", right: "ask"); a starter that narrows during the hold holds the child to it (red at 9a1883f, same values;FAKE_SET_MODE_DELAY_MSis new infake_agent.py).approve-allagent's child runsask: rm f/*; ffmpeg ...and getschose yesfrom the policy, with no person.person_auto_start,subsessions,subsessions_safety,subsessions_delivery,subsessions_live,full_access_subsessions,start_mode,subsession_trust_inheritandhub_integration(401) pass.cargo fmt --checkandclippy -D warningsare clean.daemon_chat_open::a_long_chat_opens_on_its_newest_page_and_scrolls_to_its_first_messagefails the same way on base.session_pool::pooled_sessions_end_with_the_daemon_and_after_a_crashis also red on base in the full suite, and flaky on its own (1 of 2 runs failed on this branch).Review
Codex gpt-6-astra at xhigh (
hb-inherit-mode-review, its own read-only checkout). The verdicts were BLOCK, BLOCK, BLOCK, SHIP at 33999af, then SHIP at the final head after a line-neutralserver/requests.rschange (kept at its base size for the god-file ceiling), with "Reviewed head: 04cc4de". Each finding was fixed with a test: a cross-family child under an asking starter, a remote-origin starter, a wider family default, and two starter-narrows-mid-start races. One finding was not taken: "inherit only when both fields are absent". The spec is per field, and an explicit policy is the open gap above.CI reds that are not this PR's:
Script tests (release-compile-derived-data.test.sh)is red on unrelated PRs too (runs onrail-opus-rail-minimap-settingandcmux/stop-says-stopping); release-notary and Guard status are base reds; app compile failed at "Set up runner".No new timers or observers.
hold_to_starterloops at most 4 rounds, then refuses the start.No capture: daemon behaviour only, nothing visible changed.
Agent session: big-red
🤖 Generated with Claude Code