diff --git a/src/resources/extensions/gsd/dispatch-guard.ts b/src/resources/extensions/gsd/dispatch-guard.ts index 59df30db1..c687f1b30 100644 --- a/src/resources/extensions/gsd/dispatch-guard.ts +++ b/src/resources/extensions/gsd/dispatch-guard.ts @@ -5,6 +5,7 @@ import { findMilestoneIds } from "./guided-flow.js"; import { parseUnitId } from "./unit-id.js"; import { isDbAvailable, getMilestoneSlices } from "./gsd-db.js"; import { parseRoadmap } from "./parsers-legacy.js"; +import { isClosedStatus } from "./status-guards.js"; import { readFileSync } from "node:fs"; const SLICE_DISPATCH_TYPES = new Set([ @@ -57,7 +58,7 @@ export function getPriorSliceCompletionBlocker( if (rows.length > 0) { slices = rows.map((r) => ({ id: r.id, - done: r.status === "complete", + done: isClosedStatus(r.status), depends: r.depends ?? [], })); } diff --git a/src/resources/extensions/gsd/tests/dispatch-guard-closed-status.test.ts b/src/resources/extensions/gsd/tests/dispatch-guard-closed-status.test.ts new file mode 100644 index 000000000..6be6a5a5f --- /dev/null +++ b/src/resources/extensions/gsd/tests/dispatch-guard-closed-status.test.ts @@ -0,0 +1,33 @@ +/** + * dispatch-guard-closed-status.test.ts — #3653 + * + * Verify that the dispatch guard uses isClosedStatus() instead of a raw + * `status === "complete"` check when determining whether a slice is done. + * Reconciled slices may carry statuses like "skipped" or "cancelled" which + * are also closed — the raw check caused false dispatch blocks. + */ + +import { describe, test } from "node:test"; +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { join, dirname } from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const sourceFile = join(__dirname, "..", "dispatch-guard.ts"); + +describe("dispatch-guard isClosedStatus migration (#3653)", () => { + const source = readFileSync(sourceFile, "utf-8"); + + test("imports isClosedStatus from status-guards", () => { + assert.match(source, /import\s*\{[^}]*isClosedStatus[^}]*\}\s*from\s*["']\.\/status-guards/); + }); + + test("uses isClosedStatus() for slice done check instead of raw comparison", () => { + assert.match(source, /done:\s*isClosedStatus\(r\.status\)/); + }); + + test("does not use raw status === 'complete' for DB slice rows", () => { + assert.doesNotMatch(source, /done:\s*r\.status\s*===\s*["']complete["']/); + }); +});