Archiving a desktop session never deletes its worktree when the PR was squash-merged

Status Open
Reported on v2.1.260
Maintainer reply None cached
Activity 0 comments · opened Sep 4, 2026

Environment

  • Claude Code 2.1.260, desktop app 1.46388.1 (macOS, Code tab)
  • Worktree root configured outside the repo; also reproduces with the default in-repo location

Expected

Per the docs, archiving a session removes its worktree ("To remove a worktree when you're done, hover over the session in the sidebar and click the archive icon").

Actual

For repos where PRs are squash-merged (our org default, and GitHub's most common merge strategy), the worktree survives archiving in practically every case. Over weeks this accumulated ~25 leftover worktrees / ~13 GB (each carries its own node_modules and build output).

Root cause (from inspecting local state)

Cleanup appears to run a safety check equivalent to "branch has commits not on the default branch". Squash-merging rewrites history, so the session branch always looks unmerged by commit ancestry, and the check keeps the worktree, even though:

  • the PR is merged (the app itself shows prState MERGED on the session), and
  • the branch's upstream is deleted, i.e. git branch -v shows [gone] after git fetch --prune.

Example from git-worktrees.json + on disk: an archived session's worktree remained with its branch at 2 commits "ahead" of main that are all contained in a squash-merge commit. Worktrees whose branches happened to be ancestry-merged were correctly deleted, confirming the cleanup path itself works.

Consequences

  • The documented archive behavior effectively never fires for squash-merge workflows.
  • The untracked-dir GC does not help either: leftover worktrees stay leased/protected and get recycled into new sessions, which refreshes their protection indefinitely.
  • New sessions silently reuse the stale directories (fine per se, but it masks the leak and the sidebar briefly shows a different worktree name than the one actually leased, which is confusing).

Suggested fix

When deciding whether a worktree is safe to delete, treat the branch as merged if any of:

  1. The session's tracked PR state is MERGED (the app already has this signal), or
  2. The branch's upstream is gone after a pruning fetch (the standard squash-merge heuristic), or
  3. Commit ancestry says merged (current check).

Uncommitted/untracked changes should of course still block deletion.

Repro

  1. In a repo whose PRs are squash-merged, start a desktop session in a worktree, commit, push, open a PR.
  2. Squash-merge the PR (remote branch deleted).
  3. Archive the session.
  4. The worktree directory and local branch remain; git log main..<branch> shows the squashed commits, git branch -v shows [gone].

View original on GitHub ↗