test(#2140): cover checkbox/table divergence in requirements mark-complete

Adds the fixture the green suite lacked: an ID with a checkbox and a traceability
table that does NOT mention it. Asserts (1) a checkbox-only reconcile surfaces
table_unmatched instead of a silent full-success payload, (2) re-run on the
half-written file does NOT mask the drift as already_complete, and (3) a
REQUIREMENTS.md with no traceability table stays a clean success.
This commit is contained in:
Tom Boucher
2026-07-12 13:32:59 -04:00
parent 9208c40127
commit cee8d7d723

View File

@@ -488,6 +488,71 @@ describe('requirements mark-complete command', () => {
| INFRA-01 | Phase 6 | Pending |
`;
// #2140: a traceability table EXISTS but has no row for the ID being completed.
// Only the checkbox can reconcile; the table surface is unsynced. The CLI must
// not report this as a payload indistinguishable from a full reconcile.
const TABLE_WITHOUT_FOO = `# Requirements
## Coverage
- [ ] **FOO-01**: feature one
- [ ] **BAR-01**: feature two
## Traceability
| Requirement | Phase | Status |
|-------------|-------|--------|
| BAR-01 | Phase 1 | Pending |
`;
test('#2140 checkbox-only reconcile surfaces table_unmatched (not silent success)', () => {
writeRequirements(tmpDir, TABLE_WITHOUT_FOO);
const result = runGsdTools('requirements mark-complete FOO-01', tmpDir);
assert.ok(result.success);
const out = JSON.parse(result.output);
// The checkbox WAS written, so it is in marked_complete...
assert.ok(out.marked_complete.includes('FOO-01'), 'checkbox reconcile is reported');
// ...but the traceability table had no FOO-01 row, which must be surfaced.
assert.ok(Array.isArray(out.table_unmatched), 'table_unmatched bucket must exist');
assert.ok(out.table_unmatched.includes('FOO-01'),
'an ID with a checkbox but no table row must be surfaced as table_unmatched');
const content = readRequirements(tmpDir);
assert.ok(content.includes('- [x] **FOO-01**'), 'checkbox should be checked');
// The table is untouched (no FOO-01 row synthesized).
assert.ok(!content.includes('FOO-01 | Phase'), 'no FOO-01 row should be invented');
});
test('#2140 re-run on the half-written file does NOT mask the drift as already_complete', () => {
writeRequirements(tmpDir, TABLE_WITHOUT_FOO);
// First run: flips the checkbox, surfaces table_unmatched.
runGsdTools('requirements mark-complete FOO-01', tmpDir);
// Second run on the now-[x]-checkbox-with-no-row file.
const result = runGsdTools('requirements mark-complete FOO-01', tmpDir);
assert.ok(result.success);
const out = JSON.parse(result.output);
assert.ok(!out.already_complete.includes('FOO-01'),
'a [x] checkbox with no table row is PARTIALLY reconciled, not already_complete');
assert.ok(!out.marked_complete.includes('FOO-01'),
'nothing flipped on re-run, so not marked_complete');
assert.ok(out.table_unmatched.includes('FOO-01'),
'the drift must still be surfaced as table_unmatched on re-run');
});
test('#2140 no traceability table at all → still a clean success (no table_unmatched)', () => {
// A REQUIREMENTS.md with no traceability table is legitimate; a checkbox-only
// reconcile must remain an unqualified success with no table_unmatched entry.
writeRequirements(tmpDir, '# Requirements\n\n- [ ] **NO-TABLE-01**: thing\n');
const result = runGsdTools('requirements mark-complete NO-TABLE-01', tmpDir);
assert.ok(result.success);
const out = JSON.parse(result.output);
assert.ok(out.marked_complete.includes('NO-TABLE-01'));
assert.ok(!out.table_unmatched || !out.table_unmatched.includes('NO-TABLE-01'),
'no table_unmatched when there is no traceability table');
});
test('marks single requirement complete (checkbox + table)', () => {
writeRequirements(tmpDir, STANDARD_REQUIREMENTS);