Skip to content

feat: repo-level worktree housekeeping#3034

Open
pat-s wants to merge 27 commits into
pingdotgg:mainfrom
pat-s:t3code/deffe2b9
Open

feat: repo-level worktree housekeeping#3034
pat-s wants to merge 27 commits into
pingdotgg:mainfrom
pat-s:t3code/deffe2b9

Conversation

@pat-s

@pat-s pat-s commented Jun 10, 2026

Copy link
Copy Markdown

Screenshots

image image Screen Shot 2026-06-10 at 10 35 34 PM

Summary

Implements the repo-level worktree cleanup requested in #684.

Adds a repo-scoped "Clean up worktrees" action that lists T3 Code-managed git worktrees for a repository, shows each one's on-disk size and a dirty flag, lets the user select and force-remove them, and shows the total reclaimable space upfront.

  • Server: three new VCS operations on the git driver / workflow service / WS layer — listManagedWorktrees (porcelain parse filtered to paths under worktreesDir, with a per-worktree dirty check), worktreeSize (recursive filesystem walk, lazily fetched per row and cached client-side), and removeWorktrees (batch remove with per-path force and per-path success/failure outcomes).
  • Contracts/RPC: new schemas + RPC methods, and a global worktreeCleanupScope setting (orphaned default, or orphaned-archived).
  • Web: a WorktreeCleanupDialog with lazy size loading, a reclaimable-size footer, per-row deselect, and a per-row force toggle for dirty worktrees; opened from a sidebar project context-menu item and from the Settings → Archived Threads panel.
  • Safety: worktrees still referenced by a live thread are never auto-selected/removable, and dirty worktrees (uncommitted or unpushed work) require an explicit force toggle.
  • Discoverability fix: archived-thread rows now show a visible Delete button (previously delete was only reachable via right-click).

A "managed worktree" is any git worktree whose path is under the server's worktreesDir, excluding the main checkout — the reliable identity test since all T3 Code worktrees are created there.

Closes #684.


Note

Medium Risk
Batch git worktree remove with force can delete unpushed work; mitigations include dirty detection, active-thread protection, and explicit force toggles, but filesystem/git edge cases remain.

Overview
Adds repo-scoped worktree housekeeping: list T3-managed worktrees under worktreesDir, show disk size and dirty state, and batch-remove selected paths with optional per-item force.

Backend exposes three VCS RPCs end-to-end (contracts, GitVcsDriverCore, GitWorkflowService, WebSocket): listManagedWorktrees (porcelain parse + dirty heuristic), worktreeSize (recursive walk), and removeWorktrees (per-path outcomes; batch remove refreshes VCS status). A new worktreeCleanupScope setting (orphaned vs orphaned-archived) controls default pre-selection.

Web adds WorktreeCleanupDialog with classification against live + archived thread refs (classifyManagedWorktrees / selectWorktreesForScope), lazy sizes, reclaimable total, and force gating for dirty rows. Entry points: sidebar project context menu Clean up worktrees… and Archived Threads Clean up; General settings row for scope. Archived thread rows also get a visible Delete button beside Unarchive.

Reviewed by Cursor Bugbot for commit 8ddbdf4. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add repo-level worktree housekeeping with cleanup dialog and VCS RPCs

  • Adds a WorktreeCleanupDialog component accessible from the project context menu in the sidebar and the Archived Threads settings panel, allowing users to review, select, and remove managed worktrees for a repository.
  • Classifies worktrees as active, archived-only, or orphaned relative to thread refs, with scope controlled by a new worktreeCleanupScope setting (default: 'orphaned').
  • Adds three new VCS RPCs (vcs.listManagedWorktrees, vcs.worktreeSize, vcs.removeWorktrees) wired through contracts, server WebSocket handlers, client runtime, and EnvironmentApi.
  • Server-side implementation in GitVcsDriverCore lists worktrees under the configured worktrees directory, reports dirty state via git status --porcelain, computes on-disk size recursively, and handles batch removal with per-path outcomes.
  • Bulk removal triggers a VCS status refresh; the dialog shows per-item size, force-removal toggles for dirty worktrees, and success/error toasts on completion.

Macroscope summarized 8ddbdf4.

pat-s added 21 commits June 10, 2026 14:51
Map listManagedWorktrees, worktreeSize, and removeWorktrees through the web environmentApi and fix all vcs mock objects in the affected test files.
Add classifyManagedWorktrees and selectWorktreesForScope pure helpers to
worktreeCleanup.ts, along with the WorktreeThreadRef, ClassifiedWorktree,
and WorktreeClassification types needed to drive cleanup dialog pre-selection.
…mable total

- add worktreeCleanupScope to ServerSettingsPatch so the setting persists
  through the updateSettings RPC (Schema strips unknown keys otherwise)
- totalSelectedBytes now counts only removable rows so the footer matches
  what will actually be deleted (dirty rows need force)
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: ed378e8e-abbc-4ec6-abba-8b5c78243ddd

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Jun 10, 2026
Comment thread apps/web/src/components/WorktreeCleanupDialog.tsx
Comment thread apps/web/src/components/WorktreeCleanupDialog.tsx
Comment thread apps/web/src/components/Sidebar.tsx Outdated
Comment thread apps/web/src/components/WorktreeCleanupDialog.tsx Outdated
Comment thread apps/web/src/worktreeCleanup.ts
# Conflicts:
#	apps/web/src/components/Sidebar.tsx
Comment thread apps/server/src/vcs/GitVcsDriverCore.ts
Comment thread apps/web/src/components/settings/SettingsPanels.tsx
@macroscopeapp

macroscopeapp Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

This PR introduces a substantial new feature (worktree cleanup dialog, new RPC methods, settings) with 3000+ lines of new code across multiple packages. Additionally, there are unresolved review comments including a security concern about path validation in the batch remove operation.

You can customize Macroscope's approvability policy. Learn more.

- normalize separators/trailing slashes when matching worktree paths so
  live-thread worktrees aren't misclassified as orphaned
- resolve real paths symmetrically in listManagedWorktrees so symlinked
  worktrees dirs match correctly
- hold threadRefs in a ref so frequent thread-store updates no longer
  reset the dialog and discard in-progress selection
- surface listManagedWorktrees and removeWorktrees failures via toast +
  an inline load-error state instead of silently showing "nothing to clean up"
- add a .catch with toast to the archived-thread Delete button
- route the sidebar "Clean up worktrees" action through the per-member
  submenu machinery so grouped projects target the right repo
Comment thread apps/web/src/components/Sidebar.tsx Outdated
classifyManagedWorktrees matched worktree paths against threads from every
environment, so a path collision in another environment could mark a
worktree active. Filter both the sidebar and archived-panel thread refs to
the environment the dialog is opened for.
Comment thread apps/web/src/components/Sidebar.tsx Outdated
Comment thread apps/web/src/components/WorktreeCleanupDialog.tsx Outdated
…worktrees

The sidebar passed only live-thread refs, so archived-thread worktrees opened
from it were misclassified as orphaned and pre-selected under the default
"orphaned" scope. And active worktrees were filtered out of the dialog
entirely, so the "protected" rows were never visible.

The dialog now assembles its own thread picture (live store threads + archived
snapshots) for its environment, classifies every managed worktree, and renders
all rows via derived state — active rows shown locked/protected, selection
defaulted per scope and preserved across thread-store updates via per-path
overrides. Removal is gated until both worktrees and archived snapshots load.
Both call sites drop the now-internal threadRefs prop.
@juliusmarminge

Copy link
Copy Markdown
Member

screenshots / recordings pls

Comment thread apps/web/src/components/WorktreeCleanupDialog.tsx Outdated
Comment thread apps/server/src/vcs/GitVcsDriverCore.ts Outdated
The inline `useStore((s) => selectThreadsForEnvironment(s, environmentId))`
returned a new array each render, which defeats useSyncExternalStore's
memoization and caused an infinite render loop ("Maximum update depth
exceeded") when opening the dialog. Wrap it in useShallow like the rest of
the store call sites; the underlying thread objects are cache-stable, so
shallow equality holds and the snapshot stays stable.
},
{ concurrency: 1 },
);
return { results };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Batch remove lacks managed path guard

Medium Severity

removeWorktrees runs git worktree remove for every client-supplied path under input.cwd without checking that each path lies under the server worktreesDir, unlike listManagedWorktrees which filters to managed locations only.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 8010b27. Configure here.

@pat-s

pat-s commented Jun 10, 2026

Copy link
Copy Markdown
Author

Screenshots added to OP.

…leanup

- the cleanup dialog now reads the archived-snapshot load error; when it
  fails, archived worktrees can't be distinguished from orphaned, so it
  shows the error and disables removal instead of silently offering
  archived worktrees under the orphaned scope
- listManagedWorktrees now fails with a GitCommandError when
  `git worktree list` exits non-zero, instead of returning an empty list
  that the UI renders as "Nothing to clean up"

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 8ddbdf4. Configure here.

<DialogFooter>
<span className="mr-auto text-sm text-muted-foreground">
Reclaimable: {formatBytes(total)}
</span>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reclaimable total before archive load

Medium Severity

While archived thread snapshots are still loading, the dialog body shows “Scanning worktrees…” but the footer still renders Reclaimable from totalSelectedBytes(rows). rows can be built from incomplete threadRefs until useArchivedThreadSnapshots finishes, so archived-only paths may be treated as orphaned and counted until loading completes.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 8ddbdf4. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: bulk delete all worktrees for a repo

2 participants