Skip to content

feat(worktrees): restore pruned workspaces on demand - #324

Merged
Waishnav merged 5 commits into
feat/prune-stale-worktreesfrom
feat/restore-pruned-worktrees
Sep 8, 2026
Merged

Waishnav merged 5 commits into
feat/prune-stale-worktreesfrom
feat/restore-pruned-worktrees

Conversation

@Waishnav

@Waishnav Waishnav commented Sep 7, 2026

Copy link
Copy Markdown
Owner

Summary

  • keep pruned managed-worktree sessions as lightweight metadata
  • record whether recovery comes from the original base, a detached HEAD ref, or a stash snapshot
  • lazily recreate the physical worktree when the old workspaceId is used again
  • restore staged/unstaged tracked state with git stash apply --index

Stack

Depends on #319.

Validation

  • pnpm test (126 passed, 1 skipped)
  • pnpm build

Summary by CodeRabbit

  • New Features

    • Pruned workspaces now retain recovery information and can be restored automatically when accessed.
    • Restored workspaces recover detached commits and tracked changes where applicable.
    • Workspaces can be reactivated after successful restoration.
  • Bug Fixes

    • Improved workspace handling for read, write, editing, shell, and artifact operations.
    • Failed restoration attempts now clean up safely without leaving unusable workspaces behind.
    • Concurrent requests for the same pruned workspace now share a single restoration process.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Team

Run ID: 35a5e751-ffa6-4c1b-952e-34f07831bba9

📥 Commits

Reviewing files that changed from the base of the PR and between 5f67e26 and 6c20386.

📒 Files selected for processing (13)
  • src/artifact-tools.ts
  • src/db/migrations.ts
  • src/db/schema.ts
  • src/git-worktrees.test.ts
  • src/git-worktrees.ts
  • src/oauth-store.test.ts
  • src/server.ts
  • src/tool-surfaces/claude.ts
  • src/tool-surfaces/codex.ts
  • src/workspace-store.test.ts
  • src/workspace-store.ts
  • src/workspaces.test.ts
  • src/workspaces.ts

Included review availability: Your plan provides up to 8 included reviews per hour; 6 remain after this review.


📝 Walkthrough

Walkthrough

The change preserves recovery metadata for pruned workspace sessions. Workspace lookup is now asynchronous and restores pruned managed worktrees, including concurrent-lookup deduplication and cleanup after failed reactivation. Tool handlers await workspace restoration before operating.

Changes

Workspace recovery

Layer / File(s) Summary
Recovery state persistence
src/db/migrations.ts, src/db/schema.ts, src/workspace-store.ts, src/oauth-store.test.ts, src/workspace-store.test.ts
Migration version 7 adds recovery_kind. Workspace sessions persist "head" or "stash" recovery state and can be reactivated after pruning.
Git snapshot and restoration
src/git-worktrees.ts, src/git-worktrees.test.ts
Cleanup retains pruned sessions and records the snapshot type. New functions restore or discard managed worktrees.
Asynchronous workspace restoration
src/workspaces.ts, src/workspaces.test.ts
getWorkspace returns a promise, restores pruned sessions, deduplicates concurrent restores, and cleans up when reactivation fails.
Asynchronous tool integration
src/artifact-tools.ts, src/server.ts, src/tool-surfaces/claude.ts, src/tool-surfaces/codex.ts
Tool handlers await workspace lookup before file, shell, process, and artifact operations.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: ⚪ Minimal · up to 6c203

Pruned workspaces retain recovery state and are restored before workspace-backed operations proceed, including cleanup when restoration cannot be reactivated. No merge-blocking risk remains.

Sequence Diagram(s)

sequenceDiagram
  participant ToolHandler
  participant WorkspaceRegistry
  participant WorkspaceStore
  participant GitWorktrees
  ToolHandler->>WorkspaceRegistry: await getWorkspace(workspaceId)
  WorkspaceRegistry->>WorkspaceStore: load pruned session
  WorkspaceRegistry->>GitWorktrees: restoreManagedWorktree(session)
  GitWorktrees-->>WorkspaceRegistry: restored worktree
  WorkspaceRegistry->>WorkspaceStore: reactivateSession(session.id)
  WorkspaceRegistry-->>ToolHandler: return workspace
Loading

Poem

A rabbit reads each line,
The patch grows clear beneath the moon,
Small changes hop in place,
Tests guard the garden path,
Reviews bloom before the dawn.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 18 functions across 13 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: restoring pruned workspaces on demand.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/restore-pruned-worktrees

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This change adds persisted recovery state for pruned managed worktrees and restores worktrees lazily. Two reproduced restoration failures can make a workspace request fail or leave a workspace inaccessible after an interrupted state transition. Recovery snapshot refs also remain after they are no longer needed, increasing Git storage over repeated recovery cycles.

Confidence Score: 3/5

Not safe to merge until concurrent and partially completed workspace restoration are made recoverable. The stale recovery-ref cleanup is a separate non-blocking maintenance concern.

Two independently reproduced workspace-access failures can prevent a user from opening a pruned workspace. A third reproduced lifecycle issue retains obsolete Git snapshot data.

Files Needing Attention: src/workspaces.ts needs coordinated and failure-atomic restoration; src/git-worktrees.ts and the session lifecycle need recovery-ref cleanup.

T-Rex T-Rex Logs

What T-Rex did

  • T-Rex produced a P1 finding proof and attached a TypeScript harness and initial run logs to support the corresponding review comment.
  • T-Rex produced a second P1 finding proof with an expanded set of logs to provide additional run details.
  • T-Rex produced a P2 finding proof supported by a TypeScript harness and its run logs.
  • T-Rex produced a general contract validation proof showing that PR restoration did not modify product code and that only the authored reproduction harness was added.
  • T-Rex produced a general contract validation proof confirming a recovery-lifecycle workflow with a successful after-restore run and focused regression results.

View all artifacts

T-Rex Ran code and verified through T-Rex

Comments Outside Diff (4)

  1. src/git-worktrees.ts, line 171-174 (link)

    P2 Remove consumed recovery refs

    Recovery refs remain after restoration succeeds and after the associated session is deleted. Repeated dirty-worktree recovery cycles therefore retain obsolete snapshot commits and gradually grow the repository’s Git object storage. This is a non-blocking maintenance concern; delete a ref only once its restore has completed successfully, and also define cleanup for permanently deleted pruned sessions.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

    Artifacts

    Evidence from the check

    • Authored TypeScript harness creates a real temporary Git repository and executes prune, restore, reactivation, and deletion to inspect the recovery ref, demonstrating the lifecycle under review.

    Command output from the check

    • First executed harness capture shows the ref absent before prune and present after snapshot creation; the run then stopped on a harness argument error, so it establishes only the pre-restore side.

    Command output from the check

    • Corrected harness completed successfully and shows the recovery ref exists after restoration, reactivation, and deletion of its session, confirming stale ref retention.

    Command output from the check

    • Executed the focused existing Git worktree tests with all nine tests passing, confirming the surrounding cleanup and restoration paths execute while lacking stale-ref cleanup coverage.

    View artifacts

    T-Rex Ran code and verified through T-Rex

  2. General comment

    P1 Concurrent restoration of one pruned workspace rejects the losing request

    • Bug
      • Two requests that reach getWorkspace() while the same managed worktree session is pruned both call restorePrunedWorkspace(). The first creates and reactivates the worktree; the concurrent request attempts another git worktree add for the same target path and rejects with an already-exists error, rather than reading the winner's active session/workspace.
    • Cause
      • src/workspaces.ts:259-262 has no per-workspace in-flight restoration promise, lock, or post-failure reread/reuse path. Each caller independently observes status === "pruned" before either asynchronous restoration completes.
    • Fix
      • Add a per-workspace restoration single-flight map in WorkspaceRegistry (parallel to pendingCheckoutOpens). Await the existing promise for a workspace ID when present; only its owner restores/reactivates, then all callers reread and return the active workspace. Ensure the map entry is removed in finally and add a concurrent-pruned-access regression test.

    T-Rex Ran code and verified through T-Rex

  3. General comment

    P1 Failed session reactivation strands a restored worktree

    • Bug
      • After a pruned managed worktree is physically restored, a failed reactivateSession leaves its database status as pruned while its worktree directory exists. Every subsequent workspace lookup retries restoration and is rejected because the path already exists.
    • Cause
      • restorePrunedWorkspace invokes restoreManagedWorktree before checking the result of this.store.reactivateSession (src/workspaces.ts:302-309). There is no compensation to remove the restored worktree or transition the session after the reactivation failure. restoreManagedWorktree explicitly rejects pre-existing paths at src/git-worktrees.ts:203-206.
    • Fix
      • Make the database transition and filesystem restoration failure-atomic: either reactivate/claim the session before restoration with an appropriate rollback if restore fails, or remove the newly restored managed worktree when reactivation fails before returning the error. A regression test should assert a retry remains possible after reactivateSession fails.

    T-Rex Ran code and verified through T-Rex

  4. General comment

    P2 Recovery refs survive successful restoration and session deletion

    • Bug
      • A dirty stale worktree causes cleanup to create refs/devspace/recovery/ws_lifecycle. The executed lifecycle proved that ref remains after restoration succeeds and the session becomes active, and still remains after deleteSession() removes the only persisted session. These refs keep the stash snapshot commits reachable and accumulate for abandoned/reused workspace IDs.
    • Cause
      • Cleanup creates a durable recovery ref via git update-ref at src/git-worktrees.ts:171-174, but neither restoreManagedWorktree (src/git-worktrees.ts:193-235) nor SqliteWorkspaceStore.reactivateSession (src/workspace-store.ts:151-167) deletes it. SqliteWorkspaceStore.deleteSession (src/workspace-store.ts:183-188) likewise only removes database state.
    • Fix
      • After a successful restore and session reactivation, delete the associated ref with git update-ref -d refs/devspace/recovery/<workspaceId> (with error handling that preserves recovery on failed restore). Also centralize deletion/expiry of a recovery ref when a pruned session is permanently deleted, using the stored source root and validating it as cleanup already does.

    T-Rex Ran code and verified through T-Rex

Reviews (1): Last reviewed commit: "feat(worktrees): restore pruned workspac..." | Re-trigger Greptile

Comment thread src/workspaces.ts
Comment thread src/workspaces.ts
@Waishnav
Waishnav merged commit 646ebdd into main Sep 8, 2026
4 checks passed
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.

1 participant