Repository navigation
Conversation
abate
force-pushed
the
dev/lazy-restore-clear-rekey
branch
from
October 5, 2026 10:59
86d05f8 to
620070a
Compare
abate
force-pushed
the
dev/lazy-restore-clear-rekey
branch
from
October 5, 2026 15:15
a0397b4 to
c0609de
Compare
Skills and agents are read-only in a sandboxed session because an unsandboxed claude loads them later. Some workflows need the session to write them, so SWITCHBOARD_SANDBOX_RW_SKILLS=1 and SWITCHBOARD_SANDBOX_RW_AGENTS=1 (Pre-launch Command) make them session state in ~/.claude and every bound .claude. Both stay off by default; docs/sandbox.md states the protection given up.
Review of devsuitup#483: with SWITCHBOARD_SANDBOX_RW_SKILLS/AGENTS, a skills or agents directory linked to a repository elsewhere was bound writable without the protections other writable roots get, so the session could write that repository's hooks or a settings file linked into it. - An opted-in link target is a writable root, so files linked into it from ~/.claude are mounted read-only. - The tree under an opted-in entry, real or linked, gets the same .git and .claude protection as a bound directory, without creating .claude. - The Sandbox tooltip names the opt-in among what is not isolated. - The test rig drops inherited SWITCHBOARD_SANDBOX_* variables, which leaked into the wrapper when the suite ran from a sandboxed session.
…d-only path Linked to the same directory as hooks (or agents, commands), or inside a read-only target such as hooks, plugins or a repository's hooks directory, the writable bind of skills or agents overrode the read-only one and left the protected files writable. The finished mount list is now checked and such a launch refused, whatever order the entries were bound in.
Restoring the working set resumes a claude process per session at startup, which costs a lot of memory. The new 'lazy' mode marks last run's sessions in the sidebar (outlined dot, always visible) and only resumes each one when it is clicked. Unopened sessions stay in the persisted working set across restarts.
Review of devsuitup#441: - openSession clears the dormant marker only after openTerminal succeeds, so a resume refused by guardResume or a failed open keeps the entry dormant; persistWorkingSet keeps a dormant or skipped entry whose open entry is closed. - "Don't restore" in a dormant session's context menu removes it from the saved set without spawning claude. - restore-lazy tests load the shipped functions with loadAppFunctions next to the real sidebar.js, and cover the refused resume, the failed open, the .dormant class, the visibility bypass and the dismiss. - docs/session-restore.md and docs/settings.md describe Restore on click, including the empty grid and no selected session at launch.
With Restore on click, a reload resumed the remembered session without a click, an exited one included: it is now reopened only while its PTY still runs, which is a reattach. Saved sessions the planner has not marked yet are kept by the persist through pendingRestoreEntries, now on main. A project folded for its age opens to show its dormant rows, deleting a dormant session drops it from the saved set, and the previous/next session keys reach dormant rows outside the grid.
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.
…rted one The final write skipped a restore in progress, so a session stopped during it came back, and wrote [] before the saved set was even restored. It now writes through pendingRestoreEntries, which keeps the sessions a cold index, the Restore prompt or the restore still hold, and writes nothing before the saved set is read. A save asked during the grace period of an exit that did not happen is written when it ends. A Windows logoff asks the renderer to save, best effort.
The close button sits next to the strip's controls, and a stray click stopped every session. The window's own close now asks the renderer with reason 'close', which adds a confirm unless the unsaved-edits dialog has already been answered. Quit from the menu or the updater is not asked again.
Once the renderer had acknowledged the check, nothing ended it: a page that hung after the ack held the close and every later quit, SIGTERM included. A close or quit that repeats an acknowledged check now pings the page, and with no answer within 1.5 s ends the check and destroys the window, since a close would wait on the hung page's beforeunload. The 2.5 s bound before the ack destroys it too. The close question is now the in-page choice dialog, with Cancel focused, so Enter keeps the app and the page stays able to answer.
… it apart /clear makes the CLI open a new jsonl under a new session id that names nothing of the one it replaced, so fork detection never matched it: the terminal stayed on the old row and the new conversation appeared as a separate, unattached session. The CLI's state file switches to the new id on /clear; resolve its pid up to the PTY's and re-key the session like a fork.
…ins on it An owner that cannot be established (no /proc: macOS, Windows) was matched when one Claude PTY ran in the folder, so a claude run outside the app could be taken over; it is now never matched. A re-key kept no trace of the old id, so a trigger chain that sent /clear lost its terminal: the trigger context now resolves ids through the re-keys. A pending owner, or a new file whose first records are not written yet, is rechecked a second later while fresh, instead of waiting for another change in the folder. The snapshot-only fork matcher no longer takes a /clear file. A session with no transcript yet is left out of the saved set, which is saved again once its first prompt makes it real.
# Conflicts: # test/restore-pending-persist.test.js
…ekey # Conflicts: # public/file-panel.js # test/dom-file-panel-unsaved-guard.test.js
# Conflicts: # CHANGELOG.md # docs/session-restore.md
abate
force-pushed
the
dev/lazy-restore-clear-rekey
branch
from
October 8, 2026 09:59
bf902c9 to
ff3caa4
Compare
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.
Local test build only, not for merge: current
main(eaf59ff) with the heads of #483 (55ad25a), #441 (f4bd11b), #479 (00783c2), #478 (150b213) and #477 (4a670ad) merged in that order. The merges only resolve overlaps: test harness declarations shared by #441, #477 and #479, theonUnsavedCheckhandler shared by #478 and #479, and the CHANGELOG and docs.🤖 Generated with Claude Code