Skip to content

Add a workaround for T3 Code's missing worktree cleanup #11

Description

@kelchm

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:

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions