Skip to content

Multiplexer interoperability: behavior matrix and contract tests - #204

Merged
raiseCatError merged 3 commits into
devfrom
feature/175-mux-interop
Sep 29, 2026
Merged

raiseCatError merged 3 commits into
devfrom
feature/175-mux-interop

Conversation

@raiseCatError

@raiseCatError raiseCatError commented Sep 29, 2026 •

Copy link
Copy Markdown
Owner

Closes #175

#175 is a research issue: "deliver a concise behavior matrix and prioritized implementation follow-ups … NMSh is not becoming a pane/window multiplexer; the outcome is reliable coexistence." This PR delivers that. No product behavior changes.

Deliverables

  • docs/architecture/multiplexer-interop.md: a behavior matrix for NMSh inside tmux and GNU screen and for each running inside NMSh, the persistence-overlap analysis, the detection decision, and prioritized follow-ups.
  • tests/muxInterop.test.ts: pins the verified contract with real tmux and screen, skipping when they're not installed, so CI never depends on a multiplexer. Plus environment-only host-detection cases for tmux, screen and Zellij.

Verified (tmux 3.7c, GNU screen 4.00, isolated sandboxes)

  • NMSh inside tmux:
    • the shell sees TERM=tmux-256color, TERM_PROGRAM=tmux and TMUX;
    • resize propagates (the pane minus the composer rows);
    • Ctrl+Z and jobs work; less passes through and returns;
    • a multi-line paste-buffer -p stays a paste;
    • closing the pane detaches the live session, which stays restorable.
  • NMSh inside screen: rendering, resize, Ctrl+Z, and less with its return all work.
  • screen and tmux inside NMSh: they pass through via the alternate screen and return, and tmux repaints after reattach (existing test).
  • A live session keeps its creation environment: made in tmux and reattached outside it, the shell still has TERM=tmux-256color, TERM_PROGRAM=tmux and a stale TMUX. This is the main interop wrinkle and is documented, not changed.

Not verified (physical QA or follow-ups)

  • Zellij and TUIOS: not installed, so marked unverified.
  • Mouse under tmux: needs mouse on.
  • Shift+Enter under tmux: tmux send-keys can't produce real extended keys. NMSh decodes both extended encodings, but tmux's default settings likely deliver Enter.
  • /copy via pbcopy inside tmux.

Detection

Baseline coexistence needs no multiplexer detection; nothing was added. #197's window launcher keeps its current behavior: inside tmux started from Ghostty, it detects Ghostty through the inherited GHOSTTY_RESOURCES_DIR, so "Open all" opens Ghostty windows outside tmux. That's documented and tested, and follow-up 3 decides whether to change it.

Prioritized follow-ups (in the doc)

  1. A reattach notice when the attaching environment differs from the session's (P1).
  2. Request modifyOtherKeys alongside kitty, so Shift+Enter works under tmux extended-keys on (P1).
  3. "Open all" from inside a multiplexer (P2).
  4. A Zellij pass (P2).
  5. Recommended tmux settings in the README (P3).

Also

Tests

  • tests/muxInterop.test.ts passed 4/4 repeatedly, including alongside liveHardening and ctrlZJobControl, with no tmux, screen or nmshd processes left behind.
  • Build, typecheck and git diff --check are clean. The full suite passed 595/595 locally.

Review cleanup (bf51095)

  • Every matrix row now states its evidence: automated test, manual observation or expected. The test file is described as coverage for selected tmux/screen paths.
  • New tests:
    • closing only the tmux pane (server still running) detaches the live session;
    • NMSh inside GNU screen renders, sees STY, follows a resize and suspends a job;
    • an alternate-screen program gets the full terminal in passthrough;
    • the stale-environment test proves the tmux server is gone.
  • tmux runs with multiplexer variables stripped from the inherited environment.
  • The screen-inside-NMSh test now quits its screen session, which detaches instead of exiting when its terminal closes.
  • Corrected claim: screen 4.00 keeps the window size it started with, so inside NMSh it keeps the composer-reduced height. It had been wrongly described as corrected by SIGWINCH. Added follow-up 6: hand known multiplexers the full terminal before they start.
  • vim and fg under tmux, and less under screen, are marked manual. Stale TMUX/TERM is documented as affecting later child commands, while the attaching frontend's host detection uses its own environment.
  • CI: the first two runs hit two pre-existing flakes in tests this PR doesn't touch (Live sessions: attach, detach and reattach #128's reattach-repaint test and the Ctrl+Z fg then Ctrl+C step). They'll be fixed in a stacked reliability PR. The rerun is green on Node 22 and 26.

Research for #175: a behavior matrix for NMSh inside and around tmux and
GNU screen, verified on tmux 3.7c and screen 4.00 in isolated sandboxes,
with Zellij and TUIOS marked unverified. It covers rendering, resize
propagation, job control, fullscreen passthrough, bracketed paste, mouse
and key encodings, clipboard, and how multiplexer persistence and NMSh
live sessions overlap: a closed pane detaches the live session, and a
reattached session keeps the environment it was created in.

Baseline coexistence needs no multiplexer detection. Prioritized
follow-ups cover a reattach notice for environment changes, Shift+Enter
under default tmux settings, Open all from inside a multiplexer, a
Zellij pass, and recommended tmux settings.

tests/muxInterop.test.ts pins the verified contract with real tmux and
screen where installed and skips otherwise, plus host-detection cases for
tmux, screen and Zellij environments.
The end-to-end status test typed /resume while 'sleep 30 &' was still
finishing and NMSh correctly refused to switch transcripts mid-command.
Wait for the command to complete first.
Each row of the multiplexer matrix now states its evidence: automated
test, manual observation or expected. The test file is described as
coverage for selected tmux/screen paths, not the whole contract.

Tests added where they fit the existing harness: closing only the tmux
pane (server still running) detaches the live session; NMSh inside GNU
screen renders, sees STY, follows a resize and suspends a job; a program
on the alternate screen gets the full terminal in passthrough; and the
stale-environment test confirms the tmux server is gone. tmux runs with
multiplexer variables stripped from the inherited environment, and the
screen-inside-NMSh test quits its screen, which detaches rather than
exiting when its terminal closes.

Corrected: screen 4.00 keeps the window size it started with, so inside
NMSh it keeps the composer-reduced height (new follow-up 6); it was
wrongly described as corrected by SIGWINCH. vim, fg and less inside
screen are marked as manual. Stale TMUX/TERM in a reattached shell is
documented as affecting later child commands, while the attaching
frontend's host detection uses its own environment.
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.

1 participant