Skip to content

psmux: the session-level project tag may bleed the same way the window tag did (#310) #320

Description

@pbean

Summary

#310 fixed the window-level project tag on psmux. The session-level one looks to have the
identical bleed, through the identical mechanism, and nothing in #310/#315 touches it.

runs.prunable_sessions decides which bmad-loop-<run-id> agent sessions a prune may kill by
comparing each session's PROJECT_OPTION against runs.project_tag(project). The values come
from BaseTmuxBackend.session_options (adapters/tmux_base.py:163-181):

["list-sessions", "-F", f"#{{session_name}}\t#{{{option}}}"]

That is the same #{@name} format expansion #310 identifies as the window-side bug — it reads
app.user_options, of which psmux has exactly one map per server (types.rs:676;
format.rs:905-906). If a list-sessions row's #{@bmad_project} is evaluated by whichever
server answers the call rather than by the session that owns the row, every row carries one
value and the agent-session prune cannot discriminate projects — the session-level analogue of
the ctl-window bug #310 describes.

PsmuxMultiplexer overrides neither session_options nor set_session_option, so both ride
the base path today. The write side is also ungated: generic.py:433 writes
project_tag(project) — an absolute path — straight through set_session_option, with none of
the _transportable() refusal #315 added for the window channel, so an apostrophe- or
UNC-and-spaced project path can be stored corrupted and then never compare equal.

Blast radius

runs.prunable_sessions is what bmad-loop cleanup / the TUI's prune act on. A mis-scoped read
here is a kill decision on another project's live-or-dead agent session, which is strictly
worse than the ctl-window case: agent sessions host the coding-CLI processes.

Verify first — this is a hypothesis, not a confirmed defect

Three things to establish on a Windows host with psmux 3.3.7 before writing any code:

  1. Does psmux's list-sessions enumerate sessions across all servers, or only the one it
    routes to? (bmad-loop's list_sessions() assumes all — if psmux only answers for one, that
    is a separate and larger problem for prunable_sessions.)
  2. If it does aggregate: is each row's #{@bmad_project} resolved against that row's owning
    server, or against the answering server's single map? Probe: tag two sessions with different
    values, then psmux list-sessions -F '#{session_name}=[#{@bmad_project}]'.
  3. Does a session-scoped set-option -t <session> @k <value> corrupt the same value shapes
    _transportable() refuses? (Almost certainly yes — same CLI→server control line.)

If (2) bleeds, the fix has a ready shape: a session_options override in psmux_backend.py
that reads per session rather than through one format string, plus routing the write through
the same transport gate. Both would sit behind the backend seam, tmux untouched — the same
pattern #315 established.

Refs: #310, #315, #291, #254

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:psmuxpsmux terminal-multiplexer backendbugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions