feat(sdk): empty phase dir surfaces NO_UAT_FILES reason (#3184)
Cycle 4 of ~15: phase dir present but no *-HUMAN-UAT.md files now returns a typed NO_UAT_FILES reason instead of an empty reasons array (which prior cycles used as a 'shouldn't happen' fallback). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -43,6 +43,24 @@ describe('isPhaseUatPassed', () => {
|
|||||||
expect(result.reasons.length).toBe(0);
|
expect(result.reasons.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns passed=false with NO_UAT_FILES reason when phase dir has no UAT files', async () => {
|
||||||
|
const localTmp = await mkdtemp(join(tmpdir(), 'gsd-uat-c4-'));
|
||||||
|
try {
|
||||||
|
const phaseDir = join(localTmp, '.planning', 'phases', '05-empty');
|
||||||
|
await mkdir(phaseDir, { recursive: true });
|
||||||
|
// Write a non-UAT file to ensure the dir exists but has no *-HUMAN-UAT.md
|
||||||
|
await writeFile(join(phaseDir, '05-PLAN.md'), '# Plan\nNothing here.\n');
|
||||||
|
|
||||||
|
const result = await isPhaseUatPassed(localTmp, '5');
|
||||||
|
expect(result.passed).toBe(false);
|
||||||
|
expect(result.items.length).toBe(0);
|
||||||
|
expect(result.reasons.length).toBe(1);
|
||||||
|
expect(result.reasons[0].code).toBe(REASON_CODE.NO_UAT_FILES);
|
||||||
|
} finally {
|
||||||
|
await rm(localTmp, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('returns passed=false with NO_PHASE_DIR reason when phase has no directory', async () => {
|
it('returns passed=false with NO_PHASE_DIR reason when phase has no directory', async () => {
|
||||||
const localTmp = await mkdtemp(join(tmpdir(), 'gsd-uat-c3-'));
|
const localTmp = await mkdtemp(join(tmpdir(), 'gsd-uat-c3-'));
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { resolvePhaseDir } from './phase-list-queries.js';
|
|||||||
export const REASON_CODE = Object.freeze({
|
export const REASON_CODE = Object.freeze({
|
||||||
NON_PASS_RESULT: 'non_pass_result',
|
NON_PASS_RESULT: 'non_pass_result',
|
||||||
NO_PHASE_DIR: 'no_phase_dir',
|
NO_PHASE_DIR: 'no_phase_dir',
|
||||||
|
NO_UAT_FILES: 'no_uat_files',
|
||||||
} as const);
|
} as const);
|
||||||
|
|
||||||
export type ReasonCode = typeof REASON_CODE[keyof typeof REASON_CODE];
|
export type ReasonCode = typeof REASON_CODE[keyof typeof REASON_CODE];
|
||||||
@@ -74,6 +75,15 @@ export async function isPhaseUatPassed(
|
|||||||
const files = await readdir(dir);
|
const files = await readdir(dir);
|
||||||
const uatFiles = files.filter((f) => f.endsWith('-HUMAN-UAT.md'));
|
const uatFiles = files.filter((f) => f.endsWith('-HUMAN-UAT.md'));
|
||||||
|
|
||||||
|
if (uatFiles.length === 0) {
|
||||||
|
return {
|
||||||
|
passed: false,
|
||||||
|
reasons: [{ code: REASON_CODE.NO_UAT_FILES }],
|
||||||
|
reasonsHuman: [],
|
||||||
|
items: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const items: UatItem[] = [];
|
const items: UatItem[] = [];
|
||||||
const reasons: UatReason[] = [];
|
const reasons: UatReason[] = [];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user