8000 fix(cli): scope V2 commands to the caller's workspace, reject unknown flags (#143) by amirlehmam · Pull Request #144 · amirlehmam/wmux · GitHub
[go: up one dir, main page]

Skip to content

fix(cli): scope V2 commands to the caller's workspace, reject unknown flags (#143) - #144

Merged
amirlehmam merged 1 commit into
masterfrom
fix/143-caller-window-scope
Aug 4, 2026
Merged

amirlehmam merged 1 commit into
masterfrom
fix/143-caller-window-scope

Conversation

@amirlehmam
Copy link
Copy Markdown
Owner

Fixes #143.

1. WMUX_SURFACE_ID resolved to the wrong window

windowForCaller asked each window "do you hold this surface?" and stopped at the first yes. But __wmux_hasSurface scans every workspace in a window, while __wmux_getTree / listPanes / listSurfaces answer about the active one — so resolution could be right about the window and still describe a workspace the caller had never seen.

Resolution now returns the surface's window and its workspace (__wmux_locateSurface), and that workspace becomes the default for the methods that would otherwise guess. An explicit --workspace still wins.

The window cache was also authoritative, so one outdated hit pinned a caller to that window for the rest of the session — the "byte-stable across focus changes" wrong answer in the report. It is now only a probe-order hint, so a stale entry heals on the next call.

The duplicate ids underneath

Surface ids were not unique across windows. 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. PTY id is surface id in wmux, so the clone's terminals re-attached to the original window's PTYs, and every id-based lookup had two equally valid answers.

session:load-auto now distinguishes "nothing saved" (null, launch windows only) from "come up empty" ({ fresh: true }) — the contract per-window restore has claimed since #118, now actually enforceable on the renderer side.

Id-targeted operations (close-surface, focus-surface, rename-surface, close-pane) also now find their target anywhere in the window instead of only in the active workspace. Ids are uuids, so the search cannot hit the wrong thing — whereas the old scope silently did nothing and still reported ok.

2. Unknown flags ran the command anyway

getFlag() picked out the flags it knew and ignored the rest, so wmux new-surface --help created a surface and wmux split --help split a pane.

Every command now declares its flags. Anything else is rejected with that command's usage, and --help / -h prints usage without touching the pipe. A value flag with no value is rejected too — same silent default, different costume.

Commands whose arguments are free-form text or belong to another program (send, notify, log, ssh, browser, markdown, rename-*) are exempt, because --help there is part of what you are sending; wmux help <command> works for those. COMMANDS is typed against the spec table, so the compiler catches a command shipped without usage text.

$ 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]

$ wmux tree --workspace
Flag '--workspace' needs a value.

wmux tree [--workspace <workspaceId>]

Verification

  • npx tsc --noEmit clean for both projects; npm run lint shows no new findings.
  • 720 unit tests pass, including 15 new ones: tests/unit/caller-scope.test.ts covers wrong-window selection, background-workspace scoping, stale-cache healing, unknown-surface fallback and mid-probe window death; session-windows.test.ts covers restoreAnswerFor.
  • CLI help / rejection paths exercised against the built dist/cli/wmux.js (they exit before any pipe I/O).
  • The multi-window end-to-end path is covered by the resolver unit tests, not by a live two-window run.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Y7nJeuxL4z9Yi5NL2KuhCV

… flags (#143)

Two CLI correctness problems, both of which made a probe or a query look
like it had succeeded while describing someone else's window.

WMUX_SURFACE_ID resolved to the wrong window
--------------------------------------------
`windowForCaller` asked each window "do you hold this surface?" and stopped
at the first yes. But `__wmux_hasSurface` scans every workspace in a window
while `__wmux_getTree`/`listPanes`/`listSurfaces` answer about the *active*
one — so resolution could be right about the window and still describe a
workspace the caller had never seen. It now resolves the surface to its
window AND its workspace (`__wmux_locateSurface`), and that workspace becomes
the default for the methods that would otherwise guess. An explicit
--workspace still wins.

The window cache was also authoritative, so one outdated hit pinned a caller
to that window for the rest of the session — the "byte-stable across focus
changes" wrong answer in the report. It is now only a probe-order hint, so a
stale entry heals on the next call.

Underneath that, surface ids were not unique across windows. 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. PTY id is surface id, so the
clone's terminals re-attached to the original's PTYs, and every id-based
lookup had two equally valid answers. `session:load-auto` now distinguishes
"nothing saved" (null, launch windows only) from "come up empty"
({ fresh: true }), which is the contract per-window restore has claimed since
issue #118.

Id-targeted ops (close/focus/rename surface, close pane) now find their
target anywhere in the window instead of only in the active workspace. Ids
are uuids, so the search cannot hit the wrong thing — whereas the old scope
silently did nothing and still reported ok.

Unknown flags ran the command anyway
------------------------------------
`getFlag()` picked out the flags it knew and ignored the rest, so
`wmux new-surface --help` created a surface and `wmux split --help` split a
pane. Every command now declares its flags; anything else is rejected with
that command's usage, and --help/-h prints usage without touching the pipe.
A value flag with no value is rejected too — same silent default, different
costume. Commands whose arguments are free-form text or belong to ssh are
exempt (--help is part of what you are sending); `wmux help <command>` works
for those. COMMANDS is typed against the spec table, so the compiler catches
a command shipped without usage text.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y7nJeuxL4z9Yi5NL2KuhCV
@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 524d23c37c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cli/wmux.ts
Comment on lines +760 to +763
if (i === args.length - 1 && arg !== '--blocked') {
fail(command, spec, `Flag '${arg}' needs a value.`);
}
i++;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject another flag where a value is required

When a value-taking flag is followed by another flag, this check only verifies that some token exists and then skips that token. For example, wmux split --type --bogus bypasses the unknown-flag rejection, after which getFlag() treats --bogus as the surface type and the command still mutates the layout. Treat a following --... token as a missing value before advancing, while handling the genuinely optional --blocked argument separately.

Useful? React with 👍 / 👎.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

1 participant

0