Problem
I switched T3 Code's default thread environment to new worktree. T3 Code creates a worktree (and a branch) per thread, but it has essentially no cleanup story — so both accumulate indefinitely.
Tracing t3code (pingdotgg/t3code @ bab4b6f02), there is exactly one removal trigger in the whole codebase: deleting a thread in the web UI, which checks whether any other thread points at the same path and then shows a per-thread confirm dialog before running git worktree remove.
Nothing cleans up on:
- thread archived
- thread settled
- PR / branch merged
- server startup or any periodic pass (no pruning code exists)
- thread deleted from mobile (
removeWorktree isn't wired into the mobile client at all)
And even on the one path that works, only the directory is removed — git worktree remove leaves the branch behind, so you accumulate two kinds of litter.
Current damage
~/.t3/worktrees is 670M:
2 chezmoi
2 codeburn
15 home-lab
1 rpi-nixos
2 t3code
5 void
The void repo is the clearest case — it's a pure chat sandbox, nothing is ever meant to land there:
~/Development/kelchm/void 95552a4 [main]
~/.t3/worktrees/void/t3code-2599a363 95552a4 [t3code/document-local-models]
~/.t3/worktrees/void/t3code-3dc56d1c 95552a4 [t3code/fix-forced-markdown-width]
~/.t3/worktrees/void/t3code-3fed79bb 95552a4 [t3code/add-benchmark-ideas]
~/.t3/worktrees/void/t3code-6c83e84a 95552a4 [t3code/check-unsloth-mise-options]
~/.t3/worktrees/void/t3code-cfa755a6 95552a4 [t3code/list-git-worktrees]
Every one sits at the same SHA as main. Zero commits, zero divergence — five directories and five dangling branches produced by throwaway conversations. Reclaiming them today means deleting each thread individually and clicking through a confirm each time.
Upstream status: filed, but going nowhere
Worth knowing before investing, because it means this workaround is likely to be needed for a while:
Feature requests — all closed, none implemented (no closing actor recorded, no linked merged PR, and the behavior verifiably doesn't exist in the code):
- #5269 Automatically clean up old and inactive Git worktrees
- #5208 Clean up worktrees for settled threads without deleting history
- #6659 Per-project retention limits for settled worktrees
- #684 Bulk delete all worktrees for a repo
Implementation PRs — four attempts, zero merged, none with a review decision:
- #5589 manage lifecycle on orchestrator v2 (only one with recent activity)
- #4742 safe worktree inventory, pruning, and revival
- #3034 repo-level worktree housekeeping
- #6353, #4122 — both closed
Ask
Build a workaround that lives here — a script, a Claude skill under dot_claude/skills/, a scheduled job, or some combination. Approach is deliberately open; the goal is that sandbox repos like void stop growing without me babysitting them.
Whatever the shape, it needs to answer: what's safe to remove, how is that decided, and how much do I have to be in the loop.
Constraints and hazards
These are the things that make a naive git worktree prune / rm -rf wrong:
-
Don't destroy work. A worktree with uncommitted changes, untracked files, stashes, or commits not reachable from its base is not litter. The void case is easy precisely because HEAD equals the base SHA and the tree is clean — that's a provable safety condition, and it may be worth restricting the tool to only what it can prove.
-
Threads bind to worktree paths. T3 Code stores projection_threads.worktree_path in ~/.t3/userdata/state.sqlite. Removing a worktree out from under a live thread bricks it — I hit exactly this in Aug 2026 (raw ENOENT spawn errors on turn start; recovery meant recreating the worktree at the recorded path and branch). Anything that removes directories should cross-reference the thread table first, and must include archived threads — t3code's own orphan check reads a snapshot filtered by archived_at IS NULL, which is an upstream bug I don't want to reproduce.
-
Sessions can be running. Removing a worktree while its session is live permanently wedges the thread upstream (#4723). Check session state, or only operate when T3 Code isn't running.
-
Branches too. Directory-only cleanup leaves the t3code/* branches. Deleting a branch that is merged or identical to its base is safe; anything else isn't.
-
Per-repo policy varies. void is disposable. home-lab has real PR worktrees (pr-301-…, pr-304-…) that should be left alone. Whatever the mechanism, it should be possible to be aggressive on some repos and conservative or opt-out on others.
-
Dry-run first. Given items 1–3, I want to see what it would do before it does it.
Nice to have
- Report reclaimed space — 670M is real, and per-worktree
node_modules is most of it.
- Handle worktrees whose directory is already gone but whose git metadata lingers (
git worktree prune territory), and vice versa.
- Cross-platform if cheap; macOS is the only one that matters today.
Problem
I switched T3 Code's default thread environment to new worktree. T3 Code creates a worktree (and a branch) per thread, but it has essentially no cleanup story — so both accumulate indefinitely.
Tracing t3code (
pingdotgg/t3code@bab4b6f02), there is exactly one removal trigger in the whole codebase: deleting a thread in the web UI, which checks whether any other thread points at the same path and then shows a per-thread confirm dialog before runninggit worktree remove.Nothing cleans up on:
removeWorktreeisn't wired into the mobile client at all)And even on the one path that works, only the directory is removed —
git worktree removeleaves the branch behind, so you accumulate two kinds of litter.Current damage
~/.t3/worktreesis 670M:The
voidrepo is the clearest case — it's a pure chat sandbox, nothing is ever meant to land there:Every one sits at the same SHA as
main. Zero commits, zero divergence — five directories and five dangling branches produced by throwaway conversations. Reclaiming them today means deleting each thread individually and clicking through a confirm each time.Upstream status: filed, but going nowhere
Worth knowing before investing, because it means this workaround is likely to be needed for a while:
Feature requests — all closed, none implemented (no closing actor recorded, no linked merged PR, and the behavior verifiably doesn't exist in the code):
Implementation PRs — four attempts, zero merged, none with a review decision:
Ask
Build a workaround that lives here — a script, a Claude skill under
dot_claude/skills/, a scheduled job, or some combination. Approach is deliberately open; the goal is that sandbox repos likevoidstop growing without me babysitting them.Whatever the shape, it needs to answer: what's safe to remove, how is that decided, and how much do I have to be in the loop.
Constraints and hazards
These are the things that make a naive
git worktree prune/rm -rfwrong:Don't destroy work. A worktree with uncommitted changes, untracked files, stashes, or commits not reachable from its base is not litter. The
voidcase is easy precisely because HEAD equals the base SHA and the tree is clean — that's a provable safety condition, and it may be worth restricting the tool to only what it can prove.Threads bind to worktree paths. T3 Code stores
projection_threads.worktree_pathin~/.t3/userdata/state.sqlite. Removing a worktree out from under a live thread bricks it — I hit exactly this in Aug 2026 (raw ENOENT spawn errors on turn start; recovery meant recreating the worktree at the recorded path and branch). Anything that removes directories should cross-reference the thread table first, and must include archived threads — t3code's own orphan check reads a snapshot filtered byarchived_at IS NULL, which is an upstream bug I don't want to reproduce.Sessions can be running. Removing a worktree while its session is live permanently wedges the thread upstream (#4723). Check session state, or only operate when T3 Code isn't running.
Branches too. Directory-only cleanup leaves the
t3code/*branches. Deleting a branch that is merged or identical to its base is safe; anything else isn't.Per-repo policy varies.
voidis disposable.home-labhas real PR worktrees (pr-301-…,pr-304-…) that should be left alone. Whatever the mechanism, it should be possible to be aggressive on some repos and conservative or opt-out on others.Dry-run first. Given items 1–3, I want to see what it would do before it does it.
Nice to have
node_modulesis most of it.git worktree pruneterritory), and vice versa.