feat(agent-manager): pre-warm worktrees to speed up session creation - #14050
feat(agent-manager): pre-warm worktrees to speed up session creation#14050marius-kilocode wants to merge 5 commits into
Conversation
Register a detached worktree in the background and claim it for a new session instead of running a full worktree checkout at creation time. Add per-phase creation timing and the "Pre-warm worktrees" Agent Manager setting, enabled by default. Also fix a claim path that could reset an existing branch whose name matched a pooled worktree directory, and raise the worktree add worker count for the fallback path.
Code Review SummaryStatus: 4 Issues Found | Recommendation: Address before merge Overview
Issue Details (click to expand)WARNING
SUGGESTION
Files Reviewed (20 files)
Fix these issues in Kilo Cloud Previous Review Summaries (2 snapshots, latest commit b5ab644)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit b5ab644)Status: No Issues Found | Recommendation: Merge Files Reviewed (3 files)
All four findings from the previous review were re-verified against Previous review (commit f90f52a)Status: 4 Issues Found | Recommendation: Address before merge Fix these issues in Kilo Cloud Overview
Issue Details (click to expand)WARNING
SUGGESTION
Files Reviewed (43 files)
Reviewed by deepseek-v4.1-flash · Input: 0 · Output: 0 · Cached: 0 Review guidance: REVIEW.md from base branch |
…ed slots Resolve the base ref and its commit before taking the shared git lock so a cold fetch cache never stalls user worktree operations. When adopting a leftover slot, use the worktree's real HEAD instead of persisted metadata. Apply the pre-warm toggle immediately instead of on Save, and drop fixed sleeps from the pool tests.
…-worktree-creation-latency
| const preparation = { pending: Promise.resolve() } | ||
| try { | ||
| const metadata = await sandboxSessionMetadata(this.connectionService.sandboxPreference, client, worktreePath) | ||
| preparation.pending = prepareDirectory(client, worktreePath).catch((err) => |
There was a problem hiding this comment.
WARNING: prepareDirectory is started but never awaited on the success path, and it creates a snapshot repository via the new snapshot.prepare endpoint. The teardown paths that can run after a worktree session is created - discardWorktree (multi-version sandbox reconciliation failure), the !session cleanup in createLifecycleWorktree, and tool-start's cleanupWorktree - never call kilocode.removeSnapshot (the only caller is deleteLifecycleWorktree). A failed or discarded worktree can therefore leave an orphaned snapshot repo under Global.Path.data/snapshot/<project>/<hash> that is never removed, and because the promise is not joined, teardown can race an in-flight snapshot.prepare that recreates the directory after cleanup. Consider awaiting/serializing preparation before teardown and ensuring every discard path removes the snapshot.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| timing?.mark("session") | ||
| return session | ||
| } catch (error) { | ||
| await preparation.pending |
There was a problem hiding this comment.
SUGGESTION: Awaiting preparation.pending here gates the user-visible error and the caller's worktree cleanup on Promise.allSettled, which has no timeout or cancellation. A slow or hung snapshot.prepare / mcp.status (MCP connect, large-repo seeding) now delays the failure message and leaves the worktree in place, whereas the error previously surfaced immediately. Consider bounding the wait (for example Promise.race with a timeout) or not blocking error reporting on background preparation.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| "--format=%(refname)", | ||
| "refs/kilo/snapshots", | ||
| ]) | ||
| if (refs.code === 0 && !refs.text.trim()) return false |
There was a problem hiding this comment.
WARNING: When the kilo-prepared marker exists but there are no refs/kilo/snapshots, this returns false and leaves the marker plus the seed artifacts written by prepare (seed-objects/ and objects/info/alternates) in place. KiloSnapshotCleanup.pending (packages/opencode/src/kilocode/snapshot/cleanup.ts:126) treats exactly seed-objects/alternates as materialization-in-progress, so KiloSnapshotCleanup.remove then fails with snapshot repository materialization is still pending (cleanup.ts:192). A worktree that is prepared but never tracked - for example a session created and then deleted before the first prompt, or a discarded multi-version worktree - can therefore never have its snapshot repository removed. Consider distinguishing prepared from materialization pending (drop the marker and let KiloSnapshotMaterialize.run clean the seed artifacts, or teach cleanup to recognize the prepared state).
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
| "5 seconds", | ||
| ) | ||
| yield* wait | ||
| expect(existsSync(staging)).toBe(false) |
There was a problem hiding this comment.
WARNING: wait only polls for objects/info/alternates to disappear, but KiloSnapshotMaterialize.run removes alternates and then seed-objects in sequence (materialize.ts:222-225). The immediate expect(existsSync(staging)).toBe(false) can observe the window between the two removals and flake. Poll for staging too, or use a single predicate that requires both paths to be gone.
Reply with @kilocode-bot fix it to have Kilo Code address this issue.
What Problem This Solves
Creating a new Agent Manager worktree blocks on
git worktree add, which checks out every tracked file. On the Kilo repository (about 10,000 tracked files) that checkout takes about 2.1 s, and it scales with repository size, so large repositories pay many seconds before a new session is usable.Why This Change Was Made
Register a detached worktree in the background and claim it for a new session with a cheap ref update instead of running a second full checkout at creation time. The behavior is scoped to the new "Pre-warm worktrees" Agent Manager setting (
kilo-code.new.agentManager.worktreePool), enabled by default. Per-phase creation timing was added so the remaining cost is measurable. The change also fixes a claim path that could reset an existing local branch whose name matched a prepared worktree directory, and raises the fallbackgit worktree addworker count.User Impact
Evidence
Creation timing measured through
WorktreeManager.createWorktreeon a local clone, median of 3 runs, macOS/APFS, no setup script.Raw git on the large clone: full
git worktree addabout 2.1 s, registration only about 75 ms, a ref-only claim about 70 ms, and a cold-indexgit statusabout 400 ms. Raising the fallback worker count from 2 to 4 saves about 150 ms.Limitations: these are worktree creation timings, not end-to-end time to first token. The model provider still dominates time to first token, and a configured setup script still runs after creation. Measurements were taken outside VS Code, so file watcher interaction is not included.