Repository navigation
Description
Parent acceptance epic: #269
Predecessor: #882 / PR #883
Problem
Unchanged VS Code OSS 1.137 stores MCP workspace input metadata synchronously during beforeunload, schedules Storage.flush(0), and then disposes its browser workbench in the same lifecycle dispatch. Delayer.dispose() calls clearTimeout() for that just-created zero-delay flush. WebScene removes the task, finds no terminal persistence work, dispatches pagehide immediately, and publishes the reload before the pending storage item reaches IndexedDB.
The installed exact package at AppScene cd0a02e + WebScene 2520661b reproduces the loss. The pre-reload registry contains ${input:sceneSecret}; after reload the workspace registry is empty. Awaiting the unchanged upstream IStorageService.flush(SHUTDOWN) before location.reload() makes the same package pass, isolating the defect to terminal lifecycle handoff rather than IndexedDB durability.
Reproduction
A focused native fixture now models the exact sequence:
beforeunloadcreates a zero-delay one-shot flush timer.- normal workbench disposal clears that timer before event dispatch returns;
- the callback would start a read/write IndexedDB transaction and a promise/timer continuation;
pagehidecloses the database;- durable restart must recover the value.
At current main the fixture fails with:
order=[beforeunload, preexisting-commit, pagehide]
committed=false
continuation=false
Proposed fix
Treat a zero-delay, non-repeating callback created while dispatching an accepted terminal lifecycle as committed terminal handoff work. If same-turn disposal clears its public timer handle, detach it from the public timer namespace but retain one private terminal callback under the existing 1,024-task handoff budget. Preserve terminal ancestry through its microtasks, timers, and IndexedDB operations. Do not retain intervals, animation frames, future timers, timers created outside terminal lifecycle, or work beyond the bounded budget.
This is a narrow native-shell durability contract needed by browser workbenches whose synchronous shutdown disposal cancels their delayed flush object. Document the deliberate terminal-only difference from ordinary clearTimeout() behavior.
Acceptance
- Focused fixture fails before and passes after the runtime change.
-
beforeunload→ retained flush callback → IndexedDB commit → continuation →pagehide→ host reload order is exact. - Restart recovers both the lifecycle write and a transaction already pending before reload.
- Intervals and future timers do not delay navigation.
- Self-refilling terminal work remains bounded by the existing task budget.
- Ordinary
clearTimeout()outside an accepted terminal lifecycle still cancels exactly. - Exact unchanged Code OSS package passes three sequential MCP save/reload/reopen cycles without the diagnostic explicit-flush probe.
- Record focused elapsed time and verify no idle-loop or retained-task regression.
Activity
wieslawsoltes commentedon Sep 21, 2026
Implemented and merged in PR #885 at
c10b78250173589a8e417f13d2c14c8476316482. The focused macOS arm64 Release gate passes in 0.0036 seconds. Remaining proof is the exact unchanged Code OSS package gate tracked by parent #269.wieslawsoltes commentedon Sep 21, 2026
Exact installed-package repetition found a remaining close race after PR #885: run 1 passed in 16.204s, run 2 failed in 25.623s with the pre-reload key present and the reopened key absent. In the failed run, retained terminal work delayed pagehide by 24ms, but
vscode-web-dbwas already closed. VS Code shutdown can runIDBDatabase.close()from a microtask before the retained zero-delay flush starts its transaction. Reopening this issue; the follow-up fixture will close the database from the shutdown microtask and require close to wait behind already accepted terminal persistence work.wieslawsoltes commentedon Sep 21, 2026
Follow-up PR #886 merged at
62c4c6175fc55bbebb633eb18b5a94e7de613efe. It defers a shutdown-microtaskIDBDatabase.close()behind already accepted terminal persistence; the focused exact-order/restart/bounded gate passes in 0.0036s. Rebuilding the exact SDK/package for repeated installed acceptance now.wieslawsoltes commentedon Sep 21, 2026
Final installed-package diagnosis: the intermittent acceptance driver called
window.location.reload()directly, bypassing unchanged VS CodeIHostService.reload(). The real upstream host path awaitslifecycleService.withExpectedShutdown(RELOAD), includingIStorageService.flush(SHUTDOWN), before delegating tolocation.reload(). After correcting the query-gated test overlay to invokeworkbench.action.reloadWindow, the exact AppScenecd0a02e/ WebScene62c4c617package passed 3/3 MCP store → reload → reopen runs in 16.727s, 13.633s, and 13.919s. PRs #885 and #886 remain useful runtime hardening for direct terminal navigation. Closing this focused defect; full edit/save/hot-exit coverage remains in #269.