Repository navigation
Description
What happens
Loading a saved session (Sessions → Load) replaces every workspace through replaceAllWorkspaces in src/renderer/store/workspace-slice.ts. Unlike closeWorkspace and the other close paths fixed in #65, it doesn't call killTreeTerminalPtys or killSurfacePty for the workspaces it drops, and it doesn't fire wmux:surface-closed.
So every terminal in the current layout whose surface id isn't in the loaded session keeps its shell running in main with no xterm attached, plus anything that shell started: a dev server, a build watcher, an agent. Nothing shows it, and nothing reaps it until wmux quits. The per-surface renderer state that listens for wmux:surface-closed isn't dropped either.
Surfaces whose ids are also in the loaded session are a different case. Since #346 they keep their existing terminal: the keep-alive parks it and the new layout claims it, which looks intended. One side effect: if that pane's shell has already exited, it stays on [process exited], and the loaded session's shell, cwd and startup commands for it are ignored.
Steps to reproduce
Found by reading the code on master. I haven't reproduced it in the app. Expected reproduction:
- In a terminal pane, start a long-running process, e.g.
ping -t localhostor a dev server. - Save the current layout as a session if you need one to load, then change the layout so that pane's surface isn't in the session you're about to load. Alternatively, load a session saved in an older layout.
- Sessions → Load that session.
- Expected: the dropped pane's shell and its process are ended, as closing the workspace would. Likely actual:
ping.exe(or the dev server) is still running in Task Manager, still holding its port.
Possible fix
In replaceAllWorksp
7FEF
aces (or in handleLoadSession, if the startup restore paths should stay as they are), collect the terminal surface ids of the outgoing workspaces, and for each one that isn't in the incoming split trees, call killSurfacePty, which also announces the close. The startup callers (App.tsx, the auto-save and default-session restore) have nothing to drop, because the store starts empty, and that includes after a renderer reload. So this fix can't reap PTYs that a renderer reload leaves running in main. That would need main to list the PTYs this window owns, and is probably a separate issue.
wmux version
2.40.0
Anything else
Related: #65 (the close paths), #139 (process trees orphaned by an abnormal exit). tryReplaceTabSpawn in App.tsx also kills a PTY directly without announcing the close. Noticed while working on #360, which stops a closed pane's snapshot being replayed when Load session brings its surface id back.
Activity
amirlehmam commentedon Oct 25, 2026
Shipped in v2.41.0.
fix(session): Load session ends the shells of the panes it drops (ami…