Umbrella issue for the psmux target-resolution family. The five children are separate defects on
separate call paths, but four of them are the same root cause — psmux reporting execution rather
than effect — and the fifth 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 |
| #317 |
the seam's switch_client / detach_client booleans are vacuously True — return_attached_client trusts them |
latent until #310 lands, then live |
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 five children are separate defects on
separate call paths, but four of them are the same root cause — psmux reporting execution rather
than effect — and the fifth 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_clientfailsswitch_client/detach_clientbooleans are vacuously True —return_attached_clienttrusts themWorth 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.