fix(auto): avoid resuming blocked no-unit sessions
This commit is contained in:
parent
03f6d4990f
commit
ecf6af92e8
3 changed files with 58 additions and 25 deletions
|
|
@ -1208,30 +1208,34 @@ export async function pauseAuto(ctx, _pi, _errorContext) {
|
|||
// Persist paused-session metadata so resume survives /exit (#1383).
|
||||
// The fresh-start bootstrap checks for this file and restores worktree context.
|
||||
try {
|
||||
const pausedMeta = {
|
||||
milestoneId: s.currentMilestoneId,
|
||||
worktreePath: isInAutoWorktree(s.basePath) ? s.basePath : null,
|
||||
originalBasePath: s.originalBasePath,
|
||||
stepMode: s.stepMode,
|
||||
pausedAt: new Date().toISOString(),
|
||||
sessionFile: s.pausedSessionFile,
|
||||
unitType: s.currentUnit?.type ?? undefined,
|
||||
unitId: s.currentUnit?.id ?? undefined,
|
||||
activeEngineId: s.activeEngineId,
|
||||
activeRunDir: s.activeRunDir,
|
||||
autoStartTime: s.autoStartTime,
|
||||
milestoneLock: s.sessionMilestoneLock ?? undefined,
|
||||
};
|
||||
const runtimeDir = join(
|
||||
sfRoot(s.originalBasePath || s.basePath),
|
||||
"runtime",
|
||||
);
|
||||
mkdirSync(runtimeDir, { recursive: true });
|
||||
writeFileSync(
|
||||
join(runtimeDir, "paused-session.json"),
|
||||
JSON.stringify(pausedMeta, null, 2),
|
||||
"utf-8",
|
||||
);
|
||||
const hasResumableUnit = !!(s.currentUnit?.type && s.currentUnit?.id);
|
||||
const hasResumableCustomEngine = !!s.activeEngineId && s.activeEngineId !== "dev";
|
||||
if (hasResumableUnit || hasResumableCustomEngine) {
|
||||
const pausedMeta = {
|
||||
milestoneId: s.currentMilestoneId,
|
||||
worktreePath: isInAutoWorktree(s.basePath) ? s.basePath : null,
|
||||
originalBasePath: s.originalBasePath,
|
||||
stepMode: s.stepMode,
|
||||
pausedAt: new Date().toISOString(),
|
||||
sessionFile: s.pausedSessionFile,
|
||||
unitType: s.currentUnit?.type ?? undefined,
|
||||
unitId: s.currentUnit?.id ?? undefined,
|
||||
activeEngineId: s.activeEngineId,
|
||||
activeRunDir: s.activeRunDir,
|
||||
autoStartTime: s.autoStartTime,
|
||||
milestoneLock: s.sessionMilestoneLock ?? undefined,
|
||||
};
|
||||
const runtimeDir = join(
|
||||
sfRoot(s.originalBasePath || s.basePath),
|
||||
"runtime",
|
||||
);
|
||||
mkdirSync(runtimeDir, { recursive: true });
|
||||
writeFileSync(
|
||||
join(runtimeDir, "paused-session.json"),
|
||||
JSON.stringify(pausedMeta, null, 2),
|
||||
"utf-8",
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
// Non-fatal — resume will still work via full bootstrap, just without worktree context
|
||||
logWarning(
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@ export function isBootstrapCrashLock(lock) {
|
|||
);
|
||||
}
|
||||
export function hasResumableDerivedState(state) {
|
||||
return !!(state?.activeMilestone && state.phase !== "complete");
|
||||
if (!state?.activeMilestone) return false;
|
||||
if (state.phase === "complete" || state.phase === "blocked") return false;
|
||||
return true;
|
||||
}
|
||||
export async function assessInterruptedSession(basePath) {
|
||||
const pausedSession = readPausedSessionMetadata(basePath);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
import assert from "node:assert/strict";
|
||||
import { test } from "vitest";
|
||||
import { hasResumableDerivedState } from "../interrupted-session.js";
|
||||
|
||||
test("hasResumableDerivedState_rejects_blocked_state_without_active_work", () => {
|
||||
assert.equal(
|
||||
hasResumableDerivedState({
|
||||
activeMilestone: { id: "M005", title: "Blocked milestone" },
|
||||
activeSlice: null,
|
||||
activeTask: null,
|
||||
phase: "blocked",
|
||||
}),
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
test("hasResumableDerivedState_accepts_active_non_blocked_state", () => {
|
||||
assert.equal(
|
||||
hasResumableDerivedState({
|
||||
activeMilestone: { id: "M005", title: "Active milestone" },
|
||||
activeSlice: { id: "S01", title: "Active slice" },
|
||||
activeTask: null,
|
||||
phase: "planning",
|
||||
}),
|
||||
true,
|
||||
);
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue