Skip to content

feat(web): worktree cleanup runs in the background with top bar progress - #290

Merged
TheDarkPhantom merged 1 commit into
mainfrom
feat/worktree-cleanup-background
Oct 27, 2026
Merged

TheDarkPhantom merged 1 commit into
mainfrom
feat/worktree-cleanup-background

Conversation

@TheDarkPhantom

Copy link
Copy Markdown
Owner

Problem

"Clean up dead worktrees" blocked a modal on "Scanning for dead worktrees..." for up to an hour. Three causes:

  • Disk sizing: the scan measured each candidate's disk size by visiting every file in it, node_modules included. That means hundreds of thousands of filesystem calls per worktree on Windows.
  • Git lane: the scan held the project's shared serial git lane, so the user's git actions in that project queued behind it.
  • Lost results: all state lived in the dialog, so closing it threw the results away while the RPC kept running.

Fix

  • Server:
    • vcs.scanCleanupCandidates takes an optional includeDiskBytes flag. It defaults to true, so older clients behave the same.
    • New read-scoped vcs.measureWorktreeSize sizes one managed worktree. It refuses paths outside the managed worktrees directory.
  • Client runtime: the scan and size RPCs get their own scheduler lanes, so they never block project git commands.
  • Web:
    • Job store: a module-level store (worktreeCleanupJob.ts) owns the scan, sizes and delete.
      • Each project's list shows up as soon as its scan finishes.
      • Sizes are measured two at a time afterwards, and deleting doesn't wait for them.
      • Delete runs one worktree per call so progress can count, with git gc only on each project's last worktree. It can be stopped after the current worktree.
    • Dialog: a single global dialog in the root route reads the store. Closing it only hides it.
    • Status pill: a pill in the chat header, the empty-state header and the settings header shows "Scanning worktrees 3/7", "12 dead worktrees · Review", "Deleting 7/40" or "Freed 8.2 GB". Clicking it reopens the dialog, and × cancels, stops or dismisses.
    • Finish toast: a toast reports when a step finishes while the dialog is hidden.
    • Entry points: Settings, the health warning toast and the sidebar project action all start or reopen the same job.

Verification

  • Tests:
    • New server tests cover the scan without sizes and measureSize, including the out-of-directory refusal.
    • New web logic tests cover pre-selection, gc placement, progress labels and the wording when sizes are unknown.
    • Targeted typecheck passes for server, web, contracts and client-runtime, along with lint and check:avicode.
  • Integrated pass: a headless Edge run against a worktree dev server, using a throwaway repo with seeded dead worktrees.
    • The list appeared within a second, and sizes filled in.
    • Hiding the dialog showed the pill. The pill counted the delete down and ended at "Freed 8.6 MB", with a finish toast.
    • The worktree with uncommitted changes stayed unchecked and on disk.
    • Stop after the first worktree reported "Stopped early. Removed 1 worktree."
    • The Settings entry showed the pill in the settings header, and Dismiss cleared it.

Not exercised in the browser: the sidebar project action. It is a one-line switch to the same startWorktreeCleanupScan call, and typecheck covers it.

Screenshots were captured locally during the integrated pass; the pill sits left of "Add action" in the chat header.

Done by Claude Opus 5.5 (1M context) in Claude Code.

🤖 Generated with Claude Code

The dead-worktree scan blocked a modal for up to an hour. It walked every
file in every candidate worktree to size it, held the project's git command
lane the whole time, and threw its results away if the dialog closed.

- Scan without sizes (new optional includeDiskBytes flag), then measure each
  worktree with a new vcs.measureWorktreeSize RPC, two at a time.
- Scan and size RPCs get their own client lanes so the user's git actions
  never queue behind them.
- New module-level job store owns scan, sizes and delete; the dialog is a
  single global view over it. Delete runs one worktree per call for progress,
  with git gc only on each project's last one, and can be stopped.
- Status pill in the chat, empty-state and settings headers reopens the
  dialog; a toast reports when a step finishes while it is hidden.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:XXL labels Oct 27, 2026
@TheDarkPhantom
TheDarkPhantom merged commit 4628683 into main Oct 27, 2026
12 checks passed
@TheDarkPhantom
TheDarkPhantom deleted the feat/worktree-cleanup-background branch October 27, 2026 06:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant