fix: use pauseAuto instead of stopAuto for warning-level dispatch stops (#2666)
When the uat-verdict-gate returns a non-PASS verdict, it returns action: "stop" with level: "warning". This was routed to closeoutAndStop() → stopAuto(), which destroys the session — the user must cold-restart `/gsd auto` from scratch. A non-PASS UAT verdict is a recoverable human checkpoint, not an infrastructure failure. The fix routes warning-level stops to pauseAuto() instead, making the session resumable with `/gsd auto`. Error and info-level stops continue to use closeoutAndStop() for infrastructure failures and terminal conditions respectively. Closes #2474
This commit is contained in:
parent
43b1de6d59
commit
c9da003151
2 changed files with 79 additions and 1 deletions
|
|
@ -504,7 +504,17 @@ export async function runDispatch(
|
|||
|
||||
if (dispatchResult.action === "stop") {
|
||||
deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "dispatch-stop", rule: dispatchResult.matchedRule, data: { reason: dispatchResult.reason } });
|
||||
await closeoutAndStop(ctx, pi, s, deps, dispatchResult.reason);
|
||||
// Warning-level stops are recoverable human checkpoints (e.g. UAT verdict
|
||||
// gate) — pause instead of hard-stopping so the session is resumable with
|
||||
// `/gsd auto`. Error/info-level stops remain hard stops for infrastructure
|
||||
// failures and terminal conditions respectively.
|
||||
// See: https://github.com/gsd-build/gsd-2/issues/2474
|
||||
if (dispatchResult.level === "warning") {
|
||||
ctx.ui.notify(dispatchResult.reason, "warning");
|
||||
await deps.pauseAuto(ctx, pi);
|
||||
} else {
|
||||
await closeoutAndStop(ctx, pi, s, deps, dispatchResult.reason);
|
||||
}
|
||||
debugLog("autoLoop", { phase: "exit", reason: "dispatch-stop" });
|
||||
return { action: "break", reason: "dispatch-stop" };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -882,6 +882,74 @@ test("autoLoop handles dispatch stop action", async (t) => {
|
|||
);
|
||||
});
|
||||
|
||||
// #2474: warning-level dispatch stop should pause (resumable), not hard-stop
|
||||
test("autoLoop pauses instead of stopping for warning-level dispatch stop", async (t) => {
|
||||
_resetPendingResolve();
|
||||
|
||||
const ctx = makeMockCtx();
|
||||
ctx.ui.setStatus = () => {};
|
||||
const pi = makeMockPi();
|
||||
const s = makeLoopSession();
|
||||
|
||||
const deps = makeMockDeps({
|
||||
resolveDispatch: async () => {
|
||||
deps.callLog.push("resolveDispatch");
|
||||
return {
|
||||
action: "stop" as const,
|
||||
reason: 'UAT verdict for S01 is "partial" — blocking progression.',
|
||||
level: "warning" as const,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
await autoLoop(ctx, pi, s, deps);
|
||||
|
||||
assert.ok(
|
||||
deps.callLog.includes("resolveDispatch"),
|
||||
"should have called resolveDispatch",
|
||||
);
|
||||
assert.ok(
|
||||
deps.callLog.includes("pauseAuto"),
|
||||
"warning-level stop should call pauseAuto (resumable)",
|
||||
);
|
||||
assert.ok(
|
||||
!deps.callLog.includes("stopAuto"),
|
||||
"warning-level stop should NOT call stopAuto (hard stop)",
|
||||
);
|
||||
});
|
||||
|
||||
// #2474: error-level dispatch stop should still hard-stop
|
||||
test("autoLoop hard-stops for error-level dispatch stop", async (t) => {
|
||||
_resetPendingResolve();
|
||||
|
||||
const ctx = makeMockCtx();
|
||||
ctx.ui.setStatus = () => {};
|
||||
const pi = makeMockPi();
|
||||
const s = makeLoopSession();
|
||||
|
||||
const deps = makeMockDeps({
|
||||
resolveDispatch: async () => {
|
||||
deps.callLog.push("resolveDispatch");
|
||||
return {
|
||||
action: "stop" as const,
|
||||
reason: "Cannot complete milestone: missing SUMMARY files.",
|
||||
level: "error" as const,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
await autoLoop(ctx, pi, s, deps);
|
||||
|
||||
assert.ok(
|
||||
deps.callLog.includes("stopAuto"),
|
||||
"error-level stop should call stopAuto (hard stop)",
|
||||
);
|
||||
assert.ok(
|
||||
!deps.callLog.includes("pauseAuto"),
|
||||
"error-level stop should NOT call pauseAuto",
|
||||
);
|
||||
});
|
||||
|
||||
test("autoLoop handles dispatch skip action by continuing", async (t) => {
|
||||
_resetPendingResolve();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue