fix: prune workspace session state when a project is removed (#9024)#9025
fix: prune workspace session state when a project is removed (#9024)#9025PannenetsF wants to merge 2 commits into
Conversation
removeProject → pruneWorktreeStateForRepo cleared worktreeMeta, lineage, and workspace lineage, but left the removed repo's worktrees behind in workspaceSession (lastVisitedAtByWorktreeId, sleepingAgentSessionsByPaneKey, tab state, …) and in every workspaceSessionsByHostId partition. Those dangling references caused the runtime to treat the deleted repo's worktrees as recently-active on the next launch and re-materialize their worktreeMeta. With no owning project left, the UI rendered them as an orphaned "unknown" workspace that returned after every restart. Reuse the existing removeWorkspaceSessionOwner cleanup (already used by removeFolderWorkspace and deleteProjectGroup) against the legacy session blob and each per-host partition, collecting owner keys before the worktreeMeta delete loop so host classification still works. Fixes stablyai#9024 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughProject removal now collects affected worktree owner keys before deleting worktree metadata. It prunes matching entries from global workspace session state and host-scoped workspace session partitions. Tests cover cleanup for global and host-scoped 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Thanks for tackling #9024. I tested the current head and confirmed that the full I found one cross-host regression in the host-scoped Reproduction: register the same repo ID and path on local and Could the cleanup be scoped to the selected host when Otherwise, the fix looks focused and the full-removal behavior appears correct. |
The workspace-session cleanup applied every collected owner key to the legacy
(local) session blob AND every workspaceSessionsByHostId partition. Owner keys
are `${repoId}::${path}` and carry no host, so a host-scoped removal
(removeProjectForHost) of one host wrongly wiped a surviving host's session for
a shared repo id/path — discarding its lastVisitedAt, tabs, sleeping-agent
state, and active-worktree pointer.
Scope the session prune to the removed host: prune the legacy blob only when the
target host is local (or on a full removeProject where hostId is null), and
prune only the matching workspaceSessionsByHostId partition for a non-local
host; a full removal still clears every partition. Collect session owner keys by
`${repoId}::` prefix (not belongsToHost, which is worktreeMeta-host-classified)
so a partition-only key is still prunable.
Adds regression tests for a shared repo id + path across local and an SSH host,
covering removeProjectForHost on the SSH host (local session survives) and on
the local host (SSH session survives).
Addresses review feedback on stablyai#9024.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@e-jung Thanks for the careful review and the concrete repro — you're right, that was a real cross-host bug. I pushed
I also added the two regression tests you suggested, both using the same owner key on two hosts:
Would you mind taking another look when you have a moment? Thanks again. |
Summary
Deleting a project left an orphaned "unknown" workspace in the sidebar that returned after every restart (#9024).
removeProject→pruneWorktreeStateForRepoonly cleanedworktreeMeta,worktreeLineageById, andworkspaceLineageByChildKey. It never removed the deleted repo's worktrees from:workspaceSession(the legacy blob) —lastVisitedAtByWorktreeId,sleepingAgentSessionsByPaneKey, tab state,activeWorktreeId, …workspaceSessionsByHostId— the per-host session partitionsBecause
lastVisitedAtByWorktreeIdstill referenced${repoId}::${path}, on the next launch the runtime treated those worktrees as recently-active and re-materializedworktreeMetafor them. With the owning repo/project gone, the UI rendered them as an orphaned "unknown" workspace — and hand-editingworktreeMetaout oforca-data.jsondidn't help because it was rebuilt from the session state.Fix: in
pruneWorktreeStateForRepo, collect the repo's owner keys before theworktreeMetadelete loop (sobelongsToHostcan still readmeta.hostId), then run the existingremoveWorkspaceSessionOwnercleanup against boththis.state.workspaceSessionand everythis.state.workspaceSessionsByHostIdpartition. This reuses the exact single-owner cleanup already used byremoveFolderWorkspaceanddeleteProjectGroup, so behavior stays consistent across all deletion paths.Screenshots
No visual change. Behavior-only fix in the main-process persistence layer (
src/main/persistence.ts).Testing
pnpm lint— ranoxlinton the changed files (pass); did not run the fulllintscript (it also runs localization/reliability gate scripts unrelated to this change)pnpm typecheck— pass (tsc --noEmit -p config/tsconfig.node.json)pnpm test— ran the targeted suitevitest run src/main/persistence.test.ts(19 passed); could not run the full suite locally because the nativenode-ptypostinstall build fails in my environment (unrelated to this change)pnpm build— not run locally (blocked by the same native-module build issue)Added two regression tests to
persistence.test.ts:removeProject prunes the repo worktrees from workspace session state(legacy blob)removeProject prunes the repo worktrees from per-host workspace session partitions(workspaceSessionsByHostId)Both fail on
main(the stale keys survive) and pass with this change — verified by reverting the source fix and re-running the suite.AI Review Report
Reviewed with an AI coding agent focused on the persistence-layer cascade:
belongsToHostreadsmeta.hostIdfromworktreeMeta, so it must run before the meta delete loop. The fix collects owner keys up front (fromworktreeMetaplus the session maps) to preserve the existing host-scoped semantics ofpruneWorktreeStateForRepo(id, hostId); the host-scoped path (removeProjectForHost) still only prunes the target host's entries.worktreeMeta: a worktree can be referenced inlastVisitedAtByWorktreeIdwithout a liveworktreeMetaentry (exactly the orphan case). Owner keys are therefore collected from the session maps as well, not only fromworktreeMeta.removeWorkspaceSessionOwnerhelper (already used byremoveFolderWorkspace/deleteProjectGroup), which clears tabs, layouts, browser tabs, active pointers,lastVisitedAtByWorktreeId, and sleeping agent sessions — avoiding a divergent partial cleanup.${repoId}::${path}strings and are neither parsed nor normalized per-platform.Security Audit
child_process, no filesystem access; owner keys are opaque map keys, never resolved to disk paths.removeProjectpath.Notes
removeWorkspaceSessionOwner; this brings project removal in line with them.