Reopen closed Surfaces, Workspaces, and windows (⌘⇧T / u / dor reopen) - #967
Merged
Merged
Conversation
dor send and a remote Client's input now clear `untouched` (the sidecar and VS Code host tell the webview with terminal:clientInput). An untouched shell that is running, or whose helper holds user input or has not been answered for, confirms. The Workspace close asks whenever any member's own close would (closeKind), replacing hasTouchedSurfaces || runningCount. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A reopenable close pushes a record (persistable leaf meta, the PersistedPane projection, the Pane's restore token or the Door's slot, the Workspace id) onto a per-Window stack in memory, newest first, capped at 20. Reopen pops it and rebuilds the Surface through the cold-restore per-pane path, now extracted as restorePane, with a new surface:N ref; a Surface whose Workspace closed lands in the active one. Command-mode u, the macOS File menu's Reopen Closed (Cmd+Shift+T, macOS only), and dor reopen (window.reopen) take it; an empty stack shows a brief notice or refuses. No kind is reopenable yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…asked An iframe browser, a running builtin:folder, and a running builtin:file that has reported clean now close without the letter prompt and push a reopen record; every other Tool, agent-browser and playwright browser, and any shell with work still confirms. A read-only builtin:file view reports clean as it starts, since it never can change; an editor reports once it loads. A non-confirming kill on a Door closes it as a Door, handing selection to its next neighbor, so it reopens on the Baseboard. Before this, every browser close confirmed (no `untouched` notion). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The VS Code adapter importing terminal-lifecycle for it pulled the whole terminal lifecycle into the platform module's graph, which broke tests that mock the platform. It only touches a registry entry, so it lives in terminal-store now; terminal-registry still re-exports it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Workspace close that asks nothing — every member trivial or reopenable — records the Workspace whole (its record with reported cwds, name, and strip slot) once it is gone; its members push no records of their own. Reopen brings it back as a new Workspace at that slot through cold restore, every Surface with a fresh id and refs starting over. One member whose close would ask makes the close confirm and leaves no record. dor workspace close's refusal now says the Workspace holds Surfaces Reopen cannot restore. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A window close asks when any of its Workspaces' closes would, instead of on running commands alone. One that asks nothing hands Rust its snapshot — every Workspace and Surface given a fresh id — before removing it from disk; Rust keeps up to 20 in memory, newest first. Reopen asks the host first: the newest closed window that closed after this Window's own newest record is written under a new label and opened as a new window, restoring like any other. Otherwise the Window's own stack answers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Tool row persisted its leaf title, which the iframe layer sets to the URL it shows. Restore and Reopen apply a persisted title as the user's, so a reopened or cold-restored builtin:file read "localhost:51526/…/view" instead of the file name its viewer announces. Only a user rename persists now; pane and Door alike. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reopen's command-mode u must never answer a confirmation staged for a different close. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
reopen.md now states the built rule, kinds table, record, verb, and Workspace/window behavior in the present tense with their pointers; the remaining Future is Scope: delayed-kill. Its decisions move to a new reopen.rationale.md. The promotion checklist edits: layout.md Kill confirmation (which closes skip it, the counted inputs, u out of the alphabet) and Workspace lifecycle; shortcuts.md (u, Cmd+Shift+T); the glossary's reopen verb; dor-cli.md's dor reopen; standalone.md's Per-window close and File menu; transport.md's terminal:clientInput; dor-tool.md's Tool title persistence; dor-tools-builtin.md's clean report. Budgets: reopen.md ratchets down to 1200. dor-cli (+100), dor-tool (+50), dor-tools-builtin (+50), glossary (+50), and shortcuts (+50) grow by the new verb, row, and rules they must now carry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ReopenResponse is a discriminated union reopenClosed returns directly, replacing ReopenOutcome, its mapping, and the CLI's fallbacks. - withFreshSurfaceIds mints its own ids; window-reopen no longer exports one. - Wall: selectAfterKill shared by the pane and Door kill paths, insertDoorAt shared by split and reopen, and the record's placement built once. - helperMayBeBusy shared by the running count and closeKind. - serializeReported filters once and reuses cwdSurfaceIds. - A window-close dialog says Reopen cannot bring the window back only when that is why it asks (unreopenable on the intent), not for a pending update. - Drop the unused countRunningSessionsIn re-export and its stale comment; reopenClosedWindow no longer sits under transferWorkspace's doc comment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # docs/specs/layout.md # docs/specs/reopen.md
- reopen_closed_window puts the record back, and removes a snapshot it wrote, when opening the window fails; the webview falls back to its own records when the host call rejects. - A window close asks when a Workspace's Wall has not mounted to say what it holds, rather than closing past its running work. - A Surface already fading out is not recorded a second time by a later kill. - A reopened preview viewer comes back pinned, never a second preview slot. - dor reopen answers "still mounting" when no Wall can take the record, keeping it, rather than "Nothing to reopen". - A gesture close that skips the confirmation passes `ask`, so a Tool that turns dirty meanwhile prompts instead of refusing in silence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Deploying mouseterm with
|
| Latest commit: |
c0c978c
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://032a54b9.mouseterm.pages.dev |
| Branch Preview URL: | https://reopen-build.mouseterm.pages.dev |
dormouse-bot
reviewed
Oct 3, 2026
dormouse-bot
left a comment
Collaborator
There was a problem hiding this comment.
CI has not run on this head. The branch conflicts with main in scripts/spec-word-budgets.json, and the CI workflow doesn't start on a conflicting PR, so only Cloudflare Pages and this review have reported. Once the budget conflict is resolved (re-ratchet the touched specs against main's numbers), CI will run, and this review can become an approval if it passes. The code review itself found nothing to change.
# Conflicts: # scripts/spec-word-budgets.json
dormouse-bot
approved these changes
Oct 3, 2026
nedtwigg
requested a deployment
to
hosted-preview
October 3, 2026 12:02 — with
GitHub Actions
Waiting
3 of 4 tasks
This branch is waiting to be deployed
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.
Builds Scope: reopen from
docs/specs/reopen.md(#963). A close now confirms only when Reopen can't restore it. Everything that closes without asking goes onto a per-Window reopen stack: in memory only, newest first, capped at 20.What changed for the user
builtin:fileandbuiltin:folderviewers, iframe browsers, and Workspaces or windows that hold only those plus untouched shells. This fixes the "confirm to close a cleandor open file.txt" annoyance. Before this, every browser close confirmed.u, anddor reopen(window.reopen).uis removed from the kill-confirm letters.Ctrl+Shift+Ton Windows or Linux, where programs receive it asCtrl+T.surface:N.Prerequisite fixes (shells that could close without asking)
dor sendand paired-phone (Client) input now mark a shell touched. Client input reaches the webview asterminal:clientInputfrom the sidecar, Rust and VS Code.Specs
Scope: reopen is promoted above the fold in
reopen.md, with a newreopen.rationale.md. The promotion checklist edits are in layout (Kill confirmation, Workspace lifecycle), shortcuts, the glossary verb table, dor-cli (dor reopen), standalone (Per-window close), transport, dor-tool and dor-tools-builtin. Scope: delayed-kill stays under Future, and its PR follows this one.Test plan
tscin lib, standalone and vscode-ext; spec-lintdor-cli/distbuild output and the macOS system bash), and are not confirmed on main.dor reopenon an empty stack answers "Nothing to reopen".builtin:filekeeps its name.🤖 Generated with Claude Code