Umbrella issue for the psmux target-resolution family. The four children are separate defects on
separate call paths, but three of them are the same root cause and the fourth is the audit that
found them.
Root cause: ids are per-server, and psmux runs one server per session
On tmux a bare @N (window) or %N (pane) id is server-global and unambiguous. On psmux it
is only meaningful on the server that minted it, and the CLI resolves it against the caller's
current-session server. In a normal TUI-launched run there are at least two servers — the
bmad-loop-ctl session's and the agent session's — with colliding ids (@1 exists on both). So
a bare-id -t from inside the ctl pane silently binds to the wrong pane and exits 0.
This was already root-caused once, for switch-client, in #221 / psmux/psmux#483 and fixed in
#225 by composing a session-qualified target in the backend that knows its own target grammar.
The fix was applied to one call path, not to the class. #254 is the same bug on pipe_pane.
Compounding factor: the pre-#483 rc-0 no-op
psmux builds without the #483 fix exit 0 as a no-op when switch-client gets an unresolvable
target. Every fallback in this codebase that keys off a nonzero exit code — the POSIX trailer in
adapters/tmux_base.py (switch-client -t "$ret" || switch-client -l), the pwsh override in
adapters/psmux_backend.py, and BaseTmuxBackend.switch_client — is therefore structurally
unreachable on those builds. The knowledge currently lives only in a CHANGELOG entry.
psmux/psmux#483 is CLOSED, fixed on main (d56d777 + 6c76ff9), but v3.3.7 (2026-07-20) is
still the newest tag and carries the no-op. Two children are gated on the next release.
Children
|
|
|
| #254 |
pipe_pane targets a bare @N → run logs empty on TUI-launched runs |
actionable now |
| #227 |
return_attached_client reports success even when switch_client fails |
actionable now (backend-agnostic — affects tmux too) |
| #228 |
version-gate the rc-0 no-op + document the fallback ceiling |
comment half actionable; version gate needs the tag |
| #222 |
live re-verification of the switch-client paths on the next psmux release |
blocked on the next psmux tag |
Worth doing once, here
#254 offers two shapes: qualify the target inside PsmuxBackend.pipe_pane (smallest diff), or
have new_window return a session-qualified id so every downstream -t consumer is safe. The
second is the class-level fix, and given that this is now the second instance of the same bug,
an audit of the remaining bare @N / %N uses on the psmux path is cheap while someone is in
there.
Refs: psmux/psmux#483, psmux/psmux#482, #221, #225, #185, #217.
Umbrella issue for the psmux target-resolution family. The four children are separate defects on
separate call paths, but three of them are the same root cause and the fourth is the audit that
found them.
Root cause: ids are per-server, and psmux runs one server per session
On tmux a bare
@N(window) or%N(pane) id is server-global and unambiguous. On psmux itis only meaningful on the server that minted it, and the CLI resolves it against the caller's
current-session server. In a normal TUI-launched run there are at least two servers — the
bmad-loop-ctlsession's and the agent session's — with colliding ids (@1exists on both). Soa bare-id
-tfrom inside the ctl pane silently binds to the wrong pane and exits 0.This was already root-caused once, for
switch-client, in #221 / psmux/psmux#483 and fixed in#225 by composing a session-qualified target in the backend that knows its own target grammar.
The fix was applied to one call path, not to the class. #254 is the same bug on
pipe_pane.Compounding factor: the pre-#483 rc-0 no-op
psmux builds without the #483 fix exit 0 as a no-op when
switch-clientgets an unresolvabletarget. Every fallback in this codebase that keys off a nonzero exit code — the POSIX trailer in
adapters/tmux_base.py(switch-client -t "$ret" || switch-client -l), the pwsh override inadapters/psmux_backend.py, andBaseTmuxBackend.switch_client— is therefore structurallyunreachable on those builds. The knowledge currently lives only in a CHANGELOG entry.
psmux/psmux#483 is CLOSED, fixed on
main(d56d777 + 6c76ff9), but v3.3.7 (2026-07-20) isstill the newest tag and carries the no-op. Two children are gated on the next release.
Children
pipe_panetargets a bare@N→ run logs empty on TUI-launched runsreturn_attached_clientreports success even whenswitch_clientfailsWorth doing once, here
#254 offers two shapes: qualify the target inside
PsmuxBackend.pipe_pane(smallest diff), orhave
new_windowreturn a session-qualified id so every downstream-tconsumer is safe. Thesecond is the class-level fix, and given that this is now the second instance of the same bug,
an audit of the remaining bare
@N/%Nuses on the psmux path is cheap while someone is inthere.
Refs: psmux/psmux#483, psmux/psmux#482, #221, #225, #185, #217.