acpmux: CLI sessions from an interactive Mac go to a fleet host; held hosts never placed; session stop --id without --mesh

Merged
TE
7d ago
+547-28

On 2026-10-29 cosmic-gray (Leo's interactive Mac) beachballed: an agent broadcast woke about 64 idle sessions at once, and the coordinator stopped 94 idle sessions with Leo's OK. The coordinator's order, which Leo approved ("Yes, do both"):

a resumed session restarts on cosmic-gray, and new lanes still default here. Fix both in your lane. A resume or new session from an interactive Mac (cosmic-gray, air-blue) should be placed on a fleet host through host place, unless the user opened it in the GUI. A host that sits idle for more than about 10 minutes should stop on its own, and the session stays resumable. Also: session stop with --id demands --mesh, and only the positional form works. That's a CLI papercut; fix it too.

And Leo: "lawrence is currently usign 16-24 for a bit".

What

  1. New sessions from an interactive machine go to a fleet host (cli/place.rs new_target, server/place.rs). On a machine the fleet registry marks interactive, acpmux new with no --host now places the session the way --host auto does. The placement leaves out every interactive machine, this one included (_acpmux/place with offInteractive).
    • The interactive check comes from the registry (placement::interactive): a machine is interactive if its entry has interactive: true, or if it holds the registry's role for a person's own machine (leo-mac or attended). Today that is cosmic-gray and air-blue. No host names are written into the code.
    • --host local keeps the session on this machine.
    • A launch that names a local folder (--cwd or -w) stays here and prints a warning saying how to place it. A fleet host refuses a folder on this machine: a peer may use only a known project or its webRoots (remote_guard.rs).
    • If no fleet host takes it, a new that named no host starts here with a warning, since placement never refuses a launch for load. The exception is when the registry holds this machine; then it stops. An explicit --host auto still stops when nothing is picked, as before.
    • Chats opened in the app are not affected. The app creates them over its own connection (session/new) and never goes through the CLI's new.
  2. Held and reserved hosts are never placement candidates (placement.rs hold, held_at). A registry machine entry with hold, or a top-level reservations entry naming the machine, means it takes no new work. It is skipped even as the fallback when no other host has room.
    • hold can be true, a reason, or {reason|why, owner, until}. This covers the per-machine hold field a sibling lane is adding to fleet-registry.json, so it works as soon as that lands.
    • A reservation is active from its from to its until. cmux16 to cmux24 are reserved for Lawrence today.
    • A hold or reservation whose time cannot be read still holds.
    • Without this, the new default placement could send sessions from cosmic-gray to Lawrence's minis.
    • #27662 carries its own copy of the reservation reader. Whichever PR lands second drops its copy.
  3. The idle stop already ends the agent host, not only the harness. The idle pass terminates the hosted child (Terminate). The harness exits, and the agent host leaves its loop once its exit entry is acked. The session stays resumable. The default bound is 120 s (hub::IDLE_CHILD), well under 10 minutes. an_idle_hosted_agent_is_terminated_and_the_session_resumes now also checks that the agent host process exits and that its record is gone before the session resumes. hub/idle_census.rs (why some harnesses stay up) is left to the sibling lane.
  4. session stop --id ID works without --mesh (cli/mesh_control.rs). --id no longer requires --mesh. Without --mesh, an id already resolves as an id, so stop --id and resume --id now run the same as the positional forms. --idempotency-key is still mesh-only.

Not in this PR: a CLI resume of a stopped session on an interactive Mac still restarts it here. Placing a resumed session elsewhere means moving its history, which is the session move --host auto path in #27662. That resume routing follows once #27662 lands.

Tests

  • placement_tests: a_reserved_host_is_never_a_placement_candidate covers the active window, lapsing before from and after until, an unreadable time and lookup by alias. a_held_host_is_never_a_placement_candidate covers every form of hold, the overloaded fallback, a lapsed hold, an unreadable until and lookup by alias. interactive_machines_come_from_the_registry covers the interactive field, the leo-mac/attended roles, aliases and having no registry.
  • server::place: off_interactive_drops_the_machines_people_work_at.
  • cli::place: a_new_from_an_interactive_machine_is_placed_unless_a_host_is_named also covers the no-pick case: an explicit auto still stops, an implicit one starts here, and a held machine stops.
  • cli::mesh_control: id_without_mesh_runs_the_local_command.
  • session_control_peer_cli (integration): stops a peer session with session stop --id ID and no --mesh.
  • durable_agents (integration): the idle agent host exits and the session resumes.

Risk

Medium. This changes the default for a command-line new on cosmic-gray and air-blue: such a session now runs on a fleet host, in that host's scratch folder. --host local restores the old behavior. The app's chats and every other machine are unchanged.

Overlap: #27662 (session move) also edits placement.rs. It adds reservations in place, while this PR touches only lane_hosts/weigh, so their hunks do not overlap. The duplicate reservation reader goes in whichever PR lands second.

Review: Codex gpt-6-astra (high, fast) via review-router. The first pass found three blocking issues: an explicit --host auto fell back to this machine, a held machine could still take sessions through that fallback, and reservations were not enforced. All three are fixed in the second commit. The second pass found that unreadable hold or reservations data failed open; the third commit makes it fail closed (unreadable_holds_and_reservations_hold). The third pass returned SHIP at 55dc06bfc2bc.

Agent session: agent session

šŸ¤– Generated with Claude Code