Skip to content

(restore): retry the working-set restore once the cache catches up - #239

Merged
devsuitup merged 1 commit into
mainfrom
fix/restore-cold-cache
Sep 9, 2026
Merged

(restore): retry the working-set restore once the cache catches up#239
devsuitup merged 1 commit into
mainfrom
fix/restore-cold-cache

Conversation

@devsuitup

Copy link
Copy Markdown
Owner

Closes #205.

On a launch where the cache is cold, the saved working set did not come back —
no error, no toast, nothing. restoreWorkingSet() filtered the saved list
against sessionMap and returned silently when nothing matched:

const candidates = savedSet.filter(item =>
  sessionMap.has(item.sessionId) && !openSessions.has(item.sessionId)
);
if (candidates.length === 0) return;

The filter itself is right — deleted sessions and vanished worktrees are real —
but it cannot tell "this session was deleted" from "this session is not
indexed yet"
, and treats both as "drop it". The second is a race it loses
whenever the cache is cold: after a schema migration wipes session_cache,
after a manual clear, or on a first run against a large project set.

The saved list was never lost; it lives in settings.global.openWorkingSet,
which the wipe does not touch. It just took a second launch to come back.

What changes

The decision moves to public/restore-plan.js, a pure function returning
restore, defer or nothing. The renderer loads it as a classic script and
the tests require() it — the dual-mode pattern shortcuts.js already uses.

restoreWorkingSet() keeps only the effects. On defer it arms a retry that
fires from the existing projects-changedloadProjects() pipeline; no new
IPC channel. The retry is consumed at most once, only when the index has
actually produced something, and is cancelled if the user opens a session
themselves in the meantime.

In ask mode a cold-cache skip now shows its own notice, so a pending restore
no longer looks identical to having nothing to restore.

Startup is not blocked waiting for the scan: that is the multi-minute freeze
the v0.0.33–41 perf work removed, and get-projects says so in as many words.

Verification

The mutation runs on the shipped file, not on a test-side copy: forcing the
defer branch of planWorkingSetRestore to never fire turns red the issue's
first acceptance test — sessionMap empty at decision time → defer, not drop
along with five wiring tests, and nothing else.

Stated plainly: the decision is now exercised where it ships, but the wiring
around it — the retry's call sites in app.js — is still driven through a
test-side harness that calls the real module. app.js cannot be loaded under
node:test without prohibitive DOM scaffolding.

Residual risk, not fixed: the retry fires when sessionMap is non-empty, which
means "the scan produced something", not "the scan finished". The authoritative
marker is isInitialScanComplete(), which lives in the main process. On a very
large tree the retry could in principle fire before every folder is scanned and
still drop a session. Not reproduced, not load-tested.

task check: green, lint 0 errors.

restoreWorkingSet() filtered saved sessions against sessionMap and gave up
silently on zero candidates, unable to tell "deleted" from "not indexed
yet". On a cold cache (schema-migration wipe, manual cache clear, first run
against a large project set) sessionMap is still empty when it first runs,
so the whole working set was dropped with no error and no toast.

The candidate/defer/nothing decision is pulled into a pure function,
planWorkingSetRestore() in public/restore-plan.js, loaded as a classic
script in the renderer and require()-d directly by tests (same dual-load
pattern as public/shortcuts.js) so the fix is exercised for real instead of
through a test-side reimplementation.

A saved item missing from sessionMap now schedules one retry, fired the
next time the existing projects-changed -> loadProjects cycle refreshes
sessionMap with real data. The retry is skipped if the user opens a
session themselves in the meantime, and mode 'ask' shows a distinct
"finishing indexing" notice instead of looking identical to a genuinely
empty working set.
@devsuitup
devsuitup merged commit d05c59f into main Sep 9, 2026
10 checks passed
@devsuitup
devsuitup deleted the fix/restore-cold-cache branch September 9, 2026 12:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

restore: the working set is silently dropped when the cache is still cold

1 participant