You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug]: Storage cleanup never prunes ~/.t3/scratch, so no-project thread folders grow forever #26406
Use T3 with "No project" threads (the scratch project) regularly.
Enable worktree cleanup with any retention (for example 3 days, or 1 day after a thread settles).
Let those threads go idle well past the retention, then run storage cleanup or wait for a sweep.
Inspect ~/.t3/scratch.
Expected behavior
Idle scratch thread folders should be removed by the same sweep with the same retention rules, and should appear in the storage cleanup report instead of growing silently.
Actual behavior
Nothing under ~/.t3/scratch is ever removed or listed. On my machine 40 folders accumulated in 11 days; after deleting the 6 older than 8 days by hand, 34 remain (75 MB). Disk usage grows without bound.
In the server, StorageCleanup.cleanWorktrees only builds roots from managedWorktreesDirectories (~/.t3/worktrees plus any configured directories), keeps any candidate failing roots.some(root => inside(root, realPath)) with "outside the managed worktree folder", and requires a linked worktree (.git file, git worktree remove from the project root). Scratch threads' worktreePath is ~/.t3/scratch/<name>, so every one is kept, and reportOrphanWorktrees iterates the same roots, so the panel never shows them either.
Impact
Minor bug or occasional failure. It becomes significant on machines that use no-project threads heavily, since the folders never expire.
Version or commit
0.1.4-nightly.20261027.3770
Environment
Arch Linux (CachyOS, kernel 7.3.0), desktop app.
Workaround
An external timer that deletes mtime-stale entries under ~/.t3/scratch, which is what I'm running now.
Related: #25843 and #25844 describe the same unbounded growth inside ~/.t3/worktrees; this one is about the separate scratch root. #25844's expected behavior already asks for a T3-owned scratch location that expires, which ~/.t3/scratch could be.
Additional evidence for #26406 on stable 0.1.5: Settings → Storage cleanup lists a completed, settled scratch thread with the reason “Thread is running or has pending work”, although no run or request is pending.
Diagnosis
In v0.1.5, storageCleanup.ts:789–793, thread.branch === null returns the same reason as a busy thread. The affected thread has branch: null, so this guard fires before checking managed roots or whether the folder is a linked Git worktree.
The same condition remains in main at 6575ce3. The report should distinguish missing branch metadata or scratch-folder eligibility from actual running/pending work.
Steps to reproduce
Have an existing completed scratch thread with branch: null and an existing folder.
Enable a worktree cleanup rule and run Storage cleanup.
Observe the misleading keep reason above.
The user observed this report. The investigation verified the current thread state and source; it did not rerun cleanup or create a fresh reproduction thread.
Version
Desktop and server: 0.1.5.
Environment
Windows Desktop connected to its WSL2 Linux x64 server; kernel 6.18.33.2-microsoft-standard-WSL2, Node v26.12.1.
Evidence
status: completed
activeRunId: null
pendingRequestCount: 0
branch: null
settledAt: 2026-10-16T12:51:30.382Z
latest run completedAt: 2026-10-16T12:09:34.964Z
live threads referencing the folder: 1
folder: ~/.t3/scratch/<thread-folder>
folder contents: empty; no .git; not a symlink
observed on: 2026-10-30
Fix applied or workaround
None applied. No database writes, configuration changes, source patches, or cleanup runs were made. Removing this verified-empty folder with rmdir is a possible local workaround; it does not fix scratch retention or the report reason.
Filed by
Prepared by Codex (GPT-6.1-Sol) in T3 Code via triage.
One addition to the diagnosis, from reading storageCleanup.ts on current main: scratch threads are kept even earlier than the roots check.
In cleanWorktrees, the eligibility gate thread.branch === null || !idle returns "thread is running or has pending work" before worktreePathReason ever runs — and scratch threads never have a branch, so every settled scratch thread is kept there with that misleading reason (the one @Qnt saw in the panel). The roots.some(...) keep the issue describes applies to candidates that get past that gate.
So a fix has two parts, both inside cleanWorktrees plus reportOrphanWorktrees:
A short non-git path for candidates under ~/.t3/scratch: the same activity/settled retention rules and report entries, but none of the git machinery that follows (branchChanged, localChangesReason, resolveCommit, remotes/default-branch checks), and a recursive directory delete instead of git worktree remove.
The scratch root added to the roots that reportOrphanWorktrees scans, so folders no thread owns show up in the panel too.
The managed-worktree logic would stay as it is; scratch would be handled beside it under the same worktreeCleanupEnabled / worktreeAfterDays / worktreeSettledAfterDays settings, which is the expected behavior this issue asks for.
Happy to implement once a maintainer confirms that scope — per CONTRIBUTING this changes what cleanup deletes, so I would rather agree on it first than open a PR that guesses.
One consequence the scope above doesn't cover: after a scratch folder is pruned, the thread can't start another turn. A pruned worktree is recreated from its branch at the next turn start (recreateMissingWorktree in ProviderTurnStartService.ts), but nothing recreates a scratch folder. The turn fails with "This thread's workspace folder no longer exists or is not a directory … Restore the folder at this path before retrying."
Scheduled tasks run into this. A weekly task bound to a No project thread leaves the thread inactive and settled for longer than a short retention between runs. On 0.1.10-nightly.20261112 a folder deleted by hand made both following weekly wakes fail within seconds. Pruning by the same retention rules would make that routine.
#21713 added the missing step: it recreates a missing folder directly under the scratch root before opening the session, and was verified against main. It was closed as stale on 12 Nov. A recreated folder starts empty, though, and unlike a worktree a scratch folder has nothing to restore its files from. Cleanup should probably also keep folders of threads that have an enabled scheduled task; storageCleanupThreadIdle doesn't currently check schedules.
Automated comment from a coding agent. claude-opus-5-5
Thanks — that's a consequence I hadn't accounted for, and it changes the proposal.
From reading ProviderTurnStartService.ts, recreateMissingWorktree only covers managed worktrees, so a pruned scratch folder leaves the next turn with nothing to restore, exactly as you describe. As far as I can see there are two separate ways to keep that from happening:
Don't prune it in the first place when the thread has an enabled scheduled task.storageCleanupThreadIdle doesn't look at schedules today, so a thread that only wakes weekly reads as idle and settled and gets pruned between runs. Keeping the folder present means turn start never sees a missing path, and the folder keeps its files.
Recreate a missing folder under the scratch root before opening the session, which is what fix(server): recreate deleted scratch folders when reopening threads #21713 did and verified against main. That also fixes the folder-deleted-by-hand case, but a recreated scratch folder starts empty, as you said — unlike a worktree it has nothing to restore from.
Do you want both, or only the retention side (1)? Either way I'd rather agree on the scope first than open a PR that guesses, so I'll hold off until you confirm.
Thanks for picking this one up — #36369 already settles the two questions I had (scheduled-task folders, and recreating a missing scratch folder), so I'm dropping my version.
Activity
mirsella commentedon Oct 28, 2026
Related: #25843 and #25844 describe the same unbounded growth inside
~/.t3/worktrees; this one is about the separate scratch root. #25844's expected behavior already asks for a T3-owned scratch location that expires, which~/.t3/scratchcould be.Qnt commentedon Oct 30, 2026
What happened
Additional evidence for #26406 on stable 0.1.5: Settings → Storage cleanup lists a completed, settled scratch thread with the reason “Thread is running or has pending work”, although no run or request is pending.
Diagnosis
In v0.1.5, storageCleanup.ts:789–793,
thread.branch === nullreturns the same reason as a busy thread. The affected thread hasbranch: null, so this guard fires before checking managed roots or whether the folder is a linked Git worktree.The same condition remains in main at 6575ce3. The report should distinguish missing branch metadata or scratch-folder eligibility from actual running/pending work.
Steps to reproduce
branch: nulland an existing folder.The user observed this report. The investigation verified the current thread state and source; it did not rerun cleanup or create a fresh reproduction thread.
Version
Desktop and server: 0.1.5.
Environment
Windows Desktop connected to its WSL2 Linux x64 server; kernel 6.18.33.2-microsoft-standard-WSL2, Node v26.12.1.
Evidence
Fix applied or workaround
None applied. No database writes, configuration changes, source patches, or cleanup runs were made. Removing this verified-empty folder with
rmdiris a possible local workaround; it does not fix scratch retention or the report reason.Filed by
Prepared by Codex (GPT-6.1-Sol) in T3 Code via triage.
joei4cm commentedon Nov 12, 2026
One addition to the diagnosis, from reading
storageCleanup.tson current main: scratch threads are kept even earlier than the roots check.In
cleanWorktrees, the eligibility gatethread.branch === null || !idlereturns"thread is running or has pending work"beforeworktreePathReasonever runs — and scratch threads never have a branch, so every settled scratch thread is kept there with that misleading reason (the one @Qnt saw in the panel). Theroots.some(...)keep the issue describes applies to candidates that get past that gate.So a fix has two parts, both inside
cleanWorktreesplusreportOrphanWorktrees:~/.t3/scratch: the same activity/settled retention rules and report entries, but none of the git machinery that follows (branchChanged,localChangesReason,resolveCommit, remotes/default-branch checks), and a recursive directory delete instead ofgit worktree remove.reportOrphanWorktreesscans, so folders no thread owns show up in the panel too.The managed-worktree logic would stay as it is; scratch would be handled beside it under the same
worktreeCleanupEnabled/worktreeAfterDays/worktreeSettledAfterDayssettings, which is the expected behavior this issue asks for.Happy to implement once a maintainer confirms that scope — per CONTRIBUTING this changes what cleanup deletes, so I would rather agree on it first than open a PR that guesses.
bdamokos commentedon Nov 13, 2026
One consequence the scope above doesn't cover: after a scratch folder is pruned, the thread can't start another turn. A pruned worktree is recreated from its branch at the next turn start (
recreateMissingWorktreeinProviderTurnStartService.ts), but nothing recreates a scratch folder. The turn fails with "This thread's workspace folder no longer exists or is not a directory … Restore the folder at this path before retrying."Scheduled tasks run into this. A weekly task bound to a No project thread leaves the thread inactive and settled for longer than a short retention between runs. On 0.1.10-nightly.20261112 a folder deleted by hand made both following weekly wakes fail within seconds. Pruning by the same retention rules would make that routine.
#21713 added the missing step: it recreates a missing folder directly under the scratch root before opening the session, and was verified against
main. It was closed as stale on 12 Nov. A recreated folder starts empty, though, and unlike a worktree a scratch folder has nothing to restore its files from. Cleanup should probably also keep folders of threads that have an enabled scheduled task;storageCleanupThreadIdledoesn't currently check schedules.Automated comment from a coding agent. claude-opus-5-5
joei4cm commentedon Nov 13, 2026
Thanks — that's a consequence I hadn't accounted for, and it changes the proposal.
From reading
ProviderTurnStartService.ts,recreateMissingWorktreeonly covers managed worktrees, so a pruned scratch folder leaves the next turn with nothing to restore, exactly as you describe. As far as I can see there are two separate ways to keep that from happening:storageCleanupThreadIdledoesn't look at schedules today, so a thread that only wakes weekly reads as idle and settled and gets pruned between runs. Keeping the folder present means turn start never sees a missing path, and the folder keeps its files.main. That also fixes the folder-deleted-by-hand case, but a recreated scratch folder starts empty, as you said — unlike a worktree it has nothing to restore from.Do you want both, or only the retention side (1)? Either way I'd rather agree on the scope first than open a PR that guesses, so I'll hold off until you confirm.
test(server): storage cleanup removes idle No project scratch folders (…
joei4cm commentedon Nov 13, 2026
Thanks for picking this one up — #36369 already settles the two questions I had (scheduled-task folders, and recreating a missing scratch folder), so I'm dropping my version.