diff --git a/sdk/src/query/phase-uat-passed.test.ts b/sdk/src/query/phase-uat-passed.test.ts index ff33163fc..69f247ca2 100644 --- a/sdk/src/query/phase-uat-passed.test.ts +++ b/sdk/src/query/phase-uat-passed.test.ts @@ -272,4 +272,34 @@ expected: thing await rm(localTmp, { recursive: true, force: true }); } }); + + it("emits CASE_MISMATCH reason when result value is \"PASS\" (uppercase variant of pass)", async () => { + const localTmp = await mkdtemp(join(tmpdir(), 'gsd-uat-c10-')); + try { + const phaseDir = join(localTmp, '.planning', 'phases', '05-case-mismatch'); + await mkdir(phaseDir, { recursive: true }); + const content = `--- +status: complete +phase: 5 +source: roadmap +started: 2026-05-18T00:00:00Z +updated: 2026-05-18T00:00:00Z +--- + +### 1. Uppercase pass item +expected: thing happens +result: PASS +`; + await writeFile(join(phaseDir, '05-HUMAN-UAT.md'), content); + + const result = await isPhaseUatPassed(localTmp, '5'); + expect(result.passed).toBe(false); + expect(result.items.length).toBe(1); + expect(result.reasons.length).toBe(1); + expect(result.reasons[0].code).toBe(REASON_CODE.CASE_MISMATCH); + expect(result.reasons[0].capturedValue).toBe('PASS'); + } finally { + await rm(localTmp, { recursive: true, force: true }); + } + }); }); diff --git a/sdk/src/query/phase-uat-passed.ts b/sdk/src/query/phase-uat-passed.ts index 1dea2e76f..cfd1323f2 100644 --- a/sdk/src/query/phase-uat-passed.ts +++ b/sdk/src/query/phase-uat-passed.ts @@ -11,6 +11,7 @@ import { resolvePhaseDir } from './phase-list-queries.js'; export const REASON_CODE = Object.freeze({ NON_PASS_RESULT: 'non_pass_result', + CASE_MISMATCH: 'case_mismatch', NO_PHASE_DIR: 'no_phase_dir', NO_UAT_FILES: 'no_uat_files', } as const); @@ -114,8 +115,12 @@ export async function isPhaseUatPassed( for (const item of parsed) { items.push(item); if (item.result !== 'pass') { + const code = + item.result.toLowerCase() === 'pass' + ? REASON_CODE.CASE_MISMATCH + : REASON_CODE.NON_PASS_RESULT; reasons.push({ - code: REASON_CODE.NON_PASS_RESULT, + code, file: relFile, itemName: item.name, capturedValue: item.result,