Repository navigation
Description
Note
🤖 Claude Opus 5.5 responding on behalf of Long Nguyen
Area: Storage cleanup (worktree cleanup)
Version: T3 Code 0.1.6, Linux.
Worktree cleanup deletes thread checkouts that hold work git status does not show. Before removing a checkout (by age, settled age, merge or thread deletion) it keeps it only when git status --porcelain --ignore-submodules=none prints a line, or, with worktreeKeepWhen: "any-local-files", when git ls-files --others --ignored --exclude-standard --directory prints an entry other than node_modules/ (localChangesReason in storageCleanup.ts). Related but separate: #23866 (nested worktrees).
Expected behavior. Cleanup keeps a checkout that holds any of:
- an ignored file (the default
uncommitted-changesdeletes it;any-local-fileskeeps it) - a commit on no remote (
git rev-list HEAD --not --remotesprints a line) - an edit hidden by
git update-index --assume-unchanged(git ls-files -vprintsh) - an edit hidden by
git update-index --skip-worktree(git ls-files -vprintsS) - an initialized submodule (
git submodule statusprints a line not starting with-); today the submodule path is force-removed - a failed git read (a non-zero exit from any check above); today a failed read does not block removal
Actual behavior. Observed on a scratch fixture: with any-local-files, checkouts (2), (3) and (4) are removed; with the default, (1) is removed too. Cases (5) and (6) are read from the source, not run.
Steps to reproduce (scratch fixture, run against a second server with its own --base-dir):
- Make a repo with one commit, a
.gitignorecontaining*.local, and a submodule (git submodule add <a local repo> sub && git commit -m sub). - Start six threads with
t3 thread start --worktree main --branch <name> <project> "ok". - In the checkouts: write
notes.local(1); commit a file (2);git update-index --assume-unchanged READMEthen edit README (3); the same with--skip-worktree(4);git submodule update --init(5); leave the sixth clean. - Set
storageCleanup.worktreeAfterDaysto 1, move the thread dates in the state database back 15 days, restart the server, wait past the 60 s settle grace, then edit astorageCleanupvalue to trigger a pass. - List what remains. The clean checkout is correctly removed. (1) to (5) should remain.
Impact. Silent loss of local work: ignored notes, unpushed commits and hidden edits, with no warning or list of what a pass will remove.
Workaround. Set worktreeKeepWhen: "any-local-files" everywhere a policy applies; that covers (1) only. Consider making it the default, or showing the checkouts a pass would remove before it removes them.
Activity