fix: widen completing-milestone gate to accept "None required" and similar phrasings (#2931) (#3239)
The verification_operational gate used exact equality against "none", causing a permanent dispatch-stop loop when the planning agent wrote variants like "None required", "N/A", or "Not applicable". Extract an isVerificationNotApplicable() helper with a regex that covers common not-applicable phrasings and use it in the gate condition. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
aa6cde32d9
commit
3d0eb32756
2 changed files with 98 additions and 1 deletions
|
|
@ -129,6 +129,21 @@ export function setRewriteCount(basePath: string, count: number): void {
|
|||
writeFileSync(filePath, JSON.stringify({ count, updatedAt: new Date().toISOString() }) + "\n");
|
||||
}
|
||||
|
||||
// ─── Helpers ─────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Returns true when the verification_operational value indicates that no
|
||||
* operational verification is needed. Covers common phrasings the planning
|
||||
* agent may use: "None", "None required", "N/A", "Not applicable", etc.
|
||||
*
|
||||
* @see https://github.com/gsd-build/gsd-2/issues/2931
|
||||
*/
|
||||
export function isVerificationNotApplicable(value: string): boolean {
|
||||
const v = (value ?? "").toLowerCase().trim();
|
||||
if (!v || v === "none") return true;
|
||||
return /^(?:none[\s._-]*(?:required|needed|planned)?|n\/?a|not[\s._-]+(?:applicable|required|needed)|no[\s._-]+operational[\s\S]*)$/i.test(v);
|
||||
}
|
||||
|
||||
// ─── Rules ────────────────────────────────────────────────────────────────
|
||||
|
||||
export const DISPATCH_RULES: DispatchRule[] = [
|
||||
|
|
@ -672,7 +687,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
|
|||
if (isDbAvailable()) {
|
||||
const milestone = getMilestone(mid);
|
||||
if (milestone?.verification_operational &&
|
||||
milestone.verification_operational.toLowerCase() !== "none") {
|
||||
!isVerificationNotApplicable(milestone.verification_operational)) {
|
||||
const validationPath = resolveMilestoneFile(basePath, mid, "VALIDATION");
|
||||
if (validationPath) {
|
||||
const validationContent = await loadFile(validationPath);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
/**
|
||||
* Regression test for #2931: completing-milestone gate should treat
|
||||
* "None required", "N/A", "Not applicable", etc. as equivalent to "none"
|
||||
* and skip the operational verification content check entirely.
|
||||
*/
|
||||
import { test } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { isVerificationNotApplicable } from "../auto-dispatch.ts";
|
||||
|
||||
test("isVerificationNotApplicable: bare 'none' is not applicable", () => {
|
||||
assert.equal(isVerificationNotApplicable("none"), true);
|
||||
});
|
||||
|
||||
test("isVerificationNotApplicable: 'None' (capitalized) is not applicable", () => {
|
||||
assert.equal(isVerificationNotApplicable("None"), true);
|
||||
});
|
||||
|
||||
test("isVerificationNotApplicable: 'NONE' (uppercase) is not applicable", () => {
|
||||
assert.equal(isVerificationNotApplicable("NONE"), true);
|
||||
});
|
||||
|
||||
test("isVerificationNotApplicable: 'None required' is not applicable (#2931)", () => {
|
||||
assert.equal(isVerificationNotApplicable("None required"), true);
|
||||
});
|
||||
|
||||
test("isVerificationNotApplicable: 'None needed' is not applicable", () => {
|
||||
assert.equal(isVerificationNotApplicable("None needed"), true);
|
||||
});
|
||||
|
||||
test("isVerificationNotApplicable: 'None planned' is not applicable", () => {
|
||||
assert.equal(isVerificationNotApplicable("None planned"), true);
|
||||
});
|
||||
|
||||
test("isVerificationNotApplicable: 'N/A' is not applicable", () => {
|
||||
assert.equal(isVerificationNotApplicable("N/A"), true);
|
||||
});
|
||||
|
||||
test("isVerificationNotApplicable: 'n/a' is not applicable", () => {
|
||||
assert.equal(isVerificationNotApplicable("n/a"), true);
|
||||
});
|
||||
|
||||
test("isVerificationNotApplicable: 'Not applicable' is not applicable", () => {
|
||||
assert.equal(isVerificationNotApplicable("Not applicable"), true);
|
||||
});
|
||||
|
||||
test("isVerificationNotApplicable: 'Not required' is not applicable", () => {
|
||||
assert.equal(isVerificationNotApplicable("Not required"), true);
|
||||
});
|
||||
|
||||
test("isVerificationNotApplicable: 'Not needed' is not applicable", () => {
|
||||
assert.equal(isVerificationNotApplicable("Not needed"), true);
|
||||
});
|
||||
|
||||
test("isVerificationNotApplicable: 'No operational verification needed' is not applicable", () => {
|
||||
assert.equal(isVerificationNotApplicable("No operational verification needed"), true);
|
||||
});
|
||||
|
||||
test("isVerificationNotApplicable: 'No operational' is not applicable", () => {
|
||||
assert.equal(isVerificationNotApplicable("No operational"), true);
|
||||
});
|
||||
|
||||
test("isVerificationNotApplicable: empty string is not applicable", () => {
|
||||
assert.equal(isVerificationNotApplicable(""), true);
|
||||
});
|
||||
|
||||
test("isVerificationNotApplicable: whitespace-only is not applicable", () => {
|
||||
assert.equal(isVerificationNotApplicable(" "), true);
|
||||
});
|
||||
|
||||
// Positive cases: these SHOULD require verification
|
||||
test("isVerificationNotApplicable: 'Run load tests' requires verification", () => {
|
||||
assert.equal(isVerificationNotApplicable("Run load tests"), false);
|
||||
});
|
||||
|
||||
test("isVerificationNotApplicable: 'Verify API response times under load' requires verification", () => {
|
||||
assert.equal(isVerificationNotApplicable("Verify API response times under load"), false);
|
||||
});
|
||||
|
||||
test("isVerificationNotApplicable: 'Monitor error rates for 24h' requires verification", () => {
|
||||
assert.equal(isVerificationNotApplicable("Monitor error rates for 24h"), false);
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue