Skip to content

feat: repo-level worktree housekeeping#3034

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

feat: repo-level worktree housekeeping#3034
pat-s wants to merge 28 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 optional force can delete user data; mitigations include managed-dir path checks, active-thread protection, and dirty-row force gating, but misclassification or force misuse could still remove wanted worktrees.

Overview
Adds repo-scoped worktree housekeeping: list T3-managed worktrees (under worktreesDir), show on-disk size and dirty state, and batch-remove with per-path force and outcomes.

Backend: New contracts/RPC/IPC (vcs.listManagedWorktrees, vcs.worktreeSize, vcs.removeWorktrees), GitVcsDriverCore implementation (porcelain parse + realpath filter, dirty via status/unpushed HEAD, recursive size walk, batch remove guarded to managed paths only), plumbed through GitWorkflowService, WS handlers, and client environmentApi / wsRpcClient.

Web: WorktreeCleanupDialog classifies worktrees against live + archived threads (active / archived-only / orphaned), honors new worktreeCleanupScope setting (orphaned vs orphaned-archived), lazy sizes, reclaimable total, force for dirty rows, and blocks active worktrees. Entry points: sidebar project Clean up worktrees… and Archived Threads Clean up; settings row for scope. Archived thread rows get a visible Delete button.

Tests: Driver integration tests for list/size/batch remove/path refusal; web classification and dialog logic tests; contract schema/settings tests.

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

Note

Add repo-level worktree housekeeping UI and backend RPCs

  • Adds a WorktreeCleanupDialog component accessible from the project context menu in the sidebar and the Archived Threads settings panel, letting users review and remove managed worktrees for a repository.
  • Implements three new server-side Git operations — listManagedWorktrees, worktreeSize, and removeWorktrees — exposed via WebSocket RPCs and wired through GitVcsDriverCore and GitWorkflowService.
  • Classifies worktrees as active, archived-only, or orphaned by comparing against live/archived thread refs; a new worktreeCleanupScope setting (orphaned or orphaned-archived) controls which classes are selected by default.
  • Worktree dirtiness is determined by local git status and a remote-contains check; branches with no upstream remote are treated as dirty.
  • removeWorktrees enforces a path guard, refusing to delete anything outside the configured managed worktrees directory.
  • Risk: branches with no configured upstream are reported as dirty and will require force-removal, which may surprise users who never pushed an initial commit.

Macroscope summarized a8e2927.

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: b4764e6b-94ac-4efa-9189-6b4cc49c5058

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 significant new feature for repo-level worktree cleanup, adding new RPC methods, UI components, and user workflows across multiple packages. New features with this scope require human review. Additionally, there is an unresolved medium-severity review comment about incorrect behavior during loading states.

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.
Comment thread apps/server/src/vcs/GitVcsDriverCore.ts
@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.

removeWorktrees ran `git worktree remove` on any client-supplied path. Add a
defense-in-depth check that resolves each path and refuses (per-path) anything
not under the server worktrees dir, mirroring listManagedWorktrees.
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