Skip to content

fix(server): storage cleanup reaches archived threads' worktrees - #16473

Open
Info-Cado wants to merge 1 commit into
pingdotgg:mainfrom
Info-Cado:fix/storage-cleanup-archived-threads
Open

Info-Cado wants to merge 1 commit into
pingdotgg:mainfrom
Info-Cado:fix/storage-cleanup-archived-threads

Conversation

@Info-Cado

Copy link
Copy Markdown

Problem

Automatic storage cleanup never looks at archived threads, so their worktrees stay on disk under every worktree rule except deletion.

The V2 port of readThreads (apps/server/src/storageCleanup.ts:152-157, from #2829) read archived threads with getShellSnapshot({ location: "archive" }).threads. The store returns archived threads in archivedThreads and filters threads to archivedAt === null (ProjectionStore.ts SQL layer around line 5586, memory layer around line 5760). So that list is always empty. V1 included archived threads through getArchivedShellSnapshot(), and the storage guide does not exclude them.

Change

Read one unfiltered shell snapshot and use both threads and archivedThreads. The read moves into a small exported readStorageCleanupThreads(projections) so a test can run it against the real projection store.

Archived threads then go through the same checks as active ones: idle, single owner, terminals, sessions, managed path, clean Git state, ignored files, and the re-read before removal. A side effect, also a fix: an archived thread that shares a worktree now counts as a sharer. Before, cleanup could not see it, so the single-owner check and the deleted-thread path could treat a worktree it still used as unowned.

Scope and approval

There is no prior issue. I believe this qualifies as a very small, focused fix for an obvious bug: one read misuses the snapshot contract, and the fix restores V1 and documented behavior with no new setting or policy. I found it while adding evidence to #15146. It is independent of #15146/#15150 (status gate), #14742/#14847 (squash merges) and #16472 (subagent-shared worktrees). #15080 happens to make the same change inside a much larger rewrite.

Verification

  • New test V2 storage cleanup thread reads > includes archived threads seeds one active and one archived thread into ProjectionStore.layer on in-memory SQLite, then checks that readStorageCleanupThreads returns both.
  • With the old two-call read swapped back in, the test fails: expected [ 'thread-active' ] to deeply equal [ 'thread-active', 'thread-archived' ]. With the fix: vp test run apps/server/src/storageCleanup.test.ts, 10 passed.
  • tsc --noEmit in apps/server: no errors in the changed files. The only errors are 10 in src/process/externalLauncher.test.ts, which already fail on main at 9bd1d8009a. vp lint and vp fmt --check on both files are clean.
  • A Codex review (GPT-6.1-Sol, high) of the commit had no findings.

Not checked: a live sweep removing an archived thread's worktree in a running app. The rest of the sweep needs Git, settings, terminal and session services, and no test harness covers it today.

Written by Claude Opus 5.5 in T3 Code's Claude Code harness, reviewed by GPT-6.1-Sol in T3 Code's Codex harness.

🤖 Generated with Claude Code

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:S 10-29 changed lines (additions + deletions). labels Oct 6, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 8d4b734

Macroscope's review found this PR approvable — This is a small, well-scoped server bug fix that restores archived threads to the existing storage-cleanup pipeline without changing defaults or introducing new cleanup rules. The added projection-store test covers the corrected active-plus-archived thread read.

You can add or adjust custom eligibility rules. Learn more.

@coderabbitai

coderabbitai Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Path: .coderabbit.config.ts
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: bc6353c7-0a25-4923-8ebc-2d3da409fc1d
📥 Commits

Reviewing files that changed from the base of the PR and between 8d4b734 and e07ef53.

📒 Files selected for processing (2)
  • apps/server/src/storageCleanup.test.ts
  • apps/server/src/storageCleanup.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

The storage-cleanup code now reads active and archived threads from one shell snapshot. readThreads uses the new helper, and a test checks that it returns IDs for both thread states.

Changes

Storage cleanup thread reads

Layer / File(s) Summary
Read active and archived threads
apps/server/src/storageCleanup.ts, apps/server/src/storageCleanup.test.ts
The new readStorageCleanupThreads helper maps a shell snapshot to a combined active and archived thread list. readThreads uses the helper. A projection-backed test checks that both thread IDs are returned.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: juliusmarminge

Merge Risk: ⚪ Minimal · up to e07ef

Archived threads are included in the storage-cleanup read. No issue identified here needs resolution before merging.

Security Architecture Review

Security architecture risk: 🔵 Low · up to e07ef

The fix applies existing cleanup rules to archived checkouts and improves protection for shared checkouts. Existing removal safeguards remain in place. Concurrent resume and cleanup have not been verified end to end, so some lifecycle uncertainty remains.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • observed — The expanded destructive-operation scope is archived checkout directories eligible under configured cleanup policies. Removal still requires a linked worktree beneath a managed root, rejects symlinked worktree directories and protects project-root overlap. Archived references also block deletion-path cleanup of a still-owned checkout.

Security Findings and Attack Paths

  • inferred — The changed helper and test do not establish a new attacker-controlled entrypoint or a bypass of existing filesystem-removal controls. The demonstrated change is broader consumption of persisted owners through the existing cleanup path, rather than additional removal privileges.

Trust Boundaries and Controls

  • observed — Persisted thread path and branch values are checked against managed filesystem boundaries, project ownership and current Git state before reaching worktree removal. Cleanup revalidates owner identity, activity, sessions, branch, commit, ignored files and settings, then requests non-forced removal under a workspace lease.

Resilience and Maintainability Implications

  • inferred — Cleanup and provider startup are not demonstrated to be one atomic transition. Startup attempts missing-checkout recreation, and session opening checks that its working directory exists; those are counterevidence against silently proceeding with an absent checkout, not proof of complete race recovery. These mechanisms are unchanged from the base, and no introduced security failure was established.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main fix: storage cleanup now reaches archived threads' worktrees.
Description check ✅ Passed The description completes all required sections. It explains the problem, the implementation, scope justification, focused verification, known limitations, and the agent and harness used.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Approvability ✅ Passed The pull request changes only apps/server/src/storageCleanup.ts and its test. The code fixes a focused archive-read bug by combining snapshot.threads and snapshot.archivedThreads. It does not ch…
✨ Finishing Touches 💡 1
🧪 Generate unit tests (beta)
  • Create a new PR
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

The V2 port read archived threads from `getShellSnapshot({ location:
"archive" }).threads`, but the store returns them in `archivedThreads`,
so `.threads` was always empty there. Archived threads never became
cleanup candidates, and their worktrees stayed on disk.

Read one snapshot and include both `threads` and `archivedThreads`, as
V1 did with `getArchivedShellSnapshot`.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@juliusmarminge
juliusmarminge force-pushed the fix/storage-cleanup-archived-threads branch from 8d4b734 to e07ef53 Compare October 7, 2026 17:23

This branch has not been deployed

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

Labels

size:S 10-29 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants