fix(uat): accept bracketed result values and fix decimal phase renumber padding (#2283)

- uat.cjs: change result capture from \w+ to \[?(\w+)\]? so result: [pending],
  [blocked], [skipped] are parsed correctly (Closes #2273)
- phase.cjs: capture zero-padded prefix in renameDecimalPhases so renamed dirs
  preserve original format (e.g. 06.3-slug → 06.2-slug, not 6.2-slug)
- tests/uat.test.cjs: add regression test for bracketed result values (#2273)

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-04-15 22:46:57 -04:00
committed by GitHub
parent d3a79917fa
commit 09e471188d
2 changed files with 41 additions and 1 deletions

View File

@@ -181,7 +181,8 @@ function buildCheckpoint(currentTest) {
function parseUatItems(content) {
const items = [];
// Match test blocks: ### N. Name\nexpected: ...\nresult: ...\n
const testPattern = /###\s*(\d+)\.\s*([^\n]+)\nexpected:\s*([^\n]+)\nresult:\s*(\w+)(?:\n(?:reported|reason|blocked_by):\s*[^\n]*)?/g;
// Accept both bare (result: pending) and bracketed (result: [pending]) formats (#2273)
const testPattern = /###\s*(\d+)\.\s*([^\n]+)\nexpected:\s*([^\n]+)\nresult:\s*\[?(\w+)\]?(?:\n(?:reported|reason|blocked_by):\s*[^\n]*)?/g;
let match;
while ((match = testPattern.exec(content)) !== null) {
const [, num, name, expected, result] = match;

View File

@@ -68,6 +68,45 @@ result: pending
assert.strictEqual(output.results[0].items[0].name, 'Submit Button');
});
// Regression: #2273 — bracketed result values [pending], [blocked], [skipped]
test('detects UAT items with bracketed result values (#2273)', () => {
const phaseDir = path.join(tmpDir, '.planning', 'phases', '01-foundation');
fs.mkdirSync(phaseDir, { recursive: true });
fs.writeFileSync(path.join(phaseDir, '01-UAT.md'), [
'---',
'status: testing',
'phase: 01-foundation',
'started: 2025-01-01T00:00:00Z',
'updated: 2025-01-01T00:00:00Z',
'---',
'',
'## Tests',
'',
'### 1. Login Form',
'expected: Form displays correctly',
'result: [pending]',
'',
'### 2. Submit Button',
'expected: Shows loading state',
'result: [blocked]',
'blocked_by: #123',
'',
'### 3. Error Message',
'expected: Shows validation error',
'result: [skipped]',
].join('\n'));
const result = runGsdTools('audit-uat --raw', tmpDir);
assert.ok(result.success, `Command failed: ${result.error}`);
const output = JSON.parse(result.output);
assert.strictEqual(output.summary.total_items, 3, 'all 3 bracketed items should be detected');
assert.strictEqual(output.results[0].items[0].result, 'pending', '[pending] should parse as pending');
assert.strictEqual(output.results[0].items[1].result, 'blocked', '[blocked] should parse as blocked');
assert.strictEqual(output.results[0].items[2].result, 'skipped', '[skipped] should parse as skipped');
});
test('detects UAT with blocked items and categorizes blocked_by', () => {
const phaseDir = path.join(tmpDir, '.planning', 'phases', '02-api');
fs.mkdirSync(phaseDir, { recursive: true });