Repository navigation
Conversation
The working set was written on a 500 ms debounce, so the last change before a quit could be lost, and the shutdown's own PTY kills arrived as session exits that could save an empty set. A confirmed close or quit now writes the set at once before answering main, then stops saving it; main stops sending process-exited once before-quit kills the sessions.
devsuitup
requested changes
Oct 7, 2026
devsuitup
left a comment
Owner
There was a problem hiding this comment.
Review at 82cf905. Three points block; each was reproduced by the reviewer against the shipped code and I re-read the paths.
Blocking
- Quitting while the index is cold, or before the Restore prompt is answered, overwrites the previous saved set with
[](public/app.jsflushStateForExit,persistWorkingSet({ final: true })). Nothing is open yet at that point, so the final write replaces a good set with an empty one. Fix: skip the final write when the restore has not resolved, or keep the saved entries that are not open yet. Test: quit with the saved set unrestored, then check the stored set. - A session stopped on purpose during the restore comes back at the next start. If the user stops a session while the restore is still running and quits before it ends, the unchanged previous snapshot is what stays, so the stopped session is restored again.
- Changes made during the ten-second grace period of an aborted exit are never saved.
schedulePersistWorkingSetreturns early whileexitingAppis set, and whenexitingAppTimerexpires nothing persists again. Fix: persist once when the grace period ends.
Conflicts with #441 (lazy restore)
- The final serializer must keep the dormant entries (
dormantWorkingSet), as #441'spersistWorkingSetdoes; otherwise a quit drops them. - Entries the planner has not resolved yet need the same protection as in the #441 review.
- A dormant entry dismissed during an aborted exit must not be lost. The branch also needs a rebase on current main.
Non-blocking
- Windows logoff and shutdown bypass the new flush (
unsaved-guard.js), so a pending debounce can still be lost. - The CHANGELOG entry must end with
(#479).
79 tests in the seven related files pass locally on Node 24. Not verified: Node 20/22 with c8.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The open working set (
global.openWorkingSet, what restore reopens) is saved on a 500 ms debounce, so:before-quitkills every PTY while the renderer is alive; each kill arrives asprocess-exited→entry.closed→ a scheduled persist that, if the app lingers past 500 ms (e.g. the ActivityWatch flush, up to 1.5 s), saves an empty set.Fix
unsaved-checkhandler awaitsflushStateForExit()(bounded to 2 s) before answering main. It cancels the debounce, writes the set at once, and blocks further working-set writes for 10 s (restored afterwards in case the exit does not happen, e.g. an installer that fails). A reload does neither; an exit during a restore writes nothing, keeping the previous run's set.appQuittingis set beforebefore-quitkills the PTYs, andprocess-exitedis no longer sent after it.Docs:
docs/session-restore.md→ "Closing the app".Tests
test/exit-flush.test.jsloads the shippedapp.jsfunctions: immediate write keeps other global keys; shutdown exits cannot empty the set; persistence resumes after the grace period; an exit mid-restore writes nothing.test/dom-file-panel-unsaved-guard.test.js: confirmed quit flushes before answering, reload does not, cancel does not.test/restore-live-elsewhere.test.jsharness declaresexitingApp.appQuittingguard has no unit test.🤖 Generated with Claude Code