You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
On the psmux backend, per-task pane logs stay empty even though the sink process spawns and the pipe delivers bytes — because pipe_pane targets the bare @N window id returned by new_window, and psmux window ids are per-server (one server per session). When the engine runs inside a bmad-loop-ctl pane (the normal TUI-launched case), its pipe-pane -t @N resolves against the control session's server, not the agent session's server — so the sink gets attached to whatever window happens to hold id @N there (typically the engine's own window), and the agent pane is never piped at all.
This is the same per-server bare-id ambiguity already root-caused for switch-client in #221 (psmux/psmux#483: "a bare pane or window id with no session prefix is only resolved within the current session"). The pipe-pane call path was missed.
Environment
psmux 3.3.7 (stock crates release 05cc5d4, includes the #440 pipe-pane tee rewrite)
bmad-loop 0.9.0, Windows 11, engine launched from the TUI (runs inside a bmad-loop-ctl window)
The only bytes ever captured — 16 bytes, ESC[?9001l ESC[?1004l — arrived at the exact moment the engine's own control-session pane was torn down (its ConPTY reset emission), after which the sink hit EOF and exited. That is only possible if the pipe was bound to the engine's own window, not the dev window.
Both servers in a normal run have colliding ids (e.g. @1 exists on the ctl server and on the agent-session server), so the misbind is silent: pipe-pane exits 0.
Bonus failure mode, reproduced from inside a pane: pipe-pane -t @1 -o 'pwsh "<sink>"' can also fail outright with psmux: no server running on session '-o' (the bare-id target derails argument parsing and -o is taken as a session name). The adapter's best-effort except turns that into a stderr warning that is lost with the parked pane, so the run log is just silently empty either way.
Raw-mode gating was ruled out as the cause: on the same binary, piped panes capture fine during ?9001h (win32-input-mode), ?1049h (alt screen), and 57 KB output bursts.
Root cause
GenericCliAdapter.start_session passes new_window's return value (#{window_id}, a bare @N) straight to mux.pipe_pane. On tmux that 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 (or worse, see the -o parse derail above).
Proposed fix
Session-qualify the pipe-pane target in the psmux backend, mirroring the #221 resolution for the parked-trailer return target: pipe by session:window (verified working on 3.3.7) or =session:@N, composed by the backend that knows its own target grammar. Options:
PsmuxBackend.pipe_pane composes the qualified target itself (it already knows the session via the adapter's target() seam) — smallest diff, tmux path untouched.
new_window on psmux returns a session-qualified id so every downstream -t consumer is safe — broader, worth considering since any other bare-id consumer has the same latent bug (an audit of @N/%N uses on the psmux path is cheap while at it).
Impact
Per-task run logs are empty on psmux for TUI-launched runs (engine inside the ctl session), which also blanks the TUI Log tab and starves the pane-log activity signal used by stall detection.
Completion detection (Stop hook / result artifacts) is unaffected.
Related: #185 (3.3.7 live smoke), #217 (positional sink — still valid, the sink itself is fine), #221 (session-qualified switch-client target), psmux/psmux#483 (bare-id model boundary), psmux/psmux#482
Summary
On the psmux backend, per-task pane logs stay empty even though the sink process spawns and the pipe delivers bytes — because
pipe_panetargets the bare@Nwindow id returned bynew_window, and psmux window ids are per-server (one server per session). When the engine runs inside abmad-loop-ctlpane (the normal TUI-launched case), itspipe-pane -t @Nresolves against the control session's server, not the agent session's server — so the sink gets attached to whatever window happens to hold id@Nthere (typically the engine's own window), and the agent pane is never piped at all.This is the same per-server bare-id ambiguity already root-caused for
switch-clientin #221 (psmux/psmux#483: "a bare pane or window id with no session prefix is only resolved within the current session"). The pipe-pane call path was missed.Environment
bmad-loop-ctlwindow)Evidence (live run forensics)
<log>.sink.ps1, positional invocation from psmux pipe_pane sink captures nothing on 3.3.7: -EncodedCommand transport is stripped upstream, and the CopyTo sink can't flush incrementally #217) spawned at T+2s and held the log open for 16 minutes, capturing 0 bytes, while the agent CLI in the real dev pane repainted continuously (capture-paneshowed the full TUI throughout).ESC[?9001l ESC[?1004l— arrived at the exact moment the engine's own control-session pane was torn down (its ConPTY reset emission), after which the sink hit EOF and exited. That is only possible if the pipe was bound to the engine's own window, not the dev window.@1exists on the ctl server and on the agent-session server), so the misbind is silent:pipe-paneexits 0.pipe-pane -t @1 -o 'pwsh "<sink>"'can also fail outright withpsmux: no server running on session '-o'(the bare-id target derails argument parsing and-ois taken as a session name). The adapter's best-effortexceptturns that into a stderr warning that is lost with the parked pane, so the run log is just silently empty either way.?9001h(win32-input-mode),?1049h(alt screen), and 57 KB output bursts.Root cause
GenericCliAdapter.start_sessionpassesnew_window's return value (#{window_id}, a bare@N) straight tomux.pipe_pane. On tmux that 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 (or worse, see the-oparse derail above).Proposed fix
Session-qualify the pipe-pane target in the psmux backend, mirroring the #221 resolution for the parked-trailer return target: pipe by
session:window(verified working on 3.3.7) or=session:@N, composed by the backend that knows its own target grammar. Options:PsmuxBackend.pipe_panecomposes the qualified target itself (it already knows the session via the adapter'starget()seam) — smallest diff, tmux path untouched.new_windowon psmux returns a session-qualified id so every downstream-tconsumer is safe — broader, worth considering since any other bare-id consumer has the same latent bug (an audit of@N/%Nuses on the psmux path is cheap while at it).Impact
Related: #185 (3.3.7 live smoke), #217 (positional sink — still valid, the sink itself is fine), #221 (session-qualified switch-client target), psmux/psmux#483 (bare-id model boundary), psmux/psmux#482