Skip to content

fix(session): lazy worktree creation — primary only#413

Open
dimakis wants to merge 2 commits into
mainfrom
fix/lazy-worktree-creation
Open

fix(session): lazy worktree creation — primary only#413
dimakis wants to merge 2 commits into
mainfrom
fix/lazy-worktree-creation

Conversation

@dimakis

@dimakis dimakis commented Jun 27, 2026

Copy link
Copy Markdown
Owner

Summary

  • POST /api/sessions was creating worktrees across ALL configured repos (11) on every session start
  • Most sessions never write to secondary repos, causing massive branch/worktree proliferation
  • Cleanup session found: ~2,344 session branches, 82 worktrees, 4GB disk across 6 repos
  • Now creates only the primary worktree; secondary repos use existing on-demand guard

What changed

One function call in handleSessionCreate: replaced createAllWorktrees() (loops all repos) with createWorktree() (primary only). The on-demand creation in worktree-guard.ts already handles secondaries on first write — this path is proven, it's what startChat() already uses for Mitzo-native sessions.

Test plan

  • Type check passes (tsc --noEmit)
  • Start a new Claude Code session from mgmt — verify only primary worktree created
  • Write to a secondary repo — verify on-demand worktree creation works
  • Check secondary repos have no new branches after read-only sessions
  • Pre-existing suspend test failures are unrelated (setSessionState mock)

🤖 Generated with Claude Code

POST /api/sessions was creating worktrees across ALL configured repos
(11 repos) on every session start. Most sessions never write to
secondary repos, causing massive branch/worktree proliferation:
~2,344 session branches across 6 repos, 4GB of worktree disk.

Now creates only the primary worktree. Secondary repos are created
on-demand by the existing worktree guard when an agent first writes
to them (the guard and on-demand creation already work correctly).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@dimakis

dimakis commented Jun 27, 2026

Copy link
Copy Markdown
Owner Author

Centaur Review

Found 4 issue(s) (2 warning).

server/worktree.ts

Correct lazy-creation change, but leaves dead code in worktree.ts (createSessionWorktrees) and stale documentation in CLAUDE.md that still describes eager multi-repo creation.

  • 🟡 regressions (L311): The createSessionWorktrees() function in worktree.ts (lines 311-337) is now dead code — this PR removed its only call site (it was imported as createAllWorktrees in app.ts). The chat.ts version at line 506 is a different function with the same name. Consider removing the dead function from worktree.ts in this PR to avoid confusion. [fixable]

server/app.ts

Correct lazy-creation change, but leaves dead code in worktree.ts (createSessionWorktrees) and stale documentation in CLAUDE.md that still describes eager multi-repo creation.

  • 🟡 regressions (L472): CLAUDE.md line 220 states "All get worktrees at session start via createSessionWorktrees()." and line 224 says "POST /api/sessions creates worktrees for all repos." Both are now stale — this PR changes the endpoint to create only the primary worktree. The docs should be updated to reflect lazy secondary creation. [fixable]
  • 🔵 missing_tests (L475): No tests exercise the POST /api/sessions endpoint's worktree creation behavior. routes.test.ts covers other session endpoints but not this one. A test verifying that only the primary worktree is created (and that secondary repos are NOT eagerly created) would protect against regressions. [fixable]
  • 🔵 style (L472): The inline comment "Create only the primary worktree — secondary repos are created on-demand by the worktree guard when an agent first writes to them." explains WHAT, not WHY. The motivation (avoiding 55+ unused worktree dirs per the design doc) is the non-obvious part worth documenting if any comment is kept. [fixable]

When a worktree is cleaned up (stale GC or manual removal) but the
session is still alive, findAllowedWorktree() was returning the stale
entry. The agent would then try to write to a nonexistent directory
and get "path does not exist" errors.

Now checks existsSync before returning a worktree match. If the
directory is gone, evicts the entry from the session map so
on-demand creation can recreate it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@dimakis

dimakis commented Jun 27, 2026

Copy link
Copy Markdown
Owner Author

Centaur Review

Found 4 issue(s) (1 warning).

packages/harness/src/worktree-guard.ts

Core lazy-creation approach is sound, but stale eviction + on-demand recreation produces a garbled redirect suggestion when the agent was already targeting the worktree path — the path gets doubled because the code denies unconditionally after on-demand creation instead of re-checking allowance.

  • 🟡 bugs (L139): After stale eviction + on-demand recreation, the code always denies with a redirect — even when the agent was already targeting the correct worktree path. suggestWorktreePath produces a doubled path (e.g., .../worktrees/ID/.claude/worktrees/ID/file.ts) because the absolutePath already contains the worktree prefix. After on-demand creation succeeds, findAllowedWorktree should be re-invoked to check whether the path is now inside the recreated worktree, and if so, allow it instead of denying. [fixable]

packages/harness/__tests__/worktree-guard.test.ts

Core lazy-creation approach is sound, but stale eviction + on-demand recreation produces a garbled redirect suggestion when the agent was already targeting the worktree path — the path gets doubled because the code denies unconditionally after on-demand creation instead of re-checking allowance.

  • 🔵 missing_tests (L315): The assertion toContain('.claude/worktrees/2026-04-18-abc123/server/chat.ts') is too loose — it passes even when the suggestion path is doubled/garbled (the substring appears in both the correct and incorrect paths). A stricter assertion (e.g., checking result matches the exact expected suggestion or using toMatch with a regex anchored to the full path) would catch the doubled-path bug. [fixable]
  • 🔵 missing_tests (L290): Stale eviction tests only cover Write tools. The Bash/Shell code path (line 173 of worktree-guard.ts) also calls findAllowedWorktree with checkPath and can trigger stale eviction + on-demand creation, but has no dedicated test. [fixable]

server/worktree.ts

Core lazy-creation approach is sound, but stale eviction + on-demand recreation produces a garbled redirect suggestion when the agent was already targeting the worktree path — the path gets doubled because the code denies unconditionally after on-demand creation instead of re-checking allowance.

  • 🔵 style (L311): createSessionWorktrees() in server/worktree.ts is now dead code — the only import (createSessionWorktrees as createAllWorktrees in app.ts) was removed in this PR. No other file imports it. Consider removing it or marking it for cleanup. [fixable]

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.

1 participant