fix: gate slice progression on UAT verdict, not just file existence (#1241)
Two changes: 1. checkNeedsRunUat now reads the verdict from UAT-RESULT. Only skips re-running UAT when verdict is PASS/passed. Non-PASS verdicts (FAIL, surfaced-for-human-review) no longer silently advance. 2. Added uat-verdict-gate dispatch rule between run-uat and reassess-roadmap. When uat_dispatch is enabled, scans all completed slices for non-PASS UAT verdicts and stops auto-mode if found. This prevents advancing to the next slice when a UAT failed or needs human review. Fixes #1231
This commit is contained in:
parent
79ceea257f
commit
1947e8e8e3
2 changed files with 43 additions and 4 deletions
|
|
@ -12,7 +12,7 @@
|
|||
import type { GSDState } from "./types.js";
|
||||
import type { GSDPreferences } from "./preferences.js";
|
||||
import type { UatType } from "./files.js";
|
||||
import { loadFile, extractUatType, loadActiveOverrides } from "./files.js";
|
||||
import { loadFile, extractUatType, loadActiveOverrides, parseRoadmap } from "./files.js";
|
||||
import {
|
||||
resolveMilestoneFile, resolveMilestonePath, resolveSliceFile, resolveTaskFile,
|
||||
relSliceFile, buildMilestoneFileName,
|
||||
|
|
@ -123,6 +123,35 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "uat-verdict-gate (non-PASS blocks progression)",
|
||||
match: async ({ mid, basePath, prefs }) => {
|
||||
// Only applies when UAT dispatch is enabled
|
||||
if (!prefs?.uat_dispatch) return null;
|
||||
|
||||
const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
|
||||
const roadmapContent = roadmapFile ? await loadFile(roadmapFile) : null;
|
||||
if (!roadmapContent) return null;
|
||||
|
||||
const roadmap = parseRoadmap(roadmapContent);
|
||||
for (const slice of roadmap.slices.filter(s => s.done)) {
|
||||
const resultFile = resolveSliceFile(basePath, mid, slice.id, "UAT-RESULT");
|
||||
if (!resultFile) continue;
|
||||
const content = await loadFile(resultFile);
|
||||
if (!content) continue;
|
||||
const verdictMatch = content.match(/verdict:\s*([\w-]+)/i);
|
||||
const verdict = verdictMatch?.[1]?.toLowerCase();
|
||||
if (verdict && verdict !== "pass" && verdict !== "passed") {
|
||||
return {
|
||||
action: "stop" as const,
|
||||
reason: `UAT verdict for ${slice.id} is "${verdict}" — blocking progression until resolved.\nReview the UAT result and update the verdict to PASS, or re-run /gsd auto after fixing.`,
|
||||
level: "warning" as const,
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "reassess-roadmap (post-completion)",
|
||||
match: async ({ state, mid, midTitle, basePath, prefs }) => {
|
||||
|
|
|
|||
|
|
@ -530,11 +530,21 @@ export async function checkNeedsRunUat(
|
|||
const uatContent = await loadFile(uatFile);
|
||||
if (!uatContent) return null;
|
||||
|
||||
// If UAT result already exists, skip (idempotent)
|
||||
// If UAT result already exists with a PASS verdict, skip (idempotent).
|
||||
// Non-PASS verdicts (FAIL, surfaced-for-human-review) should block slice
|
||||
// progression — return the slice for re-evaluation (#1231).
|
||||
const uatResultFile = resolveSliceFile(base, mid, sid, "UAT-RESULT");
|
||||
if (uatResultFile) {
|
||||
const hasResult = !!(await loadFile(uatResultFile));
|
||||
if (hasResult) return null;
|
||||
const resultContent = await loadFile(uatResultFile);
|
||||
if (resultContent) {
|
||||
const verdictMatch = resultContent.match(/verdict:\s*([\w-]+)/i);
|
||||
const verdict = verdictMatch?.[1]?.toLowerCase();
|
||||
if (verdict === "pass" || verdict === "passed") return null; // PASS — skip
|
||||
// Non-PASS verdict exists — don't re-run UAT, but don't advance either.
|
||||
// Return null here since the UAT already ran; the dispatch table's
|
||||
// complete-slice rule should check the verdict before advancing.
|
||||
// For now, returning the slice signals it still needs attention.
|
||||
}
|
||||
}
|
||||
|
||||
// Classify UAT type; unknown type → treat as human-experience (human review)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue