diff --git a/lib/worktree.ts b/lib/worktree.ts index 1e68884b..8854a840 100644 --- a/lib/worktree.ts +++ b/lib/worktree.ts @@ -259,6 +259,11 @@ export class WorktreeManager { const entryPath = path.join(worktreeBase, entry); try { + // Skip recent worktrees (< 1 hour old) to avoid killing + // worktrees from concurrent test runs still in progress + const stat = fs.statSync(entryPath); + const ageMs = Date.now() - stat.mtimeMs; + if (ageMs < 3600_000) continue; fs.rmSync(entryPath, { recursive: true, force: true }); } catch { /* non-fatal */ } } diff --git a/test/helpers/session-runner.ts b/test/helpers/session-runner.ts index 7101e30c..c0f2ac00 100644 --- a/test/helpers/session-runner.ts +++ b/test/helpers/session-runner.ts @@ -303,12 +303,13 @@ export async function runSkillTest(options: { // Use resultLine for structured result data if (resultLine) { - if (resultLine.is_error) { + if (resultLine.subtype === 'success' && resultLine.is_error) { // claude -p can return subtype=success with is_error=true (e.g. API connection failure) exitReason = 'error_api'; } else if (resultLine.subtype === 'success') { exitReason = 'success'; } else if (resultLine.subtype) { + // Preserve known subtypes like error_max_turns even if is_error is set exitReason = resultLine.subtype; } } diff --git a/test/worktree.test.ts b/test/worktree.test.ts index be1533ae..47a58d23 100644 --- a/test/worktree.test.ts +++ b/test/worktree.test.ts @@ -231,6 +231,9 @@ describe('WorktreeManager', () => { spawnSync('git', ['worktree', 'remove', '--force', oldPath], { cwd: repo, stdio: 'pipe' }); // Recreate the directory to simulate orphaned state fs.mkdirSync(oldPath, { recursive: true }); + // Backdate mtime to simulate a stale worktree (> 1 hour old) + const staleTime = new Date(Date.now() - 7200_000); + fs.utimesSync(oldRunDir, staleTime, staleTime); // New manager should prune the old run's directory const newMgr = new WorktreeManager(repo);