acpmux: a bare session name or id prefix reaches the one live session on any host; several are refused with each HOST/NAME command
Leo, 2026-10-30: "i feel like there's still a big mess of uncategorized stuff in the sidebar + various not as ergonomic/one-command-y as i'd liike set of commands for interacting with stuff" / "good intuitive stuff" / "and also docs!"
Part of HQ#5197 (productionize team tools: one command per common task). No HQ issue covers acpmux name resolution itself.
What was wrong
Which session a bare name reached depended on the command:
send,last,wait,info,attachandrenamewent through_acpmux/infoand the send queue. They took a session on this host first, soreviewreached the localreviewand silently ignored a peer's livereview. A peer's name worked only when no local session matched (Hub::resolve, thenresolve_remote).retryandsession cancel|stop|resumewent through_acpmux/resolve, which refused that same case.- A closed local
fixerbeat a livelinux-b/fixer. A closed and a livedeployon two peers counted as ambiguous, so the command answeredno_session. - The ambiguity refusal listed qualified ids, not a command you could run.
What changes
- One rule, in the daemon (
hub/key_route.rs): a bare key (no/, not an exact local id) names the one session that has it as its whole name or an id prefix, across this daemon and every peer it lists. Name and id are one tier, so a name never hides another session's id. Only when nothing matches that way does a name prefix name a session on this host (the daemon's existing rule). A peer's name prefix never matches, asresolve_remotenever matched one: a short key never reaches another machine's chat.
A sub-session's whole name (parent/child) is routed the same way, by whole name only. A key whose first segment is a peer, this machine orlocalstays exact (HOST/NAME, qualified ids). - Closed sessions count only when no live one matches. So
session info donestill reads a finished lane. - Several live matches are refused, never picked. A session here no longer wins over a peer's. Exit 2,
session_ambiguous.--jsoncarrieserror.data.choiceswith each choice'shost,name,routeand the key touse, every one of them (the message names 10). The CLI prints the exact command for each:$ acpmux send review "ship it" acpmux: usage: "review" names 2 live sessions: mac-a/review, linux-b/review; name one of them acpmux send mac-a/review 'ship it' acpmux send linux-b/review 'ship it' - Where it applies: in
handle_request, on the unix socket only, before any check reads the key. It covers every session method thatpeer_forward.rswould forward, plus the send queue'srequest.sessionId, sosend(also--no-wait),last,wait,attachandsession info|cancel|stop|close|rename|tag|…all find the same session. Exact keys (HOST/NAME,peer/id, a local id) are left alone, and so iskeep's exact ephemeral id. Web and peer connections are unchanged. control_id(retry,session cancel|stop|resume) uses the same live-over-closed rule and the same refusal.- Current rule kept: an exact id,
HOST/NAMEand a qualified id still win as they did before. Changed rule: local no longer wins on a bare name. Two resolvers disagreed here, and the brief asks never to pick silently. - Docs: README "lifecycle controls" section, the agent guide (
skills/acpmux/SKILL.md, §6), andacpmux send --helpwith examples.
Related: #27791 (open, its lane is gone) adds sub-session short names (a child answering to its short name) in Hub::resolve. The two compose: this router passes over a key that no whole name or id prefix matches, so the short-name lookup there still runs after it. Whichever lands second takes a small text merge in cli/errors.rs / server/requests.rs.
Tests
hub::key_route::testsa_bare_name_only_a_peer_has_routes_to_that_peera_name_on_this_machine_and_a_peer_is_refused_never_pickedan_id_prefix_names_a_peers_session_or_one_here(also: a shared prefix is refused, and a peer's name prefix is no route)closed_sessions_do_not_count_while_a_live_one_matchesone_live_session_wins_over_closed_ones,several_live_sessions_are_refused_with_each_host_and_namea_sub_sessions_whole_name_is_routed_across_hosts_too
server::key_route_tests(through the real dispatcher)info_by_a_name_here_and_on_a_peer_is_refused_with_botha_send_to_a_shared_name_is_refused_not_queued_for_the_one_herea_sub_sessions_whole_name_here_and_on_a_peer_is_refused
cli::resolve::testsan_ambiguous_name_prints_the_exact_command_for_each_hostresolve_cards_skip_closed_sessions_and_refuse_several_live_ones
Observed / Not run
- Red on base (cmux-ci run 41012776600): with the base's
src/hub,src/cli,server/requests.rsandserver/peer_forward.rsand only the new request tests,cargo test -p acpmux --lib -- server::key_route_testsfails 3 of 3.info reviewreturns the local session silently;send reviewis queued ("status":"queued","target":"review") for the local one;info review/childreturns the local sub-session. - Green on head d95ba2f: the same 3 tests pass, plus the hub and CLI tests listed above. Full
cargo test -p acpmux --libat d95ba2f: 2017 passed, 0 failed (cmux-ci run 41014420558);tests/test_ci_fleet_rule_lints.pyOK at 00419b6 (run 41013564700), and the crash ratchet names no acpmux row. - Not run: a live two-host check.
Changelog
Changed: acpmux commands take a bare session name or id prefix for a session on any peer. A name several live sessions share is refused with the exact HOST/NAME command for each.
risk: normal (CLI and daemon session-key routing, local socket only).
Agent session: agent session
🤖 Generated with Claude Code