8000 0.42.0: V2 commands resolve to the wrong window once a second window exists, and unknown flags silently execute with defaults · Issue #143 · amirlehmam/wmux · GitHub
[go: up one dir, main page]

Skip to content

0.42.0: V2 commands resolve to the wrong window once a second window exists, and unknown flags silently execute with defaults #143

Closed

Description

@Ray0483

wmux version: 0.42.0
OS: Windows 11 Pro 10.0.26200
Shell in pane: powershell.exe

Two CLI correctness problems found while verifying the #141 fix (which is fixed — details in a comment there). Both matter mainly to scripts and agents driving wmux from inside a pane.


1. WMUX_SURFACE_ID resolves to the wrong window once a second window exists

The 0.42.0 release note says "V2 commands now use caller's WMUX_SURFACE_ID to identify owning window". With a single window this appears to work. As soon as a second window exists, passing WMUX_SURFACE_ID makes commands resolve to a constant, wrong window — and the caller's own surface is not in the result.

Setup

  • Caller shell lives in surf-360768cf-…, in pane pane-c36f0826-…, in workspace "Workspace 3", in window win-fda7feb5-….
  • A second window win-2806cd6c-… was opened with wmux new-window.
  • There is also a workspace "Session 1" containing three terminal panes (pane-f45f55c3, pane-b11e7cf2, pane-89504319).

Observed

Focused window Call Panes returned by tree Contains caller's pane c36f0826?
original wmux tree (with WMUX_SURFACE_ID) f45f55c3, b11e7cf2, 89504319 ❌
original wmux tree (env var removed) c36f0826, 48d68481 ✅
new wmux tree (with WMUX_SURFACE_ID) f45f55c3, b11e7cf2, 89504319 ❌
new wmux tree (env var removed) f45f55c3, b11e7cf2, 89504319 ❌

Grepping the WMUX_SURFACE_ID result for the caller's own surface id returns 0 matches. The result is byte-stable across focus changes: it always resolves to "Session 1", a workspace the caller is not in.

So the env var makes resolution worse: removing it at least follows the focused window.

tree and list-surfaces now agree with each other (that part of #141 is fixed) — they are simply wrong together.

Reproduce

# from a shell inside a wmux pane, with at least one other workspace present
echo "$WMUX_SURFACE_ID"          # note this id
wmux tree                        # ✓ shows your pane
wmux new-window
wmux tree                        # ✗ different window; your surface id is absent
env -u WMUX_SURFACE_ID wmux tree # ✓ shows your pane again

Why it matters

A script running inside a pane always has WMUX_SURFACE_ID set — there is no way to opt out short of scrubbing the environment. So any agent or script that inspects or mutates surface state starts operating on someone else's window the moment the user opens a second one, and it looks successful. Scrubbing the variable is not a fix either, since the result then depends on which window happens to be focused.

What I would expect: caller should resolve to the window/workspace that actually contains that surface id, and fall back to the focused window only when the id is unknown.


2. There is no --help, and unknown flags are silently ignored — the command runs anyway

wmux new-surface --help    # creates a terminal surface
wmux split --help          # splits the pane

getFlag() in resources/cli/wmux.js picks out the flags it knows and ignores everything else, so a typo or an exploratory flag becomes "run with defaults". Only bare wmux prints usage.

This is a small thing for a human, but for an agent it means probing the CLI mutates the user's layout, and the JSON response looks like success. I hit it while investigating issue 1 and accidentally created a surface and split a pane.

Suggestion: reject unknown flags with an error and print that subcommand's usage, and/or support --help / -h per subcommand.

Activity

  1. amirlehmam commented on Aug 4, 2026

    @amirlehmam
    Owner

    Thanks — both are real, and the first one was worse than it looked. Fixed in #144, shipping in v0.43.0.

    1. Caller resolution

    Your diagnosis was right, and there were two independent bugs stacked on top of each other.

    The window was resolved; the workspace was not. windowForCaller asked each window "do you hold this surface?" and stopped at the first yes. But that check scans every workspace in a window, while tree / list-panes / list-surfaces answer about the active one. So resolution could be entirely right about the window and still describe a workspace you had never seen — which is exactly your "always resolves to Session 1, a workspace the caller is not in".

    Resolution now returns the surface's window and its workspace, and that workspace is the default for every method that would otherwise guess. --workspace still wins when you pass it. This applies with one window too: a caller sitting in a background workspace was already getting the wrong answer before any second window existed.

    The cache was authoritative. Once a caller probed successfully it was pinned to that window for the rest of the session, which is why you saw a byte-stable result across focus changes. It is now only a probe-order hint — a stale entry heals on the next call.

    And your surface id existed in two windows at once. This is what made the wrong window win the probe. A window opened mid-run gets no session slot, which the renderer read as "nothing saved" and answered by cloning the newest named session — same workspace, pane and surface ids as the window that already had them. Since PTY id is surface id in wmux, that clone's terminals were also re-attaching to the original window's PTYs. session:load-auto now says "come up empty" explicitly for mid-run windows instead of leaving it to be inferred.

    Behaviour now matches what you asked for: resolve to the window/workspace that actually contains the surface id, and fall back to the focused window only when the id is unknown.

    While in there, close-surface / focus-surface / rename-surface / close-pane now find their target anywhere in the window rather than only in the active workspace. Ids are uuids so the search can't hit the wrong thing, whereas the old scope silently did nothing and still reported ok.

    2. --help and unknown flags

    Agreed, and sorry about the surface and the split you didn't ask for.

    Every command now declares its flags. Anything else is an error with that command's usage, and --help / -h prints usage without sending a single byte to the pipe:

    $ wmux split --help
    wmux split [--down] [--type terminal|browser|markdown] [--color-scheme NAME]
    
    $ wmux split --bogus
    Unknown flag for 'split': --bogus
    
    wmux split [--down] [--type terminal|browser|markdown] [--color-scheme NAME]
    

    A value flag with no value is rejected too (wmux tree --workspace → Flag '--workspace' needs a value.) — that was the same silent default in a different costume.

    One deliberate exception: commands whose arguments are free-form text or belong to another program — send, notify, log, ssh, browser, markdown, rename-surface, rename-workspace — are not flag-checked, because wmux send --help has to type --help into the pane rather than print usage. For those there is now wmux help <command>, which works for every command:

    $ wmux help send
    wmux send [--surface <id>] <text>
    

    The command table is type-checked against the usage table, so a command can no longer ship without usage text.

    Thanks for testing #141 through and finding these — the "it looks successful" framing is what made the first one worth digging past the symptom.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      0