fix(standalone): stop the browser-dev harness persisting sessions the app drops - #477
Conversation
… app drops BrowserSidecarAdapter routed saveState/getState straight to localStorage while TauriAdapter gates both behind PERSIST_SESSION = false, so the harness restored panes across a reload that production standalone deliberately drops. Closes #475
Deploying mouseterm with
|
| Latest commit: |
8f7c092
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://618f2e8f.mouseterm.pages.dev |
| Branch Preview URL: | https://fix-issue-475.mouseterm.pages.dev |
dormouse-bot
left a comment
There was a problem hiding this comment.
The gate itself is right and mirrors TauriAdapter cleanly — saveSession gates on persistsSession === false before getPreviousPaneMap, so the claimed getCwd saving is real, and clearPersistedState runs first in init(), ahead of main.tsx's reconnect. One accuracy gap in the new spec text, and one test-isolation trap.
The spec paragraph frames the cost as panes not surviving a reload, but that isn't what happens here. shutdown() is never wired to beforeunload (it's only called from tauri-adapter.test.ts), so the sidecar and its PTYs outlive a page reload and resumeLiveSessions takes the live path. It reads platform.getState() for getSavedResumePlan, which now returns null — so the Sessions do resume, but every live PTY lands in one tab group with doors and saved titles dropped, which is exactly what the comment above that call ("instead of stacking every live PTY into one tab group") warns about. Real standalone has always done this across a WebView reload, so parity is intact and I don't think it changes the verdict — but it makes the reload in .claude/skills/debug-standalone-agent-browser/SKILL.md ("set dormouse.flags.abDebugLogs, then reload") noticeably more disruptive than the paragraph reads, and docs/specs/transport.md's existing "Live resume within a running app is unaffected — it reads the sidecar's live PTY list, not disk" is now the sentence a reader would check against.
…init test The new spec paragraph read as "panes are gone after a reload", which is wrong in both directions. Nothing wires `shutdown()` to `beforeunload`, so the sidecar and its PTYs outlive a page reload and `reconnect.ts` resumes over them — but it reads `getState()` for the saved resume plan, which the gate makes null, so what is lost is the layout: every live PTY lands in one tab group with doors and saved titles dropped. Real standalone has always done this across a WebView reload, so parity holds; the paragraph now says so. transport.md's "Live resume within a running app is unaffected — it reads the sidecar's live PTY list, not disk" had the same gap and predates this PR. Split it into the Sessions (unaffected) and the layout (not). The `deletes a pre-gate blob on init` test called the real `init()`, which runs `installConsoleForwarder()` — it replaces `console.log/warn/error` on the shared jsdom window with versions that POST to the dev host, behind a window flag nothing clears. Nothing observes it today since this is the last test in the file, but any test added below would inherit it. Claim the flag first so the forwarder no-ops.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NkWzVNffHPiaVCjfHybntN
BrowserSidecarAdapterroutedsaveState/getStatestraight tolocalStoragewhileTauriAdaptergates both behindPERSIST_SESSION = false, sopnpm dev:standalone:abrestored panes across a reload that production standalone deliberately drops. This mirrors the gate in the harness, reportspersistsSession: false, and deletes any pre-gate blob oninit(). Verified by four new tests instandalone/src/browser-sidecar-adapter.test.tsthat fail onmainand pass here.Why mirror rather than document the divergence
#475 left the choice open. Three facts in the repo point the same way:
docs/specs/standalone.mdspells out why:persistsSession: falsemakessaveSessionskip building the record at all, which otherwise costs agetCwdround trip per pane on every debounced save and every 30s heartbeat. In the harness that round trip goes over the HTTP bridge into the sidecar's synchronouslsof. Production never pays it; the harness did.standalone/scripts/dev-agent-browser.mjsgives each run its own temp state dir explicitly "so a dev run never touches the installed app's state" —localStoragewas the one slot that leaked state between runs, andPersistedSessioncarries transcripts.persistsSessionreadstrue(docs/specs/transport.md), so the harness was also contradicting the documented standalone answer rather than merely diverging in behavior.This is the same shape as #439, which removed the harness's false claim of native file-drop support; the new tests live in the file that PR created.
The lost dev convenience — panes surviving a browser reload — is recoverable the same way it is for
TauriAdapter: flip the flag locally. The spec now says to flip both together when workspaces-rollout turns persistence on.Deleting rather than ignoring the key
Ignoring the key would leave a blob written before this gate sitting in the developer's browser profile indefinitely, transcripts included —
localStorageis keyed by profile, not by the per-run temp dir.init()removes it, matchingTauriAdapter.init'sclear_sessioncleanup and its stated reason.Spec updates
docs/specs/transport.md→ "Standalone browser-dev harness": the contract paragraph now names Session-persistence parity alongside the PTY/clipboard/remote-Host contracts it already listed.docs/specs/standalone.md→ "Standalone persists no Session state": two new paragraphs — the harness's gate and the delete-on-init rule, plus what the gate actually costs on reload.docs/specs/transport.md→ "Standalone persists no Session state": corrected a pre-existing sentence. "Live resume within a running app is unaffected — it reads the sidecar's live PTY list, not disk" was true of the Sessions and false of the layout:resumeLiveSessionsreadsgetState()for the saved resume plan, so withpersistsSession: falseevery live PTY lands in one tab group with doors and saved titles dropped. This was already the behavior of production standalone before this PR; only the spec text changed.Both spec corrections came out of the review on
61f4a7ae— the first draft framed the reload cost as panes not surviving, which is wrong in both directions. Nothing wiresshutdown()tobeforeunload, so the sidecar and its PTYs outlive a page reload and the Sessions do resume; it is the layout that does not.Testing
Four tests added to
standalone/src/browser-sidecar-adapter.test.ts, all failing onmainbefore the fix:reports the same persistsSession as TauriAdapter— wasundefinedvsfalsedoes not write session state to localStorage— the blob was writtendoes not restore a stale blob left by an earlier run— the blob was returneddeletes a pre-gate blob on init— the key survivedAfter the fix:
vitest runinstandalone/is 62 passed (7 files),tsc -bclean,pnpm lint:specsOK (24 specs, 25 files).Closes #475 — automated triage