fix(continuum): honor @continuum-restore option for auto-restore on server start#15
Conversation
…erver start
The `@continuum-restore 'on'` option is documented in the README as
"auto-restore on psmux server start" but was silently ignored.
Root cause
==========
`psmux-continuum` ships two entry points with disjoint feature sets:
* `psmux-continuum.ps1` (lines 163-168) reads `@continuum-restore` and
fires the restore. But PPM's `Initialize-Plugin` (`ppm.ps1` lines
391-398) deliberately skips `.ps1` entry points when `plugin.conf`
exists, because psmux sources `plugin.conf` natively via `@plugin`.
* `plugin.conf` IS sourced, but the auto-restore hook was commented out
with a "Uncomment to enable" note - meaning the option was never read
by any active code path.
Empirical verification on a real install:
$ psmux show-options -g | findstr @continuum
@continuum-restore "on"
$ psmux show-hooks -g | findstr -i "session"
client-attached -> run-shell "...auto_save.ps1 ..."
# no restore hook registered
After-effect: when restore fails to fire on server start, the auto-save
loop (which IS registered) overwrites the `last` pointer with the empty
post-startup state ~15 min later, pruning the recovery target. Reboots
silently lose the saved session.
Second bug found while fixing this
==================================
The commented-out line in plugin.conf used `after-new-session`. This
hook does NOT fire on the first session creation in a fresh psmux
server - it only fires on subsequent sessions. So uncommenting that
line as-is would still have left auto-restore broken after a reboot.
Verified by hook-probing each candidate against a fresh psmux server:
after-new-session => no
session-created => FIRED
client-attached => FIRED
We use `session-created` because it fires for the very first session
in the server's lifetime, which is the moment auto-restore needs to
trigger.
Fix
===
* `plugin.conf`: register the hook unconditionally on `session-created`.
* `auto_restore.ps1`: read `@continuum-restore` at exec time and exit
early if not `on`. This keeps `plugin.conf` simple (no nested
`if-shell` quoting) and evaluates the option at hook-fire time rather
than at config-source time, which is more robust against option-order
dependencies.
* Add `@continuum-restore-fired` one-shot marker. Since `restore.ps1`
itself calls `psmux new-session` for each saved session, the hook
would otherwise re-enter for every restored session.
* Keep the `psmux-continuum.ps1` heredoc byte-identical to the
standalone `scripts/auto_restore.ps1` to preserve the existing
regeneration invariant.
Validation
==========
* `session-created` hook fires on first session - live-probed against
a fresh psmux server.
* `after-new-session` does NOT fire on first session - live-probed.
* Patched `plugin.conf` correctly registers the hook - verified via
`psmux show-hooks -g`.
* Negative path: when `@continuum-restore` is unset or `off`,
`auto_restore.ps1` exits early without setting the fired marker or
invoking `restore.ps1`. Live-tested.
* Both .ps1 files parse cleanly via `[System.Management.Automation.
Language.Parser]::ParseFile`.
* Heredoc body and `scripts/auto_restore.ps1` are byte-identical
(AST-extracted and string-compared).
Full positive E2E (save -> kill-server -> fresh server -> verify
restore) cannot be cleanly run on an isolated `-L pmtest` socket
because `psmux run-shell` doesn't pass socket info to child processes,
so child `psmux` invocations default to the user's real socket and
contaminate the test. The fix is correct for default-socket production
usage; the testability limit is a psmux internal, not a fix bug.
Note: PR psmux#13 (fix(continuum): singleton-guard auto-save, drop
Start-Job) touches the same files. This PR's diff is orthogonal to
that work and trivially rebases either way.
Honor @continuum-restore option for auto-restore on server start. Tracks upstream PR psmux#15.
There was a problem hiding this comment.
Pull request overview
Fixes auto-restore for psmux-continuum: the @continuum-restore option was documented but never honored because plugin.conf (the only entry point sourced when present) had the restore hook commented out. This PR registers the hook unconditionally and moves the opt-in check inside auto_restore.ps1, also switching from after-new-session (which doesn't fire for the very first session in a fresh server) to session-created.
Changes:
plugin.conf: registersession-createdhook unconditionally to invokeauto_restore.ps1.auto_restore.ps1(and the byte-identical heredoc inpsmux-continuum.ps1): gate execution on@continuum-restore == 'on', and use a@continuum-restore-firedone-shot marker to prevent re-entry whenrestore.ps1callsnew-sessionfor each saved session.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| psmux-continuum/plugin.conf | Replaces commented after-new-session hook with an active session-created hook delegating opt-in to the script. |
| psmux-continuum/scripts/auto_restore.ps1 | Adds Get-PsmuxBin resolver, opt-in check on @continuum-restore, and one-shot guard via @continuum-restore-fired. |
| psmux-continuum/psmux-continuum.ps1 | Updates the heredoc that generates auto_restore.ps1 to remain byte-identical with the standalone artifact. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Thank you for the PR, @MattKotsenas. Much appreciated. Everything looks good to me. I’ll go ahead and merge it. |
Summary
set -g @continuum-restore 'on'is documented inpsmux-continuum/README.mdas "auto-restore on psmux server start", but the option is silently ignored. Auto-save works; auto-restore never fires. After a server restart users get a fresh empty session, and ~15 min later the auto-save loop overwrites thelastpointer with that empty state, pruning the recovery target.This bit me after an unexpected reboot; saved session was on disk,
lasthad been clobbered to point at an empty save, and nothing fired on startup.Root cause
Two entry points with what appears to be disjoint feature sets:
psmux-continuum.ps1plugin.conf@continuum-restore@continuum-save-interval15@continuum-bootppm.ps1(lines 391–398) deliberately skips.ps1entry points whenplugin.confexists, so the only code that reads@continuum-*options never runs. Onlyplugin.confis sourced and it has the restore hook commented out.Empirical evidence on a real install:
Second bug found while fixing this
The commented-out line uses
after-new-session. Live hook-probe on a fresh psmux server:after-new-sessiondoes not fire on the first session creation in a fresh server, only on subsequent ones. So simply uncommenting that line would still have left auto-restore broken after one scenario where it really matters (server restart). This PR usessession-createdinstead.Fix
plugin.conf: unconditionally register thesession-createdhook ->auto_restore.ps1.auto_restore.ps1: read@continuum-restoreat exec time, exit early if noton. Keepsplugin.conffree of nestedif-shellquoting and evaluates the option at hook-fire time (robust to option-set ordering vs. config sourcing).@continuum-restore-firedone-shot marker. Without this guard the hook re-enters for everypsmux new-sessioncall thatrestore.ps1itself makes.psmux-continuum.ps1kept byte-identical toscripts/auto_restore.ps1to preserve the existing regeneration invariant.Validation
session-createdfires on first session - live-probed.after-new-sessiondoes not fire on first session - live-probed.plugin.conf- verified withpsmux show-hooks -g.@continuum-restoreis unset oroff,auto_restore.ps1exits early without invokingrestore.ps1or setting the fired marker - live-tested..ps1files parse cleanly ([System.Management.Automation.Language.Parser]::ParseFile).scripts/auto_restore.ps1are byte-identical (AST-extracted + string-compared).Overlap with PR #13
PR #13 (singleton-guard auto-save) touches the same files. This PR's diff is orthogonal and trivially rebases either way.