fix: resume no longer clears worktrees
This commit is contained in:
9
index.ts
9
index.ts
@@ -250,6 +250,7 @@ async function executePlanBatches(
|
|||||||
mode: ExecutionMode,
|
mode: ExecutionMode,
|
||||||
sendChatMessage?: SendChatMessage,
|
sendChatMessage?: SendChatMessage,
|
||||||
projectDir?: string,
|
projectDir?: string,
|
||||||
|
isResume?: boolean,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// Write loop-active marker so a session reload can detect an interrupted
|
// Write loop-active marker so a session reload can detect an interrupted
|
||||||
// loop and resume it (in-process agent sessions die on reload — the marker
|
// loop and resume it (in-process agent sessions die on reload — the marker
|
||||||
@@ -268,7 +269,12 @@ async function executePlanBatches(
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Clean up stale worktrees from interrupted runs before starting.
|
// Clean up stale worktrees from interrupted runs before starting.
|
||||||
if (config.execution.worktrees !== "never" && projectDir) {
|
// On resume this MUST be skipped: an interrupted in-progress task's
|
||||||
|
// worktree still carries its committed branch, which createWorktree()
|
||||||
|
// reuses to continue the task rather than restarting from scratch.
|
||||||
|
// The stale-worktree sweep only runs for fresh loops so concurrent
|
||||||
|
// loops (other PRDs) are still scoped out via the prdKey filter above.
|
||||||
|
if (!isResume && config.execution.worktrees !== "never" && projectDir) {
|
||||||
const removed = cleanupStaleWorktrees(
|
const removed = cleanupStaleWorktrees(
|
||||||
projectDir,
|
projectDir,
|
||||||
config.paths.stateDir,
|
config.paths.stateDir,
|
||||||
@@ -944,6 +950,7 @@ async function resumeLoop(
|
|||||||
mode,
|
mode,
|
||||||
sendChatMessage,
|
sendChatMessage,
|
||||||
projectDir,
|
projectDir,
|
||||||
|
true, // isResume — preserve in-progress worktrees, continue them
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!options?.skipFinalStatus) {
|
if (!options?.skipFinalStatus) {
|
||||||
|
|||||||
@@ -153,28 +153,41 @@ export function createWorktree(
|
|||||||
): WorktreeHandle | null {
|
): WorktreeHandle | null {
|
||||||
if (!isGitRepo(mainDir)) return null;
|
if (!isGitRepo(mainDir)) return null;
|
||||||
|
|
||||||
const ref = baseRef ?? getGitHead(mainDir);
|
|
||||||
if (!ref) return null;
|
|
||||||
|
|
||||||
const safeId = safeBranchSuffix(taskId);
|
const safeId = safeBranchSuffix(taskId);
|
||||||
const slug = taskTitle ? slugifyTitle(taskTitle) : "";
|
const slug = taskTitle ? slugifyTitle(taskTitle) : "";
|
||||||
const branch = slug || `ralpi/${prdKey}/${safeId}`;
|
const branch = slug || `ralpi/${prdKey}/${safeId}`;
|
||||||
const wtDir = worktreePath(mainDir, stateDir, prdKey, taskId);
|
const wtDir = worktreePath(mainDir, stateDir, prdKey, taskId);
|
||||||
|
|
||||||
|
// Prune metadata for worktree directories that no longer exist on disk
|
||||||
|
// (e.g. from a crashed previous run that left stale `.git/worktrees/` entries).
|
||||||
|
git("worktree prune", mainDir);
|
||||||
|
|
||||||
|
// ── Reuse an already-registered worktree (resume) ──
|
||||||
|
// A resumed loop skips `cleanupStaleWorktrees`, so the interrupted task's
|
||||||
|
// worktree — and the branch carrying its committed work — survives. Reuse
|
||||||
|
// it instead of destroying and recreating from the base ref; otherwise the
|
||||||
|
// prior session's commits are lost and the task restarts from scratch.
|
||||||
|
const existing = git(`worktree list --porcelain`, mainDir);
|
||||||
|
if (existing && existing.includes(`worktree ${wtDir}`)) {
|
||||||
|
// The worktree is registered — sanity-check it's a valid checkout.
|
||||||
|
if (getGitHead(wtDir)) {
|
||||||
|
return { dir: wtDir, branch, mainDir };
|
||||||
|
}
|
||||||
|
// Registered but broken (dir gone / checkout corrupt) — drop its
|
||||||
|
// metadata and fall through to fresh creation below.
|
||||||
|
git(`worktree remove --force "${wtDir}"`, mainDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fresh creation.
|
||||||
|
const ref = baseRef ?? getGitHead(mainDir);
|
||||||
|
if (!ref) return null;
|
||||||
|
|
||||||
// Ensure the parent directory exists so `git worktree add` can create
|
// Ensure the parent directory exists so `git worktree add` can create
|
||||||
// the worktree directory inside it.
|
// the worktree directory inside it.
|
||||||
ensureDir(path.dirname(wtDir));
|
ensureDir(path.dirname(wtDir));
|
||||||
|
|
||||||
// Remove a stale worktree directory if one exists (e.g. from a crashed
|
// Delete a stale branch if it exists from a previous run so `-b` doesn't
|
||||||
// previous run). `git worktree add` fails if the path already exists.
|
// fail on the new worktree.
|
||||||
// We prune first to clean up any metadata for removed-but-not-pruned dirs.
|
|
||||||
git("worktree prune", mainDir);
|
|
||||||
const existing = git(`worktree list --porcelain`, mainDir);
|
|
||||||
if (existing && existing.includes(`worktree ${wtDir}`)) {
|
|
||||||
// A worktree at this path is already registered — remove it.
|
|
||||||
git(`worktree remove --force "${wtDir}"`, mainDir);
|
|
||||||
}
|
|
||||||
// Also delete a stale branch if it exists from a previous run.
|
|
||||||
git(`branch -D "${branch}"`, mainDir);
|
git(`branch -D "${branch}"`, mainDir);
|
||||||
|
|
||||||
const result = gitRaw(
|
const result = gitRaw(
|
||||||
|
|||||||
Reference in New Issue
Block a user