enhancement(workflow): replace consecutive-call counter with prior-phase completeness scan in /gsd-next (#2097)

Removes the .next-call-count counter file guard (which fired on clean usage and missed
real incomplete work) and replaces it with a scan of all prior phases for plans without
summaries, unoverridden VERIFICATION.md failures, and phases with CONTEXT.md but no plans.
When gaps are found, shows a structured report with Continue/Stop/Force options; the
Continue path writes a formal 999.x backlog entry and commits it before routing. Clean
projects route silently with no interruption.

Closes #2089

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-04-11 09:02:30 -04:00
committed by GitHub
parent aa4532b820
commit 6b0e3904c2
3 changed files with 123 additions and 20 deletions

View File

@@ -1,11 +1,11 @@
/**
* GSD Tools Tests - /gsd-next safety gates and consecutive-call guard
* GSD Tools Tests - /gsd-next safety gates and prior-phase completeness scan
*
* Validates that the next workflow includes three hard-stop safety gates
* (checkpoint, error state, verification), a consecutive-call budget guard,
* and a --force bypass flag.
* (checkpoint, error state, verification), a prior-phase completeness scan
* replacing the old consecutive-call counter, and a --force bypass flag.
*
* Closes: #1732
* Closes: #1732, #2089
*/
const { test, describe } = require('node:test');
@@ -13,7 +13,7 @@ const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
describe('/gsd-next safety gates (#1732)', () => {
describe('/gsd-next safety gates (#1732, #2089)', () => {
const workflowPath = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'next.md');
const commandPath = path.join(__dirname, '..', 'commands', 'gsd', 'next.md');
@@ -79,19 +79,72 @@ describe('/gsd-next safety gates (#1732)', () => {
);
});
test('consecutive-call budget guard', () => {
test('prior-phase completeness scan replaces consecutive-call counter', () => {
const content = fs.readFileSync(workflowPath, 'utf8');
assert.ok(
content.includes('.next-call-count'),
'workflow should reference .next-call-count counter file'
content.includes('Prior-phase completeness scan'),
'workflow should have a prior-phase completeness scan section'
);
assert.ok(
content.includes('6'),
'consecutive guard should trigger at count >= 6'
!content.includes('.next-call-count'),
'workflow must not reference the old .next-call-count counter file'
);
assert.ok(
content.includes('consecutively'),
'guard should mention consecutive calls'
!content.includes('consecutively'),
'workflow must not reference consecutive call counting'
);
});
test('completeness scan checks plans without summaries', () => {
const content = fs.readFileSync(workflowPath, 'utf8');
assert.ok(
content.includes('Plans without summaries') || content.includes('no SUMMARY.md'),
'completeness scan should detect plans that ran without producing summaries'
);
});
test('completeness scan checks verification failures in prior phases', () => {
const content = fs.readFileSync(workflowPath, 'utf8');
assert.ok(
content.includes('Verification failures not overridden') ||
content.includes('VERIFICATION.md with `FAIL`'),
'completeness scan should detect unoverridden FAIL items in prior phase VERIFICATION.md'
);
});
test('completeness scan checks CONTEXT.md without plans', () => {
const content = fs.readFileSync(workflowPath, 'utf8');
assert.ok(
content.includes('CONTEXT.md without plans') ||
content.includes('CONTEXT.md but no PLAN.md'),
'completeness scan should detect phases with discussion but no planning'
);
});
test('completeness scan offers Continue, Stop, and Force options', () => {
const content = fs.readFileSync(workflowPath, 'utf8');
assert.ok(content.includes('[C]'), 'completeness scan should offer [C] Continue option');
assert.ok(content.includes('[S]'), 'completeness scan should offer [S] Stop option');
assert.ok(content.includes('[F]'), 'completeness scan should offer [F] Force option');
});
test('deferral path creates backlog entry using 999.x scheme', () => {
const content = fs.readFileSync(workflowPath, 'utf8');
assert.ok(
content.includes('999.'),
'deferral should use the 999.x backlog numbering scheme'
);
assert.ok(
content.includes('Backlog') || content.includes('BACKLOG'),
'deferral should write to the Backlog section of ROADMAP.md'
);
});
test('clean prior phases route silently with no interruption', () => {
const content = fs.readFileSync(workflowPath, 'utf8');
assert.ok(
content.includes('silently') || content.includes('no interruption'),
'workflow should route without interruption when prior phases are clean'
);
});
@@ -107,7 +160,7 @@ describe('/gsd-next safety gates (#1732)', () => {
);
});
test('command definition documents --force flag', () => {
test('command definition documents --force flag and completeness scan', () => {
const content = fs.readFileSync(commandPath, 'utf8');
assert.ok(
content.includes('--force'),
@@ -117,6 +170,10 @@ describe('/gsd-next safety gates (#1732)', () => {
content.includes('bypass safety gates'),
'command definition should explain that --force bypasses safety gates'
);
assert.ok(
content.includes('completeness'),
'command definition should document the prior-phase completeness scan'
);
});
test('gates exit on first hit', () => {