Three defects in the completing-milestone dispatch guard caused false
positive blocks on valid validation output:
1. Single-line constraint: [^\n]* stopped at newlines, missing verdicts
on subsequent lines. Fixed with [\s\S]{0,500}? (bounded lazy match).
2. Missing keywords: 'satisfied' and 'partially' were absent from the
alternation. LLMs commonly write 'PARTIALLY SATISFIED' or 'FULLY
SATISFIED'. Added both.
3. Markdown bold delimiters: **Operational** blocked [\s:] after the
word. The new [\s\S] class handles any character including *.
Also adds SATISFIED to the structuredMatch includes check, and ✅ to
the prose regex (overlaps with #2862).
Includes 8 regression test cases covering multi-line formats, satisfied
keyword variants, markdown bold tables, and checkmark emoji.
This commit is contained in:
parent
558b1f2081
commit
79da90edde
2 changed files with 47 additions and 5 deletions
|
|
@ -677,13 +677,13 @@ export const DISPATCH_RULES: DispatchRule[] = [
|
|||
if (validationPath) {
|
||||
const validationContent = await loadFile(validationPath);
|
||||
if (validationContent) {
|
||||
// Accept either the structured template format (table with MET/N/A)
|
||||
// Accept either the structured template format (table with MET/N/A/SATISFIED)
|
||||
// or prose evidence patterns the validation agent may emit.
|
||||
const structuredMatch =
|
||||
validationContent.includes("Operational") &&
|
||||
(validationContent.includes("MET") || validationContent.includes("N/A"));
|
||||
(validationContent.includes("MET") || validationContent.includes("N/A") || validationContent.includes("SATISFIED"));
|
||||
const proseMatch =
|
||||
/[Oo]perational[\s:][^\n]*(?:pass|verified|confirmed|met|complete|true|yes|addressed|covered|n\/a|not\s+applicable)/i.test(validationContent);
|
||||
/[Oo]perational[\s\S]{0,500}?(?:✅|pass|verified|confirmed|met|complete|true|yes|addressed|covered|satisfied|partially|n\/a|not[\s-]+applicable)/i.test(validationContent);
|
||||
const hasOperationalCheck = structuredMatch || proseMatch;
|
||||
if (!hasOperationalCheck) {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ import assert from "node:assert/strict";
|
|||
function hasOperationalEvidence(validationContent: string): boolean {
|
||||
const structuredMatch =
|
||||
validationContent.includes("Operational") &&
|
||||
(validationContent.includes("MET") || validationContent.includes("N/A"));
|
||||
(validationContent.includes("MET") || validationContent.includes("N/A") || validationContent.includes("SATISFIED"));
|
||||
const proseMatch =
|
||||
/[Oo]perational[\s:][^\n]*(?:pass|verified|confirmed|met|complete|true|yes|addressed|covered|n\/a|not\s+applicable)/i.test(
|
||||
/[Oo]perational[\s\S]{0,500}?(?:✅|pass|verified|confirmed|met|complete|true|yes|addressed|covered|satisfied|partially|n\/a|not[\s-]+applicable)/i.test(
|
||||
validationContent,
|
||||
);
|
||||
return structuredMatch || proseMatch;
|
||||
|
|
@ -104,6 +104,48 @@ test('prose: "Operational: complete" passes', () => {
|
|||
assert.ok(hasOperationalEvidence(content));
|
||||
});
|
||||
|
||||
// ─── Issue #2862: checkmark emoji ────────────────────────────────────────────
|
||||
|
||||
test('prose: "Operational: ✅" checkmark emoji passes (issue #2862)', () => {
|
||||
const content = `- **Operational:** ✅ DECISIONS.md documents D009-D013`;
|
||||
assert.ok(hasOperationalEvidence(content));
|
||||
});
|
||||
|
||||
// ─── Issue #2866: multi-line, "satisfied", markdown bold ─────────────────────
|
||||
|
||||
test('multi-line: verdict on next line after Operational heading passes (issue #2866)', () => {
|
||||
const content = `### Operational Verification
|
||||
All endpoints responsive. Health checks pass.`;
|
||||
assert.ok(hasOperationalEvidence(content));
|
||||
});
|
||||
|
||||
test('prose: "PARTIALLY SATISFIED" passes (issue #2866)', () => {
|
||||
const content = `Operational class: ⚠️ PARTIALLY SATISFIED`;
|
||||
assert.ok(hasOperationalEvidence(content));
|
||||
});
|
||||
|
||||
test('prose: "FULLY SATISFIED" passes (issue #2866)', () => {
|
||||
const content = `**Operational**: FULLY SATISFIED — all monitoring in place.`;
|
||||
assert.ok(hasOperationalEvidence(content));
|
||||
});
|
||||
|
||||
test('structured: Operational + SATISFIED passes (issue #2866)', () => {
|
||||
const content = `| Criteria | Status |
|
||||
| Operational | SATISFIED |`;
|
||||
assert.ok(hasOperationalEvidence(content));
|
||||
});
|
||||
|
||||
test('table with markdown bold: **Operational** passes (issue #2866)', () => {
|
||||
const content = `| **Operational** | ⚠️ Partially satisfied — monitoring gap noted |`;
|
||||
assert.ok(hasOperationalEvidence(content));
|
||||
});
|
||||
|
||||
test('multi-line: Operational label and "confirmed" separated by line break passes (issue #2866)', () => {
|
||||
const content = `## Operational
|
||||
Smoke tests confirmed all services healthy after deploy.`;
|
||||
assert.ok(hasOperationalEvidence(content));
|
||||
});
|
||||
|
||||
// ─── Rejection cases ─────────────────────────────────────────────────────────
|
||||
|
||||
test("no operational evidence: unrelated content fails", () => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue