acpmux --group: a variant of a group's name joins that group; a refused group exits 2 as usage.group_name

TE
4d ago
+184-62

Fixes manaflow-ai/cmuxterm-hq#4976

For Leo's ask to productionize the acpmux daemon ("do we have more productionized stuff wrt the daemon ... let's go set up more fellas on it"). This finishes what #24581 left open: acpmux new --group makes a stray sidebar group when a name is typed a little differently, and its refusal skips the usage envelope.

What

  • The app matches a group by its label, not its exact string. --group "gui push", "GUI push " or " Gui Push\n" now joins the existing "GUI push" group, and a re-homed lane finds its old row in that group. A group with exactly that name still wins over a case variant, and a new group gets the trimmed name. Before, any variant made a second "GUI push" group next to the first (group.name == name).
  • The group helpers move out of CmuxNextApp. validGroupName, group(named:accepting:in:), predecessor(named:inGroup:in:) and the new sameGroupName are pure sidebar-tree functions. They move from AgentSessionWorkspace to SidebarEdits+NamedGroup.swift in CmuxNextSidebar, next to SidebarEdits.canMove, which they use. CmuxNextApp shrinks by 35 lines (arch-ratchet app_lines).
  • A refused --group is a usage error. A blank or id-shaped --group (grp_…, ws_…, a UUID) used to exit 1 as a runtime error. It now exits 2 with detail: group_name and data.reason: usage.group_name in the --json envelope, still before any session is made.
  • Docs. The --group help, the acpmux skill and the README say that the name is matched ignoring case and outer spaces, and that an id is refused with exit 2.

Test

Red commit 48f6920beb1 adds only tests:

  • cd cmux-tui && cargo test -p acpmux --lib -- a_group_is_a_human_label_and_never_an_object_id: the refusal must classify as exit 2 with usage.group_name. Green at head 96ee0624f71: test result: ok. 39 passed; 0 failed with a_group_is_a_human_label_and_never_an_object_id ... ok (fast-checks run 41159694793). Red: not run. The broker refused the run at the red commit twice (GitHub quota 403, then busy). The reviewer confirmed by reading that it fails on base, where the refusal is a plain bail! that classifies as exit 1 / error.
  • AgentSessionGroupJoinTests.theSameLabelTypedAnotherWayJoinsTheGroup (CmuxNextAppTests): case and whitespace variants join g1 and find the predecessor row, "GUI pushes" does not match, and an exact name beats a case variant, both for the group and for the re-homed row (added after review, 96ee0624f71). On base, every variant returns nil. This lane VM has no fleet client, so the Swift suites run only in this PR's CI at its head.

Observed / Not run

  • Observed: prepush-gate.py reports no new violations; rustfmt is clean on the changed Rust files.
  • Not run: a tagged-build launch of acpmux new --group "gui push" into a sidebar that already has "GUI push". The Swift change is covered by the unit test above, not by a capture.

No capture: the sidebar looks the same; the only change is which existing group a scripted --group open joins.

Default changes: the defaults guard lists the moved functions of AgentSessionWorkspace.swift (a tunable source) by hash. No setting, key or flag default changes: the functions moved to SidebarEdits+NamedGroup.swift, and only group-name matching changed.

Review

Independent review (Codex gpt-6-astra, high): BLOCK at 1627fa5b2a3 for a missed test caller (AgentSessionGroupPlacementTests still called AgentSessionWorkspace.group) and for predecessor not preferring the exactly named group. Both were fixed in 96ee0624f71, and the delta review gave SHIP at 96ee0624f711220dc483e532f90117a8e86cfef1.

Changelog

  • Fixed: acpmux new --group joins the existing sidebar group when the name differs only in case or outer spaces, instead of making a second group.

Agent session: agent session

šŸ¤– Generated with Claude Code