Repository navigation
retire the per-registry control-session name once a psmux release carries the psmux#599 fix #733
Description
#537 names the psmux control session per registry — bmad-loop-ctl-<16-hex digest of the resolved registry root> — instead of the fixed bmad-loop-ctl. That suffix exists for one reason, and it is upstream's:
psmux keys its single-server-per-name mutex on the session name, not on PSMUX_DATA_DIR. acquire_session_mutex builds Local\psmux-session-{port_file_base} (src/platform.rs:346), and port_file_base() is {socket_name}__{session_name} or the bare session name (src/types.rs) — the data root never enters it. So two per-project registries, correctly isolated for lookup and cleanup, still share one global name namespace: with a fixed control-session name, every project's TUI launch but the first fails as a duplicate cold-spawn.
Filed upstream as psmux/psmux#599, with two reproductions on installed psmux 3.3.8 (66cf613) and a source read confirming acquire_session_mutex and port_file_base() are byte-identical between v3.3.8 and master 60e59fb.
What to do when it is fixed upstream
Retire the suffix behind a version gate — do not simply delete it. While any supported psmux build lacks the fix, removing the suffix reinstates the failure for every project after the first. The shape is the one this project already used for the 3.3.7 → 3.3.8 workaround retirements in #658: the deletion lands only behind a _LAST_UNSUPPORTED bump to a release that carries the fix.
Note the retirement is operator-visible, unlike most of that family: the control session's name changes back, so psmux ls output and any operator muscle memory or scripting around bmad-loop-ctl-<digest> changes with it. That deserves a changelog entry rather than a silent revert.
Two things to check at that point, both currently load-bearing:
runs.ctl_session_forandruns.is_ctl_session_name— the mint-side and parse-side predicates. They answer different questions on purpose (#537conflated them three times before separating them); a retirement must not re-merge them.- The reserved-shape refusal in
--run-idis independent of this workaround and should survive it: an id equal to the control session's name hands a run the live control session as its own agent session, which is true of the fixed name too — it is why--run-id ctlwas already dangerous onmain.
Related: #537, #660 (the psmux adoption umbrella — this belongs on its watch list), psmux/psmux#599.
Activity
test(psmux): tripwire probes for three upstream fixes awaiting release
refactor(runs): return the psmux control session to one fixed name
refactor(runs): return the psmux control session to one fixed name
dracic commentedon Oct 13, 2026
cleanupandattachnever mention a pre-upgradebmad-loop-ctl-<digest>control session after the rename (psmux 3.4.0)After the rename in #900, a control session started under the old
bmad-loop-ctl-<16 hex>name keeps running in the project's own registry, as the CHANGELOG says. What the CHANGELOG does not say is that cleanup does not report this surviving session in the primary registry, and attach does not locate its window. With such a session holding a parked run window tagged for the project,bmad-loop cleanup --dry-runprintsnothing to clean up,cleanup --jsonreports"legacy_leftovers": [], andbmad-loop attach <that run>answersnothing to attach(rc 1). The legacy session and its run window remain on that session's psmux server. The docs tell the operator to "end the old session once its windows have parked", but an operator who did not read the CHANGELOG is told, by these cleanup and attach commands, that there is nothing there.Measured on psmux 3.4.0 (20aa1d7), Windows 11 10.0.26200, bmad-loop 4c00c37, three runs plus a positive control. The block below is launched from PowerShell 7 (
SHELLremoved) in a bmad-loop checkout withuv syncdone; zero LLM tokens. The legacy session is seeded through bmad-loop's ownPsmuxMultiplexer, the way the old release left it: the old digest name, plus a parked run window tagged for the project withset_window_option(wid, runs.PROJECT_OPTION, runs.project_tag(project)). The positive control seeds the identical window under the current name.cleanup/cleanup --dry-runnothing to clean up,legacy_leftovers: []cleanupalready names a surviving control session in a legacy registry (left in <registry> (not migrated): …)attach <run whose window is in it>nothing to attachcleanupObserved at symbol level as well, 3/3:
runs.legacy_registry_leftovers(project)returns({}, []),launch.prunable_ctl_windows([], []),launch.ctl_window_lookup(None, 0)for the legacy window. At symbol level,kill_ctl_windowreturns 0 while the legacy window remains; a TUI consequence is source-derived, not independently observed here. Underbmad-loop-ctlthe same window givesctl_window_lookup('bmad-loop-ctl:@2', 0)andprunable_ctl_windows(['run-20261013-190001-abcd'], []), so the outcome flips on the session name alone.Source, bmad-loop 4c00c37.
runs.legacy_registry_leftovers(src/bmad_loop/runs.py:2309) is the one reader that names a surviving control session to the operator, but it only walks_legacy_registries()(runs.py:2478:get_multiplexer().legacy_registries()), that is, psmux's default registry and a displaced root:The digest-named session lives in the project's own derived registry, the primary one, which no leftover reader lists.
cmd_cleanup(src/bmad_loop/cli.py:6706) printsnothing to clean upfrom those empty lists:attach_plancallsctl_window_lookup(src/bmad_loop/tui/launch.py:467), which listsctl_session(project)only, and that isbmad-loop-ctlsincectl_session_for(runs.py:1805) returnsCTL_SESSION(runs.py:1824):is_ctl_session_name(runs.py:1827) still recognises the digest shape, so the prune spares it. Sparing it is correct; nothing reports it.Suggested, not validated: in
legacy_registry_leftovers, or in a second reader thatcmd_cleanupand the TUI's cleanup toast already print beside it, also list ctl-shaped sessions in the primary registry other thanctl_session_for(project). That puts the remainder in the sameleft in <registry> (not migrated)line and the samelegacy_leftoversJSON field.cmd_attachcould say "this run's window may be in " when it finds nothing and such a session exists.fix(cleanup): report a pre-upgrade bmad-loop-ctl-<digest> control ses…
fix(cleanup): find a pre-upgrade control session through an alias of …
fix(cleanup): find a pre-upgrade control session in an honoured regis…
fix(cleanup): print a pre-upgrade control session's command only on p…