fix: parseMustHavesBlock quoted strings + gsd state complete-phase (#2744)
* fix: parseMustHavesBlock quoted strings + gsd state complete-phase Bug #2734: parseMustHavesBlock dropped quoted truths containing ':' because fully-quoted strings like `"App-side UUIDv4: generated locally"` fell into the kv-parse branch, the regex failed (value starts with '"'), and current stayed as empty {}. Fix: detect fully-quoted strings before the ':' check and extract them directly. Two regression tests added to frontmatter.test.cjs. Bug #2735: `gsd state complete-phase` subcommand was missing — unknown subcommands fell through to cmdStateLoad. Added cmdStateCompletePhase to state.cjs (updates Status, Last Activity, and Current Position to COMPLETE), exported it, and wired it into the case 'state': dispatch in gsd-tools.cjs. Closes #2734 Closes #2735 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(state): unknown subcommand returns explicit error instead of silent fallthrough Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -66,6 +66,13 @@ describe('dispatcher error paths', () => {
|
||||
assert.ok(result.error.includes('Invalid --cwd'), `Expected "Invalid --cwd" in stderr, got: ${result.error}`);
|
||||
});
|
||||
|
||||
// Unknown subcommand: state
|
||||
test('state unknown subcommand errors', () => {
|
||||
const result = runGsdTools('state bogus', tmpDir);
|
||||
assert.strictEqual(result.success, false, 'Should exit non-zero');
|
||||
assert.ok(result.error.includes('Unknown state subcommand'), `Expected "Unknown state subcommand" in stderr, got: ${result.error}`);
|
||||
});
|
||||
|
||||
// Unknown subcommand: template
|
||||
test('template unknown subcommand errors', () => {
|
||||
const result = runGsdTools('template bogus', tmpDir);
|
||||
|
||||
@@ -512,6 +512,41 @@ must_haves:
|
||||
assert.strictEqual(result[0].min_lines, 100);
|
||||
});
|
||||
|
||||
test('#2734: quoted truth containing ":" is preserved as a string — not dropped', () => {
|
||||
// When a dash-item is a fully-quoted string that contains ':', the old code
|
||||
// fell into the key-value branch, failed the kvMatch regex (because the value
|
||||
// started with '"'), and silently left current as {}, losing the string.
|
||||
const content = `---
|
||||
phase: 01
|
||||
must_haves:
|
||||
truths:
|
||||
- "App-side UUIDv4: generated locally"
|
||||
- "No colon in this one"
|
||||
- "Another colon: example"
|
||||
---
|
||||
`;
|
||||
const result = parseMustHavesBlock(content, 'truths');
|
||||
assert.ok(Array.isArray(result), 'should return an array');
|
||||
assert.strictEqual(result.length, 3, `expected 3 truths, got ${result.length}: ${JSON.stringify(result)}`);
|
||||
assert.strictEqual(result[0], 'App-side UUIDv4: generated locally');
|
||||
assert.strictEqual(result[1], 'No colon in this one');
|
||||
assert.strictEqual(result[2], 'Another colon: example');
|
||||
});
|
||||
|
||||
test('#2734: single-quoted truth containing ":" is preserved as a string', () => {
|
||||
const content = `---
|
||||
phase: 01
|
||||
must_haves:
|
||||
truths:
|
||||
- 'Key: value pattern preserved'
|
||||
---
|
||||
`;
|
||||
const result = parseMustHavesBlock(content, 'truths');
|
||||
assert.ok(Array.isArray(result), 'should return an array');
|
||||
assert.strictEqual(result.length, 1);
|
||||
assert.strictEqual(result[0], 'Key: value pattern preserved');
|
||||
});
|
||||
|
||||
test('handles nested arrays within artifact objects', () => {
|
||||
const content = `---
|
||||
phase: 01
|
||||
|
||||
Reference in New Issue
Block a user