Desktop: pooled session worktrees are never reclaimed — archiving pools instead of removing, with no expiry

Status Open
Maintainer reply None cached
Activity 1 comment · opened Sep 1, 2026

Summary

Archiving a Desktop session releases its worktree to a reuse pool rather than deleting it. I can find no expiry or eviction path for pooled worktrees, so their disk is never reclaimed.

The docs state the opposite: "To remove a worktree when you're done, hover over the session in the sidebar and click the archive icon." (desktop docs)

Observed

Archiving a finished local session via the sidebar archive icon logs:

23:05:10 [info] Cleaning up worktree <name> for session local_<id>
23:05:12 [info] [WorktreePool] Released worktree <name> to pool (was leased by local_<id>)

The cleanup path runs and succeeds — it just doesn't delete. Afterwards the directory is still present on disk and still listed by git worktree list.

Why this compounds

A worktree only becomes useful once dependencies are installed and the app has been built, and those artifacts live inside the worktree. In my case one worktree holds:

| | Size |
|---|---|
| .next (build output) | 5.0G |
| node_modules | 1.2G |
| checkout (src, public, etc.) | ~120M |
| total | 6.3G |

The worktree mechanism itself is cheap (~120M — git worktrees share the object database). The cost is everything you must put inside it to make it usable.

Parallel sessions are the primary use case for worktrees, and concurrent sessions cannot share one, so each needs its own. The pool only recycles idle worktrees. So this multiplies with parallelism and never shrinks.

No reclamation path found

From ~/.config/Claude/git-worktrees.json:

"worktrees": {
  "<name>": {
    "leasedBy": null,
    "createdAt": 1788230281178,
    "pooledAt":  1788239112270
  }
}
  • The entry has createdAt and pooledAt but no TTL, expiry, or max-age field.
  • The only GC state in that file is untrackedDirGc, which tracks roots/cwds/sightings — directories under .claude/worktrees. A registered worktree like this one has no comparable expiry state.
  • In the desktop bundle, cleanupPeriodDays appears only in transcript-retention paths; I could not find it referencing worktrees.
  • Searching the bundle for a worktree sweeper or pool eviction (reapWorktree, pruneWorktree, staleWorktree, maxAgeMs, maxWorktrees, evict) returned nothing. pooledAt appears to be read for ordering reuse candidates, not for eviction.

I want to be clear about the limits of that evidence: the last two points are searches of a minified bundle coming up empty, which is weaker than the registry schema itself. It's possible a GC computes age from pooledAt without any TTL in the schema. But the observed behaviour matches "no expiry" — the worktree is still there.

Reproduce

  1. Start a local Desktop session with worktree isolation in a Git repo
  2. Install dependencies and build/run the app inside the worktree
  3. Finish the session, hover it in the sidebar, click the archive icon
  4. Check git worktree list, the worktrees directory, and ~/.config/Claude/git-worktrees.json

Expected: the worktree is removed, per the docs.
Actual: it is pooled with leasedBy: null, remains on disk in full, and nothing appears to expire it.

Requests

  1. Give pooled worktrees an expiry, or bound the pool by count or total size
  2. Surface pooled worktrees somewhere — their disk cost, and a way to clear them
  3. Either correct the docs, or make the archive icon actually remove the worktree

Worktrees are meant to be disposable. Keeping them indefinitely, with no visibility and no way to reclaim the space short of git worktree remove by hand, inverts that.

Environment

  • claude-desktop 1.40609.0
  • Ubuntu 26.04.1 LTS, kernel 7.0.0-30-generic

Possibly related

  • #88239 — session teardown refusing on a clean worktree (different surface, and that one at least surfaces an error; this one is silent)
  • #83180 — worktree allocator reusing idle pooled directories

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗