AI Agents SDE Task Viewer
      • Context
      • Plan
      • Prd
  1. Home
  2. AgentSDE
  3. agent-core
  4. gh-272
  5. plan
  6. plan.md
plan.md(4.0 KB)· Apr 3, 2026· 3 min read
  • Summary
  • Files
  • Steps
  • Verification
  • Risks

Plan: Persist worktree across phases, cleanup on task completion#

Summary#

Replace the per-phase create/destroy worktree lifecycle with an idempotent getOrCreateWorktree() that reuses existing worktrees. Move cleanup from PhaseRouterService.executePhase() finally block to InternalAdapterService when a task reaches terminal state (complete). This eliminates the root cause of #246 — git branch -D failing on checked-out branches causing permanent stuck loops.

Files#

FileActionDescription
src/worktree/worktree.service.tsmodifyReplace createWorktree() with idempotent getOrCreateWorktree(): reuse existing worktree, attach existing branch, or fresh-create. Remove aggressive pre-cleanup (lines 78-118).
src/worktree/worktree.service.spec.tsmodifyUpdate existing tests and add: worktree-exists-reuse, branch-exists-no-worktree, neither-exists-fresh-create cases.
src/phase-router/phase-router.service.tsmodifyCall getOrCreateWorktree() instead of createWorktree() (line 260). Remove cleanupWorktree() from finally block (lines 427-438).
src/phase-router/phase-router.service.spec.tsmodifyUpdate mock method names. Add test: worktree NOT cleaned up after phase.
src/internal-adapter/internal-adapter.service.tsmodifyInject WorktreeService. Call cleanupWorktree() when task status is set to complete in advanceAndEnqueue() (line 361).
src/internal-adapter/internal-adapter.module.tsmodifyImport WorktreeModule so WorktreeService is available for injection.
src/internal-adapter/internal-adapter.service.spec.tscreateUnit tests: worktree IS cleaned up on terminal state, worktree NOT cleaned up on phase advancement.

Steps#

  1. Refactor WorktreeService.createWorktree() → getOrCreateWorktree(): Remove lines 78-118 (aggressive pre-cleanup). Add three-way logic: (a) if worktree dir exists and is valid → git fetch origin, return path; (b) if branch exists but no worktree → git worktree add <path> <branch> (no -b); (c) neither exists → git worktree add -b <branch> <path> origin/master.
  2. Detect valid worktree: Use git worktree list --porcelain in the repo and check if worktreePath appears. If the directory exists but is not a valid worktree, log a warning and recreate.
  3. Update PhaseRouterService: Replace this.worktree.createWorktree() with this.worktree.getOrCreateWorktree() at line 260. Remove the entire finally block cleanup (lines 427-438).
  4. Add terminal-state cleanup in InternalAdapterService: Import WorktreeModule in internal-adapter.module.ts. Inject WorktreeService in InternalAdapterService. In advanceAndEnqueue() after setting status: 'complete' (line 361), call this.worktree.cleanupWorktree(task.repo, task.issue) wrapped in try/catch.
  5. Update unit tests for WorktreeService: Rename createWorktree() tests to getOrCreateWorktree(). Add three new test cases for the three-way idempotent logic. Remove tests that assert aggressive pre-cleanup behavior.
  6. Update unit tests for PhaseRouterService: Update mock to use getOrCreateWorktree. Remove assertions that cleanupWorktree is called after phase execution. Add assertion that cleanupWorktree is NOT called.
  7. Create InternalAdapterService unit tests: Add test that cleanupWorktree() is called when task reaches complete status. Add test that cleanupWorktree() is NOT called on normal phase advancement.

Verification#

  • npm run test — all unit tests pass with no regressions.
  • npm run lint — zero warnings.
  • npm run build — compiles cleanly.

Risks#

  • Concurrent phase invocations on same worktree: Mitigated by the queue's serial-per-task processing — only one phase runs at a time per issue.
  • Stale worktree state between phases: getOrCreateWorktree() runs git fetch origin on reuse, ensuring the worktree has latest refs. The Claude skill is responsible for checking out the correct branch/commit.
ContextPrd