Uh oh!
There was an error while loading. Please reload this page.
feat(server): clean worktree build artifacts when threads settle - #8771
feat(server): clean worktree build artifacts when threads settle#8771IAmJSD wants to merge 6 commits into
Conversation
Settled worktree threads keep their node_modules, Cargo target, and framework cache directories around indefinitely, so parked threads eat disk. A new ThreadSettleCleanupReactor reacts to thread.settled events and deletes regenerable build artifacts from the thread's linked worktree, behind a new opt-in "Clean settled worktrees" server setting. Cleanup only touches linked worktrees (never a primary checkout), skips worktrees shared with another live thread, never follows symlinks, and never removes tracked files. Written by Claude Code. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Warning Your free Security trial is over. An organization admin can activate Security or dismiss this notice. Comment |
There was a problem hiding this comment.
Effect service conventions: the new ThreadSettleCleanupReactor service is introduced with the legacy Services/ + Layers/ split, a standalone ...Shape interface, and a ...Live layer export. The repo's canonical shape for new services (see orchestration/ThreadBackgroundLiveness.ts, relay/AgentAwarenessRelay.ts, workspace/WorkspacePaths.ts) is a single module exporting the tag with an inline interface, make, and layer. Details inline.
Posted via Macroscope — Effect Service Conventions
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want fixes drafted automatically? Bugbot Autofix can create code changes for findings. A team admin can enable Autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 1003ff8. Configure here.
Uh oh!
There was an error while loading. Please reload this page.
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — This PR introduces a new event-driven worktree cleanup capability with recursive filesystem deletion, production reactor wiring, and a user-facing product setting. Despite being opt-in and defaulting off, the feature’s scope and destructive side effect require human review. You can add or adjust custom eligibility rules. Learn more. |
Collapse the new reactor into a single canonical module (tag with inline interface, make, layer) per current Effect service conventions; re-check the projected settled state before cleaning so a stale queued settle event cannot clean under a thread that has since unsettled; and require Cargo.toml to be a regular file before treating a sibling target directory as a build artifact. Written by Claude Code. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
IAmJSD
commented
Aug 30, 2026
Addressed all three review findings in 35c7822 (fix written by Claude Code, as is this comment):
🤖 Generated with Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
A primary checkout cloned with --separate-git-dir also keeps a .git pointer file, so the entry type alone could let settle cleanup touch a primary checkout. Resolve the pointer's gitdir target and require its commondir file, which only a linked worktree's private git dir carries. Written by Claude Code. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
IAmJSD
commented
Aug 30, 2026
Fixed the separate-git-dir finding in 5a0e290 (written by Claude Code, as is this comment): The earlier Cursor finding ("cleanup runs after thread unsettles") was already addressed in 35c7822 via the 🤖 Generated with Claude Code |
There was a problem hiding this comment.
Reviewed for Effect service conventions. The service is now a single canonical module (apps/server/src/orchestration/ThreadSettleCleanupReactor.ts) with the interface inline in Context.Service, Service["Service"] references, make, and layer — the layout findings from the previous run are resolved. One remaining item: the reactor's new backend behavior has no focused test.
Posted via Macroscope — Effect Service Conventions
Uh oh!
There was an error while loading. Please reload this page.
Adds focused reactor tests driving a real thread.settled event through make with stubbed engine/repository/settings layers and real filesystem fixtures: cleanup fires only when the setting is enabled and every guard passes; disabled setting, unsettled or deleted threads, shared worktrees, and primary checkouts are all left untouched. Aligns the reactor with the sibling deletion reactor's seen-sequence watermark and drainThrough so the tests wait on receipts instead of sleeps. Written by Claude Code. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
IAmJSD
commented
Aug 30, 2026
Addressed the test-coverage finding in 6144972 (written by Claude Code, as is this comment): added focused reactor tests that drive a real 🤖 Generated with Claude Code |
There was a problem hiding this comment.
Effect Service Conventions: one finding — namespace-erasing aliased imports in the new reactor test. Everything else in the new ThreadSettleCleanupReactor module (canonical single-file layout, inline Context.Service interface, make/layer, environment-acquired dependencies, Foo["Service"] references) matches the conventions.
Posted via Macroscope — Effect Service Conventions
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Hardens settle cleanup against the two review criticals: artifact directories are now deleted only after git confirms they are ignored and hold no tracked files (anything unverified is skipped and logged), and isLinkedWorktreePath additionally requires the private git dir's gitdir back-reference to resolve to this worktree's own .git pointer, so a pointer borrowed from another worktree vouches for nothing. Artifact and reactor tests now run against real git repositories and worktrees, including an impostor-pointer case and unverified-skip cases. Written by Claude Code. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
IAmJSD
commented
Aug 30, 2026
Addressed both criticals in c8bf5e8 (written by Claude Code, as is this comment):
On the Medium watermark finding (event committed between the 🤖 Generated with Claude Code |
Drops the latestSequence head-noting from the settle cleanup reactor: seenSequence now only advances for events the subscription actually handed to the worker, so drainThrough can never report coverage of an event committed between the head read and subscription start. This reactor's drainThrough has no production callers to hang on pre-subscription sequences; reactors also start behind the command readiness gate, so no settle can commit before the subscription is live. Written by Claude Code. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
IAmJSD
commented
Aug 30, 2026
Follow-up on the Medium watermark finding — rather than leaving it at the written dismissal, 12d9550 (written by Claude Code, as is this comment) removes the 🤖 Generated with Claude Code |

Note
This PR was written by Claude Code (Claude Fable 5) under human direction — the code, the tests, and this description included.
What Changed
ThreadSettleCleanupReactor(modeled onThreadDeletionReactor) that reacts tothread.settleddomain events. When the settled thread runs in a linked worktree, it deletes regenerable build artifacts:node_modules, Cargotarget(only when next to aCargo.toml),.next,.nuxt,.turbo,.svelte-kit.worktreePathnot shared with another live thread; the path must be a linked worktree (.gitpointer file — a primary checkout is never touched). The scan never enters.gitor matched artifacts, never follows symlinks out of the worktree, and removal is best-effort per directory. Tracked files and uncommitted work are never at risk..git-pointer detection), unit coverage for the shared-worktree guard, and theOrchestrationReactorstart-order test updated.Scope note: cleanup fires on explicit
thread.settledevents; client-derived auto-settle (inactivity / merged PR) emits no domain event and is deliberately out of scope.Why
Settled worktree threads keep their install and build caches on disk forever — a handful of parked JS/Rust threads easily holds gigabytes. This reclaims that disk the moment a thread is parked while keeping the worktree itself intact (branch, tracked files, uncommitted changes), so an unsettled thread simply resumes and the setup script regenerates caches on the next turn.
Complementary to worktree pruning proposals like #4742: those remove whole worktrees under retention policies; this keeps the worktree and strips only regenerable artifacts, triggered by settling.
UI Changes
One standard settings row (title, description,
Switch) in Settings → General. No screenshots included; happy to add them on request.Checklist
Written by Claude Code with Claude Fable 5 (
claude-fable-5).🤖 Generated with Claude Code
Note
Medium Risk
Opt-in filesystem deletes on worktrees, but gated by linked-worktree verification, git ignore/tracked-file checks, and shared-thread guards; primary checkouts are never touched.
Overview
Adds an opt-in Clean settled worktrees setting (default off) and a
ThreadSettleCleanupReactorthat runs when athread.settleddomain event is emitted.When enabled, the reactor removes regenerable artifacts (
node_modules, framework caches, Cargotargetnext toCargo.toml) only from linked git worktrees, after checks that the thread is still settled, not deleted, not sharing the path with another live thread, and that git treats each directory as ignored with no tracked files. Scanning is depth-bounded, skips.gitand symlinks that escape the tree, and deletion is best-effort per directory.New
worktreeArtifactshelpers implement discovery, linked-worktree detection, and guarded removal. The reactor is started fromOrchestrationReactorand registered in the server reactor layer; Settings → General gets a toggle plus search/reset/dirty tracking.Reviewed by Cursor Bugbot for commit 12d9550. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Add
ThreadSettleCleanupReactorto clean worktree build artifacts on thread settlethread.settledevents and removes regenerable build artifact directories (e.g.node_modules,.next,.turbo) from linked git worktreesisLinkedWorktreePathdetects linked worktrees,findWorktreeArtifactDirectoriesdoes a bounded-depth walk over an allowlist, andremoveWorktreeArtifactsverifies candidates are git-ignored with no tracked files before recursive deletioncleanWorktreeArtifactsOnSettleboolean toServerSettings(defaultfalse) andServerSettingsPatch, with a toggle in the General settings UI and settings search entryOrchestrationReactor.start()and the live server layer compositionremoveWorktreeArtifactsrecursively force-deletes directories; a false positive in the git-ignore/track verification would delete uncommitted files. Reviewers should checkremoveWorktreeArtifactsin worktreeArtifacts.ts and the shared-worktree guardisWorktreeSharedWithAnotherThreadin ThreadSettleCleanupReactor.tsMacroscope summarized 12d9550.