Skip to content

acpmux: under MDM's forced off, agents already in full access end (#23322 b) - #25539

Closed
teamleaderleo wants to merge 34 commits into
feat-cmux-nextfrom
lane/hb-stale-trust-23322-b
Closed

teamleaderleo wants to merge 34 commits into
feat-cmux-nextfrom
lane/hb-stale-trust-23322-b

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

What (part b of #23322)

This is part b of #23322 (sessions.allowFullAccess under MDM). It was stacked on #23322 (a1); a1 has landed on feat-cmux-next (33db003), and b is rebased onto it, so its base is feat-cmux-next. The rebase kept both sides of the two conflicts: a1's refuse_started_in_full_access next to b's full_access_turned_off(changed), and both sets of test profiles in full_access_managed.

It builds the decision on #23322, quoted word for word:

"#23322 (sessions.allowFullAccess under MDM), decided: an MDM-managed value is an upper bound the user can't override. If MDM sets allowFullAccess=false, Full access is unavailable and the mode menu says "Managed by your organization". If MDM is unset, nothing changes. This only narrows access, so it needs no call from Leo."

"Full access is unavailable" also covers agents already running in it. Under MDM's off, an agent ends wherever runs_full_access (a1's one predicate) turns true for it. end_if_runs_full_access always runs under the session's spawn lock. It ends the live child and any host a restart left, and it nulls a full-access option value, so the next start doesn't replay it. The places it runs:

  • The push that turns it off. full_access_turned_off calls end_every_managed_full_access on every session. A turn in progress ends with it.
  • The next start. ensure_child checks first. child_for never hands out such an agent, and the idle-reuse path skips it.
  • A mode or option write. This includes the harness's own update: web_control wakes the hub's background task (idle.rs), which ends the agent under the spawn lock.
  • A mode switch that raced the push. a1's post-write check now calls end_if_runs_full_access.
  • Daemon start. adopt_agent_hosts ends a host left in full access instead of adopting it, so its turn is not resumed. For a harness that declared no modes, it first reads back from the log the mode the harness last reported (read_back_undeclared_mode). A live host this build can't read (another record version) ends too.
  • A host adopted while MDM was unset. The push and the background check read that mode back too, for any session whose host a restart left, before the in-memory check (review 27).

The readback skips a current_mode_update the tap marked as a subagent's (SubagentTree::annotate marks only another thread than the session's own), as on_update skips it live; a mark on the session's own thread does not hide its mode. Ending a live child is bounded (twice SHUTDOWN_GRACE), and a host that never answers is ended by its nonce.

Past review findings, each now a case or a unit test of the predicate:

Round Finding Where it is covered
1 A profile pinning full access in argv (--yolo) started anyway; the start_mode answer was stale a1 BOUND "round 1 and 2: a profile pinning full access in argv does not start"; "the off holds across a restart"
2 Other argv spellings (--permission-mode=bypassPermissions, -sdanger-full-access, -csandbox_mode=..., -y, --approval-mode yolo); agents already running or adopted a1 BOUND pinned profiles (all spellings); b RUNNING "round 2: an agent running in full access takes no more work"
3 Check-then-act races (#25214's guard approval; a forward after child_for) a1: the guard approval reads the managed off again under the prompt's locks (full_access_guards); b: child_for and the start path call end_if_runs_full_access under the spawn lock
4 Startup adoption resumed a full-access host; a mode switch racing the push b: adopt_agent_hosts ends it (durable_agents a_retained_full_access_host_ends_once_mdm_turns_full_access_off); a switch re-checks after it writes its mode
5 A mode the harness reports without declaring modes, and other config options; unadoptable hosts predicate forms UndeclaredMode and Option; b RUNNING round 5 cases; durable_agents an_unadoptable_full_access_host_ends_once_mdm_turns_full_access_off
6 An approve-all decision outliving the push; an undeclared mode across a restart; an older webview probe a1 full_access_turned_off waits on every session.permissions lock; b read_back_undeclared_mode (durable_agents a_retained_host_that_moved_itself_into_full_access_ends_once_mdm_turns_it_off); a2 probe tickets
7 The mode config option saying full access while modes.currentModeId asks predicate Option includes the mode option; b RUNNING "round 8: a harness whose mode option says full access while its mode asks ends"; unit each_form_of_full_access_is_found
8 (on the restructure) Pooled and probe launches skipped the pinned-profile refusal; a switch the push overtook entered full access; replay_config read the setting once a1 BOUND "round 9: a switch into full access that the push overtakes is refused"; spawn_pooled refuses and the push drains the pool; replay_config is base's per-step read again; a4 (#25549) "round 9: a profile pinning full access is not probed under the off"
9 (review 25 of a1) Claude live model lists, an ACP probe's stale check, a pooled agent that came up in full access a4 (#25549)
review 27 of b (740fd4f) (1) the child's terminate was unbounded; (2) after a restart with MDM unset, the push's in-memory prefilter skipped the log readback; (3) a subagent's mode update could hide the session's; (4) a claimed pool entry returned after the drain (1) end_managed_full_access bounds it to twice the grace, then ends the host by its nonce (no deterministic test: the stuck link has no seam); (2) durable_agents an_adopted_host_that_moved_itself_into_full_access_ends_when_mdm_turns_it_off; (3) durable_agents a_subagents_mode_does_not_hide_its_sessions_full_access_across_a_restart; (4) a4 (#25549)
review 36 of b (aa5bc58) (1) the bound read the creation's argv, so an agent a config reload respawned with a pinned argv was missed; (2) the push's teardown ran only on a change and read the state as it was then, so a repeated off did nothing and an unset push landing mid-teardown skipped the rest (1) a session records the argv its agent last started with (SessionMeta::spawn_argv, set in ensure_child, kept across a restart) and FullAccessView::of_session reads it, falling back to the creation's before any start: RUNNING "review 36: an agent a config reload respawned with a pinned argv ends at the push"; (2) pushes run one at a time through their teardown (Hub::full_access_pushes), and every off push ends what runs in full access, only the changing one draining the pool: RUNNING "review 36: an off push ends every full-access agent, also when an unset push follows"
review 44 of b (1cf4f25) spawn_argv was set in memory before the spawn but written by whichever save came next, so a daemon that died while the agent started could leave a host the next daemon judged by the old argv and, under MDM's off, adopted ensure_child saves the argv itself before the agent starts, when it changed: durable_agents a_host_a_daemon_left_while_it_started_ends_under_mdm_off_by_the_argv_it_ran (a model pick restarts the chat on a profile a reload pinned to --yolo, the daemon is killed while the agent initializes, and the next daemon under MDM's off ends that host)
review 51 of b (44feabb) (1) a host a restart left was read back only for a no-modes harness's current_mode_update, and not at all when the saved chat had modes, so saved metadata older than the host's reports (a crash before the save) hid a full-access mode or option and the host was adopted under MDM's off; (2) a failed end by nonce was ignored: the evidence was scrubbed and the host could be adopted or reached again (1) reported_by_left_host rebuilds the host's modes and options from its log since host_started (start answers, set_mode and set_config_option answers, its own updates; a subagent's skipped), whatever was saved, once per host; (2) end_managed_full_access returns StillRunning and keeps the evidence while the host may run: adoption skips it and a start is refused with full_access_off until it ends. Tests: durable_agents a_left_host_ends_under_mdm_off_by_the_modes_it_reported_not_the_saved_ones (mode and mode option) and a_full_access_host_whose_end_failed_is_never_reached_under_mdm_off (debug seam ACPMUX_TEST_HOST_END_FAILS)
review 60 of b (4b9443e) (1) a left host's mode or option change it kept while no daemon was attached reached the log only when it replayed, after the adoption check, so a prompt could be written to it before the bound saw it; (2) a set_config_option the harness answered without its options was not counted, so a full-access value it took looked asking (1) adoption judges the session again from the log once the host's replay is in, before host_adopted, and hands nothing out when it ends or cannot end; (2) the value a bare answer took is kept (FloorState::asked_options, read by the bound, replaced by the next options the harness reports, gone when the agent exits or ends) and the left-host readback takes the request's value when the logged answer has no options. Tests: durable_agents a_full_access_option_answered_without_options_ends_under_mdm_off (live push and restart) and a_mode_a_left_host_replays_is_judged_before_its_agent_is_reached_under_mdm_off (the harness changes mode outside any turn while no daemon is attached; debug seam ACPMUX_TEST_UPDATE_DELAY_MS holds each update before the inbound loop applies it)
review 61 of b (2bd9843) (1) an option answer whose options were null counted as a report and dropped the value the request took; (2) a left-host log that could not be read, or a replay that did not complete, let the host be adopted though full access was not ruled out; (3) the once-per-host readback flag survived a new host (1) a null is a bare answer; only a reported list replaces the kept value; (2) under MDM's off an unreadable log counts as full access, and a host whose replay did not complete ends instead of being adopted; (3) the flag clears at every host_started. Tests: durable_agents null cases of a_full_access_option_answered_without_options_ends_under_mdm_off (after an off and an unset push), a_left_host_whose_log_cannot_be_read_ends_under_mdm_off (debug seam ACPMUX_TEST_LEFT_HOST_SCAN_FAILS), a_left_host_whose_replay_is_incomplete_ends_under_mdm_off (debug seam ACPMUX_TEST_REPLAY_INCOMPLETE)
review 62 of b (da67daa) (1) the left-host readback skipped a torn or malformed record, so a log that lost the full-access report read as asking; (2) a mode or option the harness reported while a load replayed (.replay records) was not read back; (3) a fork's answer (the new chat's defaults) was read as this chat's state (1) the readback starts at the newest host_started and needs every record after it in seq order; a gap, a missing start or a read error counts as full access under MDM's off; (2) .replay updates count as their live kinds, as on_update writes them; (3) a fork's answer is not read. Tests: durable_agents a_left_host_whose_log_lost_a_record_ends_under_mdm_off, a_full_access_mode_reported_during_a_load_ends_a_left_host_under_mdm_off, a_forks_answer_does_not_hide_its_parents_full_access_under_mdm_off
review 63 of b (7d65e3f) (1) the readback's scan still skipped an unreadable line, and a torn last record leaves no gap in seq; (2) a link that broke while its host ran was reattached and handed out before what the host kept meanwhile was judged (1) the readback reads strictly (Store::scan_strict: an unreadable line is an error, counted as full access under MDM's off); (2) under MDM's off a reattached agent waits for its host's replay and is judged again before it is handed out, and ends when it runs in full access or the replay did not complete. Tests: durable_agents a_mode_a_reattached_host_replays_is_judged_before_its_agent_is_reached_under_mdm_off (debug seam ACPMUX_TEST_UNLOGGED_ONCE fails to log one host entry, which breaks the link) and a_left_host_whose_log_ends_in_a_torn_record_ends_under_mdm_off
review 64 of b (aad7934) a host whose link broke (an entry could not be logged) may hold a full-access report this daemon never saw; an off push trusted the chat's last known mode and left it running until a request reattached it under MDM's off an agent whose host link is broken, or closed while its host still runs, counts as full access, so the push ends it by its nonce. Test: durable_agents a_host_whose_link_broke_ends_when_mdm_turns_full_access_off
review 65 of b (522d78e) (1) a host link that broke after MDM's off woke nothing, so the agent ran on until a request, a push or the idle period; (2) when the end of an unheard agent failed, nothing kept that it was unsafe, and the next push skipped it; (3) ending an agent whose reader had stopped left its requests (a prompt) waiting forever (1) the stopped reader tells the hub (Inbound::LinkBroken), which runs the full-access check at once under MDM's off; (2) a failed end is kept (full_access_end_pending) until an end succeeds or a new host starts, and every check counts it as full access; (3) the end answers what waited on the agent and cancels its pending permissions. Tests: durable_agents a_link_that_breaks_under_mdm_off_ends_its_agent_at_once, a_failed_end_of_an_unheard_agent_is_tried_again_by_the_next_push, a_prompt_on_an_unheard_agent_settles_when_mdm_ends_it
review 66 of b (5f9aabf) (1) only a link that broke on an unlogged entry told the hub; one that closed (the host died, or another owner took it over) woke nothing; (2) host records that could not be read counted as no host, so a failed end was taken as done (1) the reader tells the hub whenever its link stops; (2) an end needs the records to prove the host gone (host_record_gone), else it stays pending. Tests: durable_agents a_link_lost_under_mdm_off_ends_its_agent_at_once, an_end_that_could_not_read_the_host_records_is_tried_again
review 67 of b (81d6df6) the log readback and the unheard check still read host records that could not be read as no host, so an adopted host in full access was skipped by the push they ask whether the records prove the host gone (host_record_gone). Test: durable_agents an_adopted_host_in_full_access_ends_under_mdm_off_though_its_records_cannot_be_read
review 68 of b (5ec5de1) (1) an off pushed while a left host was adopted, after adoption read the policy and before the host replayed, left that replay unjudged; (2) after a failed acknowledgement the hub was told before the agent counted as unheard, so with its socket still open the check skipped it (1) adoption and re-adoption carry the off with the count of offs (OffLatch), so an off pushed meanwhile judges the replay; (2) a reader that stops marks its agent not alive before it tells the hub. Tests: durable_agents an_off_pushed_while_a_left_host_is_adopted_judges_its_replay, an_agent_whose_ack_failed_ends_under_mdm_off
8 Approve-all was capped under the person's own off too, with MDM unset a3 "MDM unset: the person's own off leaves approve-all as it was"

Tests

  • tests/full_access_managed.rs under_an_mdm_off_what_runs_in_full_access_ends runs the RUNNING case table:

    • round 2: an agent running in full access takes no more work;
    • round 5: a harness that reports a full-access mode without declaring modes ends;
    • round 8: a harness whose mode option says full access while its mode asks ends;
    • round 5: a harness that sets another config option to full access ends.
  • tests/durable_agents.rs:

    • a_retained_full_access_host_ends_once_mdm_turns_full_access_off;
    • a_retained_host_that_moved_itself_into_full_access_ends_once_mdm_turns_it_off;
    • an_unadoptable_full_access_host_ends_once_mdm_turns_full_access_off.
  • Red at fe91440 (a1's code with these tests, run 40370601829): all four RUNNING cases and the three durable tests fail.

  • Green at 740fd4f (run 40370601422). fmt and clippy are clean, full_access_managed passes 2/2, and durable_agents passes 56/56. Besides a1's base reds, two hub_integration tests failed in the full run: stop::a_cancel_ends_an_honouring_agents_turn_within_the_budget and subagent_retry::the_retry_queues_behind_a_running_turn. Run alone three times, both pass on this head and on base (runs 40372489684 and 40372489383), so they are timing flakes under the full run's load.

  • Review 27 round: red at eb44da3 (b's code with the two new durable_agents tests; run 40380501478 on the same tree: the adopted agent ran on after the push; run 40379138809 on 8f95c07, the same subagent test: the subagent's mode hid it). Green at 2ffe473 (rebased on a1 e70c3d8; run 40379138660 on 01a1bc3: fmt and clippy clean, lib 1101/1102 with only base's red, full_access_managed 2/2, durable_agents 58/58, hub_integration full_access 14/14; run 40381441370 on cc85593, the same tree as 2ffe473). aa5bc58 makes the two case tables run one at a time: both set CMUX_NEXT_CONFIG_FILE, and run side by side "MDM unset changes nothing" read the other table's settings file once in four runs (run 40381990458). At aa5bc58 five runs pass, with fmt and clippy clean (run 40382894202).

  • Old code: the pre-restructure head with the full case table is 64e5662 (run 40365300783). There, the round-8 case fails.

  • Review 36 round: red at 5d59ab8 (the two new RUNNING cases on aa5bc58; run 40393100527: "the respawned pinned agent ran on after the push" and an agent "ran on after the off push"). Green at 1cf4f25 (run 40393298206: full_access_managed 2/2, session_pool 17/17, durable_agents 57/58; run 40393298519: fmt and clippy -D warnings clean, lib 1101/1102 with only base's red; run 40393298156: hub_integration 490/491). The two failures are the known flakes durable_agents stalled_turn::an_adopted_turn_whose_model_went_quiet_ends_as_stalled_and_frees_its_agent and hub_integration stalled_turn::a_turn_counts_from_its_prompt_being_written; both pass alone at 1cf4f25 (run 40393818409).

  • Review 44 round (rebased onto feat-cmux-next with a1): the new durable_agents test does not fail on the previous head, so there is no red run. A save-site probe (run 40404365364) found that on a cold start the folder check (revive_folder, worktree_care/revive.rs) saves the chat after spawn_argv is set and before the spawn, but only when its git read answers within 2 s. The fix makes the save independent of that read; the test covers the crash and restart and guards against that save going away. No test seam makes the git read slow. Green at 44feabb: run 40405195004 (fmt and clippy -D warnings clean; lib 1142/1143 with only base's red own_servers_run_under_the_daemon_prefix), run 40405199026 (durable_agents 63/63, full_access_managed 2/2, session_pool 16/17 with only base's red pooled_sessions_end_with_the_daemon_and_after_a_crash), run 40405201915 (hub_integration 497/497).

  • Review 51 round (rebased onto feat-cmux-next 3e72956): red at db599af (run 40411599957: both new durable_agents tests fail; the next daemon adopted the full-access agent on its saved modes, and a prompt reached the host whose end failed). Green at 4b9443e: run 40411974631 (fmt and clippy -D warnings clean; lib 1164/1165 with only base's red own_servers_run_under_the_daemon_prefix), run 40412437781 (durable_agents 65/65, full_access_managed 2/2), run 40411974621 (session_pool 16/17 with only base's red pooled_sessions_end_with_the_daemon_and_after_a_crash), run 40411974764 (hub_integration 497/497). The diff is about 880 changed lines, some 550 of them tests; the code part stays near 330.

  • Review 60 round: red at 7e2954b (run 40420750440: the bare-answer test fails, the agent ran on under the off) and dfe9e5d (run 40422629526: "a prompt reached the full-access agent the host replayed"). Green at 2bd9843: run 40422935460 (fmt and clippy -D warnings clean; lib 1164/1165 with only base's red own_servers_run_under_the_daemon_prefix), run 40422935087 (durable_agents 67/67, full_access_managed 2/2, session_pool 17/17), run 40422935074 (hub_integration 497/497).

  • Review 61 round: red at 3549eb4 (run 40428603661: the three tests fail; the null-answer agent ran on, and the next daemon adopted both left hosts). Green at da67daa: run 40428603664 (fmt and clippy -D warnings clean; lib 1164/1165 with only base's red), run 40428603691 (durable_agents 69/69, full_access_managed 2/2, session_pool 16/17 with only base's red), run 40428603706 (hub_integration 497/497).

  • Review 62 round: red at 2b17332 (run 40435892213: the three tests fail; the next daemon adopted each host). Green at 7d65e3f: run 40436061104 (fmt and clippy -D warnings clean; lib 1164/1165 with only base's red), run 40436061303 (durable_agents 72/72, full_access_managed 2/2, session_pool 17/17), run 40436061120 (hub_integration 497/497). Known cost: under MDM's off, a log a crash tore mid-write ends the host the restart left, since what it reported cannot be ruled out.

  • Review 63 round: red at dc30179 (run 40441769368: the reattach test fails, "the full-access agent a reattached host replayed answered a prompt"). The torn-record test passes on the previous head too: there the next daemon also ended that host by another path, so the test guards the strict read rather than proving a red. Green at aad7934: run 40441769314 (fmt and clippy -D warnings clean, durable_agents 74/74); e8dc375 has the same code and only differs in that test: run 40440768263 (lib 1164/1165 with only base's red), run 40440768387 (full_access_managed 2/2, session_pool 17/17, stats 11/11, prompt_images 29/29), run 40440768868 (hub_integration 497/497).

  • Review 64 round: red at 4b4d2d7 (run 40451136699: "an agent whose link broke ran on after MDM turned full access off"). Green at 522d78e: run 40451136658 (fmt and clippy -D warnings clean, durable_agents 75/75); 7038765 has the same code: run 40450081606 (lib 1164/1165 with only base's red), run 40450081231 (full_access_managed 2/2, session_pool 17/17, stats 11/11, prompt_images 29/29), run 40450081188 (hub_integration 497/497).

  • Review 65 round: red at 30b2910 (run 40453253479: the three tests fail). Green at 5f9aabf: run 40453375422 (fmt and clippy -D warnings clean; lib 1164/1165 with only base's red), run 40453375230 (durable_agents 78/78, full_access_managed 2/2, session_pool 17/17, stats 11/11, prompt_images 29/29), run 40453375339 (hub_integration 497/497).

  • Review 66 round: red at f235570 (run 40454765151: both tests fail). The fix 654effc, then feat-cmux-next merged in (a3 and a4 landed) with the off read once per operation and passed down, and session ids read through one blank-to-None normalizer (known_id). Green at 81d6df6: run 40456355829 (fmt and clippy -D warnings clean; lib 1318/1319 with only base's red), run 40456355928 (full_access_managed 2/2); at d79ba80 (the same code before a clippy fix): run 40455737157 (durable_agents 84/84, session_pool 26/26, stats 29/29, prompt_images 11/11, web_mode_current 13/13), run 40455737258 (hub_integration 507/507).

  • Review 67 round: red at de54566 (run 40457941135: the test fails). Green at 5ec5de1: run 40458211283 (fmt and clippy -D warnings clean; lib 1318/1319 with only base's red), run 40458611259 (durable_agents 85/85), run 40458211274 (full_access_managed 2/2, stats 29/29, prompt_images 11/11, web_mode_current 13/13, session_pool 25/26 with only pooled_sessions_end_with_the_daemon_and_after_a_crash, which fails the same way on base: run 40458862075), run 40458211223 (hub_integration 507/507).

  • Review 68 round: red at cab0e08 (run 40460850011: both tests fail). Green at a53707b: run 40462536166 (fmt and clippy -D warnings clean; lib 1318/1319 with only base's red), run 40462536156 (durable_agents 87/87, full_access_managed 2/2, stats 29/29, prompt_images 11/11, web_mode_current 13/13, session_pool 25/26 with only base's red), run 40462536120 (hub_integration 507/507).

  • Size: about 2030 changed lines: some 1210 in tests (durable_agents, full_access_managed, the fake agent) and some 820 in code. It stays one PR because each review round's fix and its red test belong to the same bound.

No capture: no UI changes.

Review: review 69 (Codex gpt-6-astra xhigh) at a53707b is BLOCK with three findings not yet fixed: (1) a pooled entry claimed before an off push keeps no count of offs, so after an unset it can be handed out though it ran during the off; (2) records that cannot be read can let a stale clean log read be cached in left_host_read; (3) child_for's fast path reads only what the inbound loop has applied, so a prompt can reach an agent whose full-access update is logged but not applied yet.

Agent session: agent session

🤖 Generated with Claude Code

@cursor

cursor Bot commented Oct 26, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot uses usage-based billing for this team and requires on-demand usage to be enabled.

A team admin can enable on-demand usage in the Cursor dashboard.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Owned by an agent session · session

@teamleaderleo
teamleaderleo force-pushed the lane/hb-stale-trust-23322-b branch 2 times, most recently from cc85593 to 2ffe473 Compare October 26, 2026 18:51
Base automatically changed from fix/full-access-enforcement to feat-cmux-next October 26, 2026 20:59
@github-actions github-actions Bot added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 26, 2026
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

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

@teamleaderleo
teamleaderleo force-pushed the lane/hb-stale-trust-23322-b branch from 1cf4f25 to 44feabb Compare October 26, 2026 21:37
@github-actions

github-actions Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

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

Review a53707b: 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=pr25539 -f ref=lane/hb-stale-trust-23322-b -f platform=macos -f backend_mode=local

web and size checks: green

teamleaderleo and others added 10 commits October 26, 2026 22:18
…se per form the reviews found (#23322 b; red)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ns true for it (#23322 b)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… at a later MDM push, and a subagent mode does not hide it (#23322 b, review 27; red)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the log, a subagent's mode is not its session's, and ending is bounded (#23322 b, review 27)

The push and the background check read back the mode a harness that
declares no modes reported, before the in-memory check, for a host a restart
left. The readback skips updates the tap marked as a subagent's (on another
thread than the session's own), as on_update does live. The child's
terminate is bounded; a host that never answers is ended by its nonce.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Both set CMUX_NEXT_CONFIG_FILE; run in parallel, one table's daemon read the
other's settings file ("MDM unset changes nothing" failed one run in four).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…off push an unset push follows (#23322 b, review 36; red)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ted with, and each off push ends what runs in full access before the next push lands (#23322 b, review 36)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…b, review 44)

`spawn_argv` was set in memory before the spawn and written by whichever
save came next. On a cold start that is the folder check's
(`revive_folder`), which saves only when its git read answers within 2 s;
past that, a daemon that died while the agent initialized left a host the
next daemon judged by the old argv, and under MDM's off adopted it. The
start now saves the argv itself when it changed.

durable_agents a_host_a_daemon_left_while_it_started_ends_under_mdm_off_by_the_argv_it_ran
covers the crash and restart. It passes on the previous head too, because
there the folder check's save lands in time; no test seam makes the git
read slow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s, and one whose end failed (#23322 b, review 51; red)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…and one whose end failed is never reached (#23322 b, review 51)

The bound read back only a no-modes harness's current_mode_update, and only when the saved
chat had no modes; it now reads back every mode and option the left host reported since it
started (start answers, set_mode and set_config_option answers, its own updates), once per
host. A host whose end by nonce fails keeps what says it runs in full access, is not adopted,
and its chat is refused until it ends.

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

cursor Bot commented Oct 27, 2026

Copy link
Copy Markdown

Bugbot needs on-demand usage enabled

Bugbot uses usage-based billing for this team and requires on-demand usage to be enabled.

A team admin can enable on-demand usage in the Cursor dashboard.

…the off (#23322 b, review 68; red)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… is not heard (#23322 b, review 68)

Adoption and re-adoption carry MDM's off as an OffLatch (the state with the count of
offs, read once where they begin), so an off pushed while a left host is adopted still
judges what its replay says before the agent is handed out. A host reader that stops for
any reason marks its agent not alive before it tells the hub, so a failed acknowledgement
with the socket still open counts as unheard under the off.

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

Copy link
Copy Markdown
Collaborator Author

Review (codex gpt-6-astra xhigh, next-review): BLOCK at a53707b

  • hub/lifecycle.rs:1425: Full-access reports can race new prompts. The fast path checks only applied session metadata. A harness’s full-access update is logged before the inbound loop applies it, so a concurrent prompt can still reach that agent under MDM’s off. The existing update-delay seam exposes this window. Enforcement must account for pending reports before handing out the child.

  • hub/hosts.rs:290: Unadoptable hosts can retain unseen full access. If a detached harness switches into full access, its update remains buffered in the host. An unsupported record version prevents replay; this check trusts the older asking-mode log, and host_unheard returns false because no child was attached. The host therefore survives startup and subsequent off pushes indefinitely. A live host whose state cannot be recovered must be ended rather than cleared by its stale log.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Closing: this PR was split into pieces under 800 lines. Each piece landed on its own on feat-cmux-next, with red-first tests and its own Codex review at its head:

Follow-ups from the reviews:

Agent session: agent session

This branch was previously deployed

3 inactive deployments
artifacts — a53707b4 Deployed Oct 27, 2026 by teamleaderleo via add the Windows daemon to the tree #62806
sentry-dsyms — a53707b4 Deployed Oct 27, 2026 by teamleaderleo via upload cmux-tui dSYMs to Sentry #62806
ci-routing-agent — a53707b4 Deployed Oct 27, 2026 by teamleaderleo via wait for same-key tree publisher #62806
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