Repository navigation
resurrect + continuum persist unnamed sessions, recreating them on every server start #37
Description
Repo: psmux/psmux-plugins (psmux-resurrect, psmux-continuum)
psmux: 3.3.8 (66cf613, 2026-08-18), installed via winget
Windows: 11 Pro 26200 · PowerShell 7.6.5 and Windows PowerShell 5.1
Summary
psmux-resurrect saves every live session including unnamed ones — the 0, 1, 2
that psmux assigns when you run a bare psmux/tmux. With
@continuum-restore 'on', those get recreated on every server start, so a session
you never deliberately made becomes permanent.
The part that makes it more than cosmetic is that it cannot be cleaned up by hand.
Deleting session 0 from the save files fixes nothing: as soon as a stray 0 exists
again for any reason, the next auto-save writes it straight back and every subsequent
restore recreates it. Save → restore → save is a closed loop with no exit.
Practical effect: you type tmux, land in session 0, and your real workspace is
restored beside you rather than under you. Every session, forever.
Reproduction
# 1. Start from a clean server
tmux.exe kill-server
# 2. Bare invocation -> psmux names the session "0"
tmux.exe # (detach)
# 3. Let continuum's auto-save run, or force it
pwsh -NoProfile -File "$env:USERPROFILE\.psmux\plugins\psmux-resurrect\scripts\save.ps1"
# 4. The save now contains session "0"
$last = (Get-Content "$env:USERPROFILE\.psmux\resurrect\last" -Raw).Trim()
(Get-Content $last -Raw | ConvertFrom-Json).sessions.name
# -> 0
# <your real sessions>
# 5. Remove "0" from every save file by hand, then restart the server.
# It comes back the moment anything creates an unnamed session again.Observed here across a working day: next_session_id reached 48, and the process
tree confirms restore is the thing recreating it —
psmux.exe server -s 0 -d C:\Users\rishi -n pwsh <- recreated by restore.ps1
psmux.exe server -s claude-code -d C:\Users\rishi -n claude
Both spawned by restore.ps1, identical shape, from a save whose 0 entry I had
already deleted once.
Root cause
psmux-resurrect/scripts/save.ps1:57 enumerates every session with no filter:
$sessionLines = (& $PSMUX list-sessions -F '#{session_name}' 2>&1) | Out-String
...
foreach ($line in ($sessionLines -split "`n")) {
$sessionName = $line.Trim()
if ([string]::IsNullOrWhiteSpace($sessionName)) { continue }
# no check for auto-assigned namesAn unnamed session carries no user intent — the name is an implementation detail of
how psmux numbers sessions. tmux-resurrect has the same behaviour upstream, but it
bites harder here because psmux spawns internal helper sessions (__warm__) and a
bare psmux is a much more common entry point on Windows, where users reach for the
plain command rather than a shell alias.
Suggested fix
Skip auto-named sessions at save time, behind an option so anyone relying on the
current behaviour can opt back in:
# save.ps1, inside the foreach at :57
if ([string]::IsNullOrWhiteSpace($sessionName)) { continue }
# Auto-assigned names carry no user intent, and persisting them makes them
# permanent: restore recreates them, the next save re-persists them.
if ($saveUnnamed -ne 'on' -and $sessionName -match '^\d+$') { continue }with $saveUnnamed read once before the loop:
$saveUnnamed = (& $PSMUX show-options -gv '@resurrect-save-unnamed' 2>&1 | Out-String).Trim()Filtering at save time rather than restore time is the important part — restore-side
filtering still leaves the poisoned record on disk for anything else that reads it.
Internal sessions (__warm__) are worth excluding on the same pass.
Workaround, for anyone who finds this first
Wrap save.ps1 and strip unnamed sessions from the result, then point the hooks at the
wrapper instead of the plugin script. Note that @continuum-save-interval appears to be
non-functional — auto_save.ps1 only reads -IntervalMinutes as a param() and never
looks the option up — so changing cadence means overriding the client-attached hook,
and that override must sit below the run '~/.psmux/plugins/ppm/ppm.ps1' line in
psmux.conf or ppm clobbers it when it sources each plugin's own plugin.conf.
Happy to open a PR for the save-side filter if the approach looks right.
Activity
fix(continuum): honour @continuum-save-interval in the auto-save loop…
psmux commentedon Sep 14, 2026
Hey @CodedRichy, thank you for this one. The root cause section was exactly right and the closed loop you described (save, restore, save) is what makes it worse than cosmetic, so I reproduced it end to end before touching anything.
Reproduction on 3.3.8 (7ffcb3d), isolated data root
new-session -dwith no name (what a barepsmuxdoes) plus a namedworksave.ps1wrote both:SAVED SESSION NAMES: [0, work]kill-server, thenrestore.ps1:Restored session: 0 (1 windows)save.ps1again:0is back in the saveSo the loop is real and it only takes one bare launch to seed it.
What landed (01c03ab, on main now)
The filter lives at save time, as you argued, behind
@resurrect-save-unnamed:auto(default): an auto-named session is saved only once you have shaped it, meaning a second window or pane, or a program other than an idle shell in its pane. An untouched0is what the next bare launch gives you anyway, so it is left out.on: save every auto-named session, which is what tmux-resurrect does and what the plugin did until today.off: never save auto-named sessions, your proposal as written.I went with
autorather thanoffas the default because a lot of people do real work in session0(baretmuxis the most common entry point there too), and silently dropping a session with six windows because of its name would trade your bug for theirs. The untouched shape is the one that carries no intent, and it is the shape every stray0in your process tree had.Sessions whose names start with
__(the__warm__standby) are never saved regardless of the setting. When every running session was filtered out the save now says so explicitly and does not repointlast, instead of the old "server likely down" line. The save toast counts what was skipped.Verified the same way: the untouched
0stays out of the save, a split1and named sessions still round trip, a cold restore brings back1andworkonly, and the save taken right after the cold restore is still clean. All of that is pinned intests/test_resurrect_save_unnamed.ps1against the real binary in a privatePSMUX_DATA_DIR.Your
@continuum-save-intervalside note was also correctThe option was documented but the hook runs
auto_save.ps1 -IntervalMinutes 15and the script never read it. I confirmed that the hook's exact invocation with the option set to1produced no save in 80 seconds. baab0f9 makes the loop read@continuum-save-intervalat the top of every lap: a number wins over the parameter,0stops the loop, and a change made on a running server takes effect without restarting anything. Same 80 second probe after the fix: one save written. So you no longer need to override the hook, and the ppm ordering trick is not needed for cadence any more. (Sourcing order is otherwise as you described and intentional: ppm applies each plugin'splugin.conf, and anything you want to override has to come after the ppm line.)One thing worth knowing while you test: psmux runs one server process per session, so a
set -gtyped at runtime only reaches the session it targets. Put the options in~/.psmux.confand they apply to every session as it starts. I added that note to the README as well.Prefix + Uin ppm pulls the update. If a stray0still comes back after that, please tell me whatpsmux lsand the save file show and I will dig in again.