Skip to content

feat(agent-manager): reclaim disk space from leftover worktree folders - #14255

Merged
kirillk merged 12 commits into
mainfrom
plucky-otter
Sep 17, 2026
Merged

kirillk merged 12 commits into
mainfrom
plucky-otter

Conversation

@kirillk

@kirillk kirillk commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Issue

Fixes #

No linked issue — this came from a report of an Agent Manager worktree list carrying 47 leftover folders worth 3.15 GB, with no way to find out what they were or clear them out safely.

Context

Every worktree Kilo creates lives under .kilo/worktrees/. When one is removed outside Kilo, a deletion is interrupted, or something writes into the folder after git stopped tracking it, the directory stays on disk while git no longer lists it as a worktree. They pile up quietly — gigabytes of them — and some still hold a git checkout with uncommitted work in it.

The only thing on offer before this PR was a notice with a single "clean up" action: no count, no size, no list. You could not tell whether clicking it would remove a stale empty directory or the last copy of work you had not pushed, so the safe move was to ignore it forever and let the folders accumulate. Cleanup was also only half a cleanup — the directory went away but its backend state and snapshot repository stayed behind, and a big folder froze the UI for as long as the delete took.

Implementation

Leftover folders are now something you can look at before you act on.

You can see what is there. The banner sits above the worktree list with the number of leftover folders and how much disk they are using. While that total is still being worked out it says so, and if it cannot be worked out it shows the count alone rather than inventing a number.

You can review before deleting. "Resolve…" opens a list of every leftover folder with its full path, its size, and whether it still contains a git checkout. Anything holding a checkout may hold uncommitted work, so it starts unselected — you have to opt in to deleting it. The explanation of where these folders come from starts collapsed behind a "Show more" link so it never crowds out the list.

Deleting is safe and stays out of the way. Removal happens in the background, so nothing freezes even on a huge folder, and each path is re-checked against git at the moment it is touched — a folder that became a real worktree in the meantime is skipped rather than deleted. Cleanup now also takes the backend state and snapshot repository with it, retries once if a directory reappears straight after removal, and finishes with a notification saying how many folders went. On JetBrains an open project window rooted at the folder is closed first.

It stops working when you stop caring. Measuring folder sizes is real disk reading, so it is cancelled the moment you start a delete, and whenever the list changes underneath it, instead of grinding through folders that are on their way out. Whatever is left afterwards gets measured again.

Both IDEs behave the same way throughout, and every new string is translated.

Two things worth knowing as a reviewer: the checkbox styling lands in the shared component, so it applies to every checkbox in the VS Code webview rather than only this dialog — that consistency is deliberate, and non-VS Code surfaces are unaffected. And one JetBrains cleanup call is absent from the pinned CLI version, so it quietly does nothing until that pin moves.

Screenshots / Video

JetBrains

Screen Shot 2026-09-17 at 12 40 33 PM Screen Shot 2026-09-17 at 12 40 55 PM

VSCode

GIF Recording 2026-09-17 at 1 53 12 PM

How to Test

  1. Create a few leftover folders under .kilo/worktrees/ — remove one with git worktree remove outside Kilo, or copy a directory in — and open the Agent Manager.
  2. Check the banner appears above the worktree list, says it is still measuring, then settles on a folder count and total size.
  3. Open Resolve…. Every folder should show its path, size, and the git-checkout flag, rows with a checkout should start unselected, and the explanation should start collapsed behind "Show more".
  4. Confirm the selection checkboxes read like the IDE's own, including the select-all box when only some rows are picked.
  5. Delete a subset. The UI should stay responsive, the notification should report how many were removed, and both the banner and the list should drop them. The remaining folders get re-measured.
  6. Repeat in the other IDE and confirm it behaves the same.

Automated coverage runs on both sides: VS Code typecheck, lint, and 6061 unit tests, plus the JetBrains typecheck and full frontend suite. Verified after merging the latest main into the branch.

Blocked checks and substitute verification

  • Screenshots could not be attached: gh gist create rejects binary files and no other image host is reachable here. Substituted by rendering the built stylesheet in a real browser and reading back the resulting checkbox colours before and after, which is what the description above is based on.
  • Root bun run lint fails in this workspace because a leftover, gitignored Agent Manager worktree from an unrelated session confuses the linter's config discovery. Unrelated to this diff. Substituted with the linter scoped to every file this PR touches: no errors, and the only warnings sit on lines this PR does not change.

Checklist

  • Issue linked above, or exception explained
  • Tests/verification described
  • Screenshots/video included for visual changes, or marked N/A
  • Changeset considered for user-facing changes
  • I personally reviewed the diff and can explain the changes, including any AI-assisted work.

Get in Touch

Leftover folders under .kilo/worktrees were surfaced as a notice with one
blind "clean up" action, so there was no way to see what a cleanup would
actually delete. Deleting also only removed the directory: backend state
and the snapshot repository were left behind, and a large tree blocked the
caller for as long as the removal took.

Both clients now report the count and total size, and open a dialog listing
each folder with its full path, size, and whether it still holds a git
checkout. Folders with a checkout may hold uncommitted work, so they start
unselected. Deletion stages a rename and reaps in the background, tears
down backend state, removes the snapshot repository, and stages once more
if a directory reappears straight after removal.

JetBrains also closes an open project frame rooted at the target before
staging, and picks up the CLI teardown/snapshot parity VS Code already had.
kilocode.teardownWorktree is absent from the pinned CLI spec, so it goes
through a raw request that no-ops until the pin advances.
The notice sat below every worktree, where a yellow headline read as an
error the list was reporting rather than a housekeeping prompt, and its
ghost CTA did not look clickable. Move it above the first worktree, keep
only the warning tint on the surface, and use an ordinary action button.

The dialog asked for a bulk delete of dozens of folders while explaining
nothing about where they came from, so add the same header explanation
the JetBrains dialog carries. The rest is VS Code chrome the shared
Dialog already implies: the header's 20px gutter on the body, one normal
font instead of small bold headers, the shared check glyph, and columns
sized to their content so the git checkout flag sits at the left edge of
its cell instead of adrift in an over-wide one.
…er worktree dialog

The leftover-worktree-folders checkbox selection column used three unrelated
visual states instead of a checkbox: checked was a light gray filled blob,
unchecked was a transparent box with a border, and the select-all header's
indeterminate state fell through to a third, unrelated look. Map the kilo-ui
checkbox to @vscode/webview-ui-toolkit's own tokens instead, and treat
indeterminate exactly like checked (a VS Code checkbox has no third visual
language for "some selected", only the glyph changes). Fallbacks reproduce
today's colors outside the VS Code webview, so Storybook and kilo-web-ui are
unaffected.

Also collapse the dialog's explanation behind a Show more / Show less link on
both platforms, so the three-bullet detail no longer eats a third of the
JetBrains dialog by default; the intro sentence stays visible either way. The
JetBrains banner's pending state ("47 leftover worktree folder(s)") now says
it is still calculating the total size instead of presenting the count as if
it were final, composed from the existing summary/calculating strings so no
new translation was needed there.
Comment thread packages/kilo-vscode/src/agent-manager/orphan-sizing.ts Outdated
Comment thread packages/kilo-ui/src/components/checkbox.css
Comment thread packages/kilo-vscode/src/agent-manager/WorktreeManager.ts
Comment thread packages/kilo-vscode/src/agent-manager/worktree-recovery.ts
Comment thread packages/kilo-vscode/tests/unit/agent-manager-arch.test.ts
Comment thread .changeset/orphan-dialog-native-checkbox-collapsible-help.md Outdated
@kilo-code-bot

kilo-code-bot Bot commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Files Reviewed (33 files)
  • packages/kilo-vscode/src/agent-manager/orphans/sizing.ts
  • packages/kilo-vscode/src/agent-manager/orphans/size.ts
  • packages/kilo-vscode/src/agent-manager/project/context.ts
  • packages/kilo-vscode/src/agent-manager/project/init.ts
  • packages/kilo-vscode/src/agent-manager/worktree-health.ts
  • packages/kilo-vscode/src/agent-manager/worktree-reconcile.ts
  • packages/kilo-vscode/src/agent-manager/worktree-recovery.ts
  • packages/kilo-vscode/tests/unit/orphan-sizing.test.ts
  • packages/kilo-vscode/tests/unit/orphan-size.test.ts
  • packages/kilo-vscode/tests/unit/orphan-dialog-logic.test.ts
  • packages/kilo-vscode/tests/unit/worktree-health.test.ts
  • packages/kilo-vscode/tests/unit/worktree-recovery.test.ts
  • packages/kilo-vscode/tests/unit/agent-manager-arch.test.ts
  • packages/kilo-vscode/tests/fixtures/orphan-help-toggle.tsx
  • packages/kilo-vscode/webview-ui/agent-manager/orphans/OrphanDialog.tsx
  • packages/kilo-vscode/webview-ui/agent-manager/orphans/OrphanNotice.tsx
  • packages/kilo-vscode/webview-ui/agent-manager/orphans/dialog-logic.ts
  • packages/kilo-vscode/webview-ui/agent-manager/ProjectSidebarBody.tsx
  • packages/kilo-vscode/webview-ui/agent-manager/SidebarBody.tsx
  • packages/kilo-jetbrains/backend/src/main/kotlin/ai/kilocode/backend/worktree/orphans/OrphanSize.kt
  • packages/kilo-jetbrains/backend/src/main/kotlin/ai/kilocode/backend/rpc/KiloWorktreeRpcApiImpl.kt
  • packages/kilo-jetbrains/backend/src/test/kotlin/ai/kilocode/backend/rpc/KiloWorktreeRpcApiImplTest.kt
  • packages/kilo-jetbrains/shared/src/main/kotlin/ai/kilocode/rpc/dto/orphans/OrphanDto.kt
  • packages/kilo-jetbrains/shared/src/main/kotlin/ai/kilocode/rpc/dto/WorktreeDto.kt
  • packages/kilo-jetbrains/shared/src/main/kotlin/ai/kilocode/rpc/KiloWorktreeRpcApi.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/agentManager/AgentManagerPanel.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/agentManager/orphans/OrphanBanner.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/agentManager/orphans/OrphanDialog.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/agentManager/worktree/KiloWorktreeService.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/agentManager/worktree/WorktreeController.kt
  • packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/agentManager/orphans/OrphanBannerTest.kt
  • packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/agentManager/orphans/OrphanDialogTest.kt
  • packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/testing/FakeWorktreeRpcApi.kt
Previous Review Summaries (5 snapshots, latest commit 53ab56a)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 53ab56a)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
packages/kilo-vscode/src/agent-manager/orphan-sizing.ts 115 missing excludes paths covered by the in-flight walk, but line 117 aborts that walk unconditionally before starting the replacement pass — so when the orphan set grows mid-walk, the aborted walk's paths land in neither known nor pending and nothing measures them until an unrelated reconcile runs. Wastes the walk already done and can leave the banner on "calculating size…" again.
Files Reviewed (4 files in this revision)
  • packages/kilo-vscode/src/agent-manager/orphan-sizing.ts - 1 issue
  • packages/kilo-vscode/tests/unit/orphan-sizing.test.ts
  • packages/kilo-vscode/tests/unit/agent-manager-arch.test.ts
  • packages/kilo-jetbrains/backend/src/test/kotlin/ai/kilocode/backend/rpc/KiloWorktreeRpcApiImplTest.kt

The previously reported early-return gap and the two verbatim-source assertions are resolved in this revision: the leave-check on pending now aborts a walk whose paths left the list even when this call's own missing is empty, the Kotlin test now cancels a genuinely in-flight walk against a timed baseline instead of cancelling before the call, and the arch test drives the real createProjectWiring construction path.

Fix these issues in Kilo Cloud

Previous review (commit 4515f66)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
packages/kilo-vscode/src/agent-manager/orphan-sizing.ts 95 The early return skips the in-flight abort, so a walk already running can record paths this reconcile just pruned; a directory later recreated at that path keeps a stale size that is never re-measured.
Files Reviewed (2 files in this revision)
  • packages/kilo-vscode/src/agent-manager/orphan-sizing.ts - 1 issue
  • packages/kilo-vscode/tests/unit/orphan-sizing.test.ts

Fix these issues in Kilo Cloud

Previous review (commit eed37cb)

Status: 3 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 2
Issue Details (click to expand)

WARNING

File Line Issue
packages/kilo-vscode/src/agent-manager/orphan-sizing.ts 55 A later reconcile that finds the same orphan set skips sizing and leaves the fresh report unsized, so the banner still falls back to 'calculating size…' with no pass coming to answer it.

SUGGESTION

File Line Issue
packages/kilo-jetbrains/backend/src/test/kotlin/ai/kilocode/backend/rpc/KiloWorktreeRpcApiImplTest.kt 217 The new cancellation test cancels the job before withContext, so the walk never runs and it passes regardless of the cooperative cancellation it documents.
packages/kilo-vscode/tests/unit/agent-manager-arch.test.ts 290 The two added tests assert verbatim source text again, including the exact sized wiring expression the earlier review asked to loosen.

Also verified against HEAD eed37cbcd4: the JetBrains banner 0 B failure case and the duplicate changeset are fixed, and the arch-test prop assertion on line 266 was loosened. Findings left on files this revision does not touch (checkbox.css, WorktreeManager.ts, the JetBrains reap/symlink paths) were not re-verified here and remain in the review history.

Files Reviewed (42 files in this revision)
  • .changeset/worktree-orphan-cleanup-dialog.md
  • packages/kilo-jetbrains/backend/src/main/kotlin/ai/kilocode/backend/rpc/KiloWorktreeRpcApiImpl.kt - cooperative cancellation in orphanSizes/walkSize
  • packages/kilo-jetbrains/backend/src/test/kotlin/ai/kilocode/backend/rpc/KiloWorktreeRpcApiImplTest.kt - 1 issue
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/agentManager/worktree/KiloWorktreeService.kt
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/agentManager/worktree/OrphanBanner.kt - banner failure handling verified
  • packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/agentManager/worktree/OrphanBannerTest.kt
  • packages/kilo-vscode/src/agent-manager/AgentManagerProvider.ts
  • packages/kilo-vscode/src/agent-manager/orphan-sizing.ts - 1 issue
  • packages/kilo-vscode/src/agent-manager/project/context.ts
  • packages/kilo-vscode/src/agent-manager/project/init.ts
  • packages/kilo-vscode/src/agent-manager/project/wiring.ts
  • packages/kilo-vscode/src/agent-manager/worktree-reconcile.ts
  • packages/kilo-vscode/src/agent-manager/worktree-recovery.ts
  • packages/kilo-vscode/tests/unit/agent-manager-arch.test.ts - 1 issue
  • packages/kilo-vscode/tests/unit/orphan-dialog-logic.test.ts
  • packages/kilo-vscode/tests/unit/orphan-sizing.test.ts
  • packages/kilo-vscode/tests/unit/worktree-recovery.test.ts
  • packages/kilo-vscode/webview-ui/agent-manager/OrphanDialog.tsx
  • packages/kilo-vscode/webview-ui/agent-manager/OrphanNotice.tsx
  • packages/kilo-vscode/webview-ui/agent-manager/orphan-dialog-logic.ts
  • packages/kilo-vscode/webview-ui/agent-manager/i18n/*.ts (21 locales)
  • packages/kilo-vscode/webview-ui/src/types/messages/extension-messages.ts

Fix these issues in Kilo Cloud

Previous review (commit 93658c3)

Status: 10 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 6
SUGGESTION 4
Issue Details (click to expand)

WARNING

File Line Issue
packages/kilo-vscode/src/agent-manager/orphan-sizing.ts 45 Early return on an unchanged path set discards sizes computed for the previous report objects, so a later reconcile drops all bytes and the UI reverts to "Calculating…" permanently.
packages/kilo-ui/src/components/checkbox.css 21 Checked/indeterminate fill uses --vscode-checkbox-background/border (the unchecked tokens) instead of --vscode-checkbox-selectBackground/selectBorder, so checked and unchecked render identically apart from the glyph.
packages/kilo-jetbrains/backend/src/main/kotlin/ai/kilocode/backend/rpc/KiloWorktreeRpcApiImpl.kt 923 Blocking execute() on the no-read-timeout API client runs while holding the per-repository lock; a hung CLI wedges removal and later mutations for that repo.
packages/kilo-jetbrains/backend/src/main/kotlin/ai/kilocode/backend/rpc/KiloWorktreeRpcApiImpl.kt 875 Post-reap reappearance retry re-stages/reaps without re-checking git worktree list, so a worktree re-created at the same deterministic path can be deleted.
packages/kilo-vscode/src/agent-manager/WorktreeManager.ts 779 Same retry path calls detach()/fs.rm without the registeredPaths() guard that detachOrphanDirectory enforces before the first rename.
packages/kilo-jetbrains/backend/src/main/kotlin/ai/kilocode/backend/rpc/KiloWorktreeRpcApiImpl.kt 1006 orphanDirs follows symlinks and closeIfOpen is symlink-aware, so a symlinked orphan can close/dispose an unrelated open project.

SUGGESTION

File Line Issue
packages/kilo-vscode/webview-ui/agent-manager/OrphanDialog.tsx 73 Selection is seeded once from props.orphans; a leftover row appearing while the dialog is open never gets the default checked state.
packages/kilo-vscode/src/agent-manager/worktree-recovery.ts 152 Cancelled or stale paths are counted as failures, producing "Failed to remove N folders" / "Removed X of Y, 0 failed".
packages/kilo-vscode/tests/unit/agent-manager-arch.test.ts 266 Assertions match exact source/CSS substrings and indexOf ordering rather than rendered behaviour.
packages/kilo-jetbrains/backend/src/main/kotlin/ai/kilocode/backend/rpc/KiloWorktreeRpcApiImpl.kt 963 visitFileFailed → CONTINUE silently under-reports size, contradicting the orphanSizes "fails to walk ⇒ omitted" contract.
Files Reviewed (5 files changed in this revision)
  • .changeset/worktree-orphan-cleanup-dialog.md
  • packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/agentManager/worktree/OrphanBanner.kt - failed-size-pass 0 B finding resolved
  • packages/kilo-jetbrains/frontend/src/test/kotlin/ai/kilocode/client/agentManager/worktree/OrphanBannerTest.kt
  • packages/kilo-vscode/tests/unit/agent-manager-arch.test.ts
  • .changeset/orphan-dialog-native-checkbox-collapsible-help.md - deleted (finding resolved)

All findings above are carried forward from the previous review after re-verification against the current HEAD; the JetBrains banner 0 B and duplicate-changeset findings were resolved in this revision.

Fix these issues in Kilo Cloud

Previous review (commit a813609)

Status: 12 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 7
SUGGESTION 5
Issue Details (click to expand)

WARNING

File Line Issue
packages/kilo-vscode/src/agent-manager/orphan-sizing.ts 45 Early return on an unchanged path set discards sizes computed for the previous report objects, so any later reconcile drops all bytes and the UI reverts to "Calculating…" permanently.
packages/kilo-ui/src/components/checkbox.css 21 Checked/indeterminate fill uses --vscode-checkbox-background/border (the unchecked tokens) instead of --vscode-checkbox-selectBackground/selectBorder, so checked and unchecked render identically apart from the glyph.
packages/kilo-jetbrains/backend/src/main/kotlin/ai/kilocode/backend/rpc/KiloWorktreeRpcApiImpl.kt 923 Blocking execute() on the no-read-timeout API client runs while holding the per-repository lock; a hung CLI wedges removal and later mutations for that repo.
packages/kilo-jetbrains/backend/src/main/kotlin/ai/kilocode/backend/rpc/KiloWorktreeRpcApiImpl.kt 875 Post-reap reappearance retry re-stages/reaps without re-checking git worktree list, so a worktree re-created at the same deterministic path can be deleted.
packages/kilo-vscode/src/agent-manager/WorktreeManager.ts 779 Same retry path calls detach()/fs.rm without the registeredPaths() guard that detachOrphanDirectory enforces before the first rename.
packages/kilo-jetbrains/frontend/src/main/kotlin/ai/kilocode/client/agentManager/worktree/OrphanBanner.kt 62 A failed/partial size pass sums to 0, and sizedPaths blocks a retry, so the banner presents 0 B as final for folders that were never measured.
packages/kilo-jetbrains/backend/src/main/kotlin/ai/kilocode/backend/rpc/KiloWorktreeRpcApiImpl.kt 1006 orphanDirs follows symlinks and closeIfOpen is symlink-aware, so a symlinked orphan can close/dispose an unrelated open project.

SUGGESTION

File Line Issue
packages/kilo-vscode/webview-ui/agent-manager/OrphanDialog.tsx 73 Selection is seeded once from props.orphans; a leftover row appearing while the dialog is open never gets the default checked state.
packages/kilo-vscode/src/agent-manager/worktree-recovery.ts 152 Cancelled or stale paths are counted as failures, producing "Failed to remove N folders" / "Removed X of Y, 0 failed".
packages/kilo-vscode/tests/unit/agent-manager-arch.test.ts 266 New assertions match exact source/CSS substrings and indexOf ordering rather than rendered behaviour.
packages/kilo-jetbrains/backend/src/main/kotlin/ai/kilocode/backend/rpc/KiloWorktreeRpcApiImpl.kt 963 visitFileFailed → CONTINUE silently under-reports size, contradicting the orphanSizes "fails to walk ⇒ omitted" contract.
.changeset/orphan-dialog-native-checkbox-collapsible-help.md 2 Three overlapping changesets for one feature; this one omits the JetBrains behaviour it changes.
Files Reviewed (90 files)
  • VS Code host: AgentManagerProvider.ts, WorktreeManager.ts, host.ts, vscode-host.ts, provider-lifecycle.ts, orphan-size.ts, orphan-sizing.ts, worktree-health.ts, worktree-reconcile.ts, worktree-recovery.ts, types.ts, project/context.ts, project/init.ts
  • VS Code webview: OrphanDialog.tsx, OrphanNotice.tsx, ProjectSidebarBody.tsx, SidebarBody.tsx, orphan-dialog-logic.ts, agent-manager.css, i18n/*.ts (21 locales), message type contracts
  • Shared UI: packages/kilo-ui/src/components/checkbox.css
  • JetBrains backend/shared: KiloWorktreeRpcApiImpl.kt, WorktreeTrash.kt, KiloWorktreeRpcApi.kt, WorktreeDto.kt
  • JetBrains frontend: OrphanBanner.kt, OrphanDialog.kt, AgentManagerPanel.kt, KiloWorktreeService.kt, WorktreeController.kt, WorktreeDiagnosticsAction.kt, 19 KiloBundle*.properties
  • Tests: TS unit/arch/fixture tests, Kotlin backend/frontend tests
  • Changesets (3)

Fix these issues in Kilo Cloud


Reviewed by deepseek-v4.1-flash · Input: 0 · Output: 0 · Cached: 0

Review guidance: REVIEW.md from base branch main

…e walk falls short

The leftover-folders banner claimed a definitive "· 0 B" whenever the size pass
failed. orphanSizes catches its errors and answers an empty map, and requestSizes
cached that as the answer, which also marked the path set as sized so it never
asked again. A partial answer had the same shape of bug: the RPC contract omits
paths it cannot walk, so summing them silently undercounted.

Publish a total only when every path was actually measured, and distinguish "still
walking" from "walked and came up short" — the banner shows the size when known,
says it is calculating only while a walk is genuinely in flight, and otherwise
falls back to the bare count rather than asserting a wrong number. Remember a
failed path set so the failing walk is not re-run on every refresh, which is what
naively dropping the cache would have caused.

Also loosen the arch assertion added in the previous commit, which pinned the
whole OrphanHelp prop expression verbatim, and fold this PR's changeset into the
existing leftover-cleanup one so the JetBrains half of the change is covered by a
release note.
@kirillk kirillk changed the title fix(agent-manager): native checkboxes and collapsible help for leftover worktree dialog feat(agent-manager): review and delete leftover worktree folders Sep 17, 2026
kirillk and others added 3 commits September 17, 2026 11:19
…the set changes

Choosing to delete a leftover folder left the size walk running over it. Both
clients only reacted to a superseded path set, and only after the fact: VS Code
aborted at the reconcile that follows a delete, by which point the directories
were already renamed away, and JetBrains never cancelled at all -- requestSizes
dropped its Job, so the walk always ran to completion just to be discarded on
arrival, and walkSize had no cancellation check for a blocking walkFileTree that
Dispatchers.IO cannot interrupt.

Sizing now stops the moment the user commits to a delete, and restarts for
whatever is still on disk afterwards. VS Code pauses the tracker for the whole
delete rather than merely aborting, because the delete re-reconciles before it
touches anything and that reconcile would otherwise start a fresh pass over the
doomed set; the pause forgets the measured set so the closing reconcile actually
re-walks the survivors instead of skipping them as already done. That closing
reconcile is now unconditional, since the pass has to be restarted even when
nothing was removed. JetBrains keeps the Job, cancels it on delete and on
dispose, and cancels any pass still in flight before starting a new one. The
backend cooperates: orphanSizes re-checks between paths and hands walkSize an
isActive probe, and a walk that stops early answers null so a partial sum is
never recorded as a measurement. KiloWorktreeService rethrows
CancellationException instead of turning a cancelled pass into the empty map that
means "this walk failed".
@kirillk kirillk changed the title feat(agent-manager): review and delete leftover worktree folders feat(agent-manager): reclaim disk space from leftover worktree folders Sep 17, 2026
… forever

The banner never left "calculating size…" in VS Code. Two separate reasons.

The size pass is started by whichever reconcile runs first, and for the Agent
Manager that is initContextState -- which never passed the push callback, unlike
reconcileAndPush. So the walk finished (35s for 58 folders totalling 100 GB on
the reporting machine), wrote its numbers onto the report, and nothing told the
webview. No later reconcile recovered it either: the tracker had already recorded
that path set, so every subsequent pass short-circuited and the callback that did
exist was never reached. Sizing now notifies through a `sized` hook on the
project context, wired once where contexts are built, so every reconcile path
delivers its results and the per-call callback is gone.

Second, the banner treated "no size" as "still measuring", but the walk omits a
directory it cannot read rather than failing the batch, so a single unreadable
folder among dozens would have kept the affordance up for good even with the push
in place. A completed pass now marks every folder it covered as settled, with a
size when it got one, and the UI distinguishes measuring from measured-and-
unknown: the banner drops to the plain count, and the dialog's rows, footer and
delete button say the size is unknown instead of pretending to still be working.

The second cause did not reproduce on the reporting machine -- all 58 folders
measured cleanly -- but one unreadable folder is enough to reintroduce the
symptom, so both paths are fixed and covered.
Comment thread packages/kilo-vscode/src/agent-manager/orphan-sizing.ts Outdated
Comment thread packages/kilo-vscode/tests/unit/agent-manager-arch.test.ts Outdated
The banner showed a total for one poll interval and then went back to
"calculating size…" for good.

Sizes only ever existed as a mutation on the report's orphan objects, and every
reconcile rebuilds those objects from scratch, so the routine worktree-health poll
threw the numbers away. The tracker meanwhile still remembered that it had already
sized that path set, so the pass that would have recomputed them short-circuited:
the sizes were gone and nothing was ever going to fetch them again.

Measurements now live on the tracker, which outlives any single report, and are
re-applied to whatever objects the current reconcile built. Only directories that
have never been measured are walked, so a poll costs nothing and one new leftover
folder no longer re-reads the other forty -- on the reporting repo that is 58
folders and 100 GB of walking saved per poll. Paths that leave the list are
dropped from the cache, so it cannot grow for the life of the window and a folder
that is deleted and recreated is measured again rather than resurrecting a stale
number.

A delete keeps the cache on purpose: removing one folder does not change the size
of the others, so the reconcile that follows can show a correct total immediately
instead of re-reading everything that survived.
Comment thread packages/kilo-vscode/src/agent-manager/orphans/sizing.ts
…der leaves and rejoins the list

Review follow-up on the sizing cache added in the previous commit.

A walk in flight was only aborted when the reconcile that ran while it was
running itself needed a walk of its own. If every path that walk covered left
the orphan list at once -- removed outside Kilo, all in the same reconcile --
that reconcile's own missing set was empty, the early return fired first, and
the abort never happened. The walk then landed later and wrote its answer into
the cache anyway, for a path nothing had listed as an orphan for several
reconciles. If that path reappeared, the stale number applied immediately, with
no new walk ever running to correct it, because the cache already had an entry
for it.

The tracker now records which paths a walk is measuring and checks, on every
call, whether any of them have left the current list -- aborting immediately if
so, regardless of whether this call needs a walk of its own. Also fixes the
pause path, which needed to forget the same bookkeeping or a delete followed by
a resume would have permanently excluded whatever was mid-walk when the pause
began.

Separately hardens the two tests from the same review round that exercised the
wrong thing without either failing: the JetBrains cancellation test cancelled a
job before the suspend call was ever reached, proving only that `withContext`
on a dead job throws, not that the cooperative checks inside the walk do
anything -- it now times an uncancelled baseline over a large tree and shows a
cancelled pass finishes in a fraction of that time. The wiring arch test
asserted a literal `sized: (ctx) => opts.pushState(ctx)` source string, which
the previous review round had already asked this file to stop doing elsewhere
-- it now drives `createProjectWiring` for real and asserts the push actually
happens.
Comment thread packages/kilo-vscode/src/agent-manager/orphans/sizing.ts
…ed modules

Move every leftover-worktree-folder ("orphan") file into its own module on
both clients, per review feedback on this PR:

- VS Code: src/agent-manager/orphans/{size,sizing}.ts and
  webview-ui/agent-manager/orphans/{OrphanDialog.tsx,OrphanNotice.tsx,
  dialog-logic.ts}, previously flat in their parent directories.
- JetBrains shared: OrphanDto/OrphanKind/OrphanRemoveResultDto/
  RemoveOrphansResultDto extracted from the large WorktreeDto.kt into their
  own ai.kilocode.rpc.dto.orphans package.
- JetBrains frontend: OrphanBanner.kt/OrphanDialog.kt (+ tests) moved from
  ...agentManager.worktree into ai.kilocode.client.agentManager.orphans.
- JetBrains backend: the self-contained orphanSizes/walkSize filesystem walk
  extracted out of the 1972-line KiloWorktreeRpcApiImpl.kt into
  ai.kilocode.backend.worktree.orphans.OrphanSize.kt; the impl now delegates.

Worktree reconcile/classification and the removeOrphans staging/reap path
stay where they are on both clients: they share machinery with regular
worktree lifecycle management (registration checks, locks, the same
stage-rename-and-reap primitive) rather than being orphan-specific, mirroring
the existing split between orphan detection and worktree health classification.

Pure move + extraction, no behavior change. Verified with the full VS Code
unit suite (6071 tests) and the full JetBrains shared/backend/frontend suite
(4994 tests), plus typecheck/lint/knip on both sides.
…nt pass when the orphan set grows

Addresses the still-open kilo-code-bot review comment on this PR
(orphan-sizing.ts, "The replacement pass drops the paths of the walk it
just aborted, so they stop being measured at all").

`missing` excluded any path already covered by a still-trustworthy
in-flight walk (`pending`), but the very next statement unconditionally
aborted that walk before starting a new pass over `missing` alone. When the
orphan set grew while a walk was running, the growing call would abort the
walk anyway to widen it, discard the aborted walk's result, and start the
new one only over the newly-missing paths — dropping the excluded pending
paths from both `known` and `pending` with nothing left to re-request them.
Affected paths would show "calculating size…" until some unrelated reconcile
happened to re-walk them, which is not guaranteed to ever happen.

Fold `state.pending` into the new walk's target set whenever a replacement
pass has to abort it, so the new pass covers everything the aborted one did.

Added a regression test that grows the orphan set while the first walk is
still in flight (the existing grow test at orphan-sizing.test.ts awaited the
first pass first, so it never exercised this). Confirmed it fails on the
prior code and passes with the fix.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants