Merge pull request #2487 from jeremymcs/fix/isolation-mode-downgrade
fix(gsd): downgrade isolation mode when worktree creation fails
This commit is contained in:
commit
a53d021864
3 changed files with 103 additions and 0 deletions
|
|
@ -118,6 +118,10 @@ export class AutoSession {
|
|||
// ── Sidecar queue ─────────────────────────────────────────────────────
|
||||
sidecarQueue: SidecarItem[] = [];
|
||||
|
||||
// ── Isolation degradation ────────────────────────────────────────────
|
||||
/** Set to true when worktree creation fails; prevents merge of nonexistent branch. */
|
||||
isolationDegraded = false;
|
||||
|
||||
// ── Dispatch circuit breakers ──────────────────────────────────────
|
||||
rewriteAttemptCount = 0;
|
||||
|
||||
|
|
@ -200,6 +204,7 @@ export class AutoSession {
|
|||
this.pendingQuickTasks = [];
|
||||
this.sidecarQueue = [];
|
||||
this.rewriteAttemptCount = 0;
|
||||
this.isolationDegraded = false;
|
||||
|
||||
// Signal handler
|
||||
this.sigtermHandler = null;
|
||||
|
|
|
|||
|
|
@ -846,3 +846,70 @@ test("GitService is rebuilt with originalBasePath after exitMilestone", () => {
|
|||
|
||||
assert.equal(gitServiceBasePath, "/project"); // project root, not worktree
|
||||
});
|
||||
|
||||
// ─── Isolation Degradation Tests (#2483) ──────────────────────────────────
|
||||
|
||||
test("enterMilestone sets isolationDegraded when worktree creation throws (#2483)", () => {
|
||||
const s = makeSession();
|
||||
const deps = makeDeps({
|
||||
getAutoWorktreePath: () => null,
|
||||
createAutoWorktree: () => {
|
||||
throw new Error("empty repo");
|
||||
},
|
||||
});
|
||||
const ctx = makeNotifyCtx();
|
||||
const resolver = new WorktreeResolver(s, deps);
|
||||
|
||||
resolver.enterMilestone("M001", ctx);
|
||||
|
||||
assert.equal(s.isolationDegraded, true);
|
||||
assert.equal(s.basePath, "/project"); // unchanged — error recovery
|
||||
});
|
||||
|
||||
test("enterMilestone is no-op when isolationDegraded is true (#2483)", () => {
|
||||
const s = makeSession();
|
||||
s.isolationDegraded = true;
|
||||
const deps = makeDeps();
|
||||
const ctx = makeNotifyCtx();
|
||||
const resolver = new WorktreeResolver(s, deps);
|
||||
|
||||
resolver.enterMilestone("M001", ctx);
|
||||
|
||||
assert.equal(s.basePath, "/project"); // unchanged
|
||||
assert.equal(findCalls(deps.calls, "createAutoWorktree").length, 0);
|
||||
assert.equal(findCalls(deps.calls, "enterAutoWorktree").length, 0);
|
||||
assert.equal(findCalls(deps.calls, "shouldUseWorktreeIsolation").length, 0);
|
||||
});
|
||||
|
||||
test("mergeAndExit is no-op when isolationDegraded is true (#2483)", () => {
|
||||
const s = makeSession({
|
||||
basePath: "/project",
|
||||
originalBasePath: "/project",
|
||||
});
|
||||
s.isolationDegraded = true;
|
||||
const deps = makeDeps({
|
||||
getIsolationMode: () => "worktree",
|
||||
});
|
||||
const ctx = makeNotifyCtx();
|
||||
const resolver = new WorktreeResolver(s, deps);
|
||||
|
||||
resolver.mergeAndExit("M001", ctx);
|
||||
|
||||
assert.equal(findCalls(deps.calls, "mergeMilestoneToMain").length, 0);
|
||||
assert.equal(findCalls(deps.calls, "teardownAutoWorktree").length, 0);
|
||||
assert.equal(findCalls(deps.calls, "getIsolationMode").length, 0);
|
||||
assert.ok(
|
||||
ctx.messages.some(
|
||||
(m) => m.level === "info" && m.msg.includes("isolation was degraded"),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
test("isolationDegraded is reset by session.reset() (#2483)", () => {
|
||||
const s = new AutoSession();
|
||||
s.isolationDegraded = true;
|
||||
|
||||
s.reset();
|
||||
|
||||
assert.equal(s.isolationDegraded, false);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -150,6 +150,18 @@ export class WorktreeResolver {
|
|||
*/
|
||||
enterMilestone(milestoneId: string, ctx: NotifyCtx): void {
|
||||
this.validateMilestoneId(milestoneId);
|
||||
|
||||
// If worktree creation failed earlier this session, skip all future attempts
|
||||
if (this.s.isolationDegraded) {
|
||||
debugLog("WorktreeResolver", {
|
||||
action: "enterMilestone",
|
||||
milestoneId,
|
||||
skipped: true,
|
||||
reason: "isolation-degraded",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.deps.shouldUseWorktreeIsolation()) {
|
||||
debugLog("WorktreeResolver", {
|
||||
action: "enterMilestone",
|
||||
|
|
@ -220,6 +232,9 @@ export class WorktreeResolver {
|
|||
`Auto-worktree creation for ${milestoneId} failed: ${msg}. Continuing in project root.`,
|
||||
"warning",
|
||||
);
|
||||
// Degrade isolation for the rest of this session so mergeAndExit
|
||||
// doesn't try to merge a nonexistent worktree branch (#2483)
|
||||
this.s.isolationDegraded = true;
|
||||
// Do NOT update s.basePath — stay in project root
|
||||
}
|
||||
}
|
||||
|
|
@ -304,6 +319,22 @@ export class WorktreeResolver {
|
|||
*/
|
||||
mergeAndExit(milestoneId: string, ctx: NotifyCtx): void {
|
||||
this.validateMilestoneId(milestoneId);
|
||||
|
||||
// If worktree creation failed earlier, skip merge — work is on current branch (#2483)
|
||||
if (this.s.isolationDegraded) {
|
||||
debugLog("WorktreeResolver", {
|
||||
action: "mergeAndExit",
|
||||
milestoneId,
|
||||
skipped: true,
|
||||
reason: "isolation-degraded",
|
||||
});
|
||||
ctx.notify(
|
||||
`Skipping worktree merge for ${milestoneId} — isolation was degraded (worktree creation failed earlier). Work is on the current branch.`,
|
||||
"info",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const mode = this.deps.getIsolationMode();
|
||||
debugLog("WorktreeResolver", {
|
||||
action: "mergeAndExit",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue