From 6b0e3904c2ea41c74ba4b3600a7e67349f0b8daf Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Sat, 11 Apr 2026 09:02:30 -0400 Subject: [PATCH] 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 --- commands/gsd/next.md | 4 +- get-shit-done/workflows/next.md | 56 ++++++++++++++++++--- tests/next-safety-gates.test.cjs | 83 +++++++++++++++++++++++++++----- 3 files changed, 123 insertions(+), 20 deletions(-) diff --git a/commands/gsd/next.md b/commands/gsd/next.md index a3793485c..eeda7ee1c 100644 --- a/commands/gsd/next.md +++ b/commands/gsd/next.md @@ -14,7 +14,9 @@ No arguments needed — reads STATE.md, ROADMAP.md, and phase directories to det Designed for rapid multi-project workflows where remembering which phase/step you're on is overhead. -Supports `--force` flag to bypass safety gates (checkpoint, error state, verification failures). +Supports `--force` flag to bypass safety gates (checkpoint, error state, verification failures, and prior-phase completeness scan). + +Before routing to the next step, scans all prior phases for incomplete work: plans that ran without producing summaries, verification failures without overrides, and phases where discussion happened but planning never ran. When incomplete work is found, shows a structured report and offers three options: defer the gaps to the backlog and continue, stop and resolve manually, or force advance without recording. When prior phases are clean, routes silently with no interruption. diff --git a/get-shit-done/workflows/next.md b/get-shit-done/workflows/next.md index 7ede7211f..828ed4292 100644 --- a/get-shit-done/workflows/next.md +++ b/get-shit-done/workflows/next.md @@ -82,12 +82,56 @@ Use `--force` to bypass this check. ``` Exit. -**Consecutive-call guard:** -After passing all gates, check a counter file `.planning/.next-call-count`: -- If file exists and count >= 6: prompt "You've called /gsd-next {N} times consecutively. Continue? [y/N]" -- If user says no, exit -- Increment the counter -- The counter file is deleted by any non-`/gsd-next` command (convention — other workflows don't need to implement this, the note here is sufficient) +**Prior-phase completeness scan:** +After passing all three hard-stop gates, scan all phases that precede the current phase in ROADMAP.md order for incomplete work. Use the existing `gsd-tools.cjs phase json ` output to inspect each prior phase. + +Detect three categories of incomplete work: +1. **Plans without summaries** — a PLAN.md exists in a prior phase directory but no matching SUMMARY.md exists (execution started but not completed). +2. **Verification failures not overridden** — a prior phase has a VERIFICATION.md with `FAIL` items that have no override annotation. +3. **CONTEXT.md without plans** — a prior phase directory has a CONTEXT.md but no PLAN.md files (discussion happened, planning never ran). + +If no incomplete prior work is found, continue to `determine_next_action` silently with no interruption. + +If incomplete prior work is found, show a structured completeness report: +``` +⚠ Prior phase has incomplete work + +Phase {N} — "{name}" has unresolved items: + • Plan {N}-{M} ({slug}): executed but no SUMMARY.md + [... additional items ...] + +Advancing before resolving these may cause: + • Verification gaps — future phase verification won't have visibility into what prior phases shipped + • Context loss — plans that ran without summaries leave no record for future agents + +Options: + [C] Continue and defer these items to backlog + [S] Stop and resolve manually (recommended) + [F] Force advance without recording deferral + +Choice [S]: +``` + +**If the user chooses "Stop" (S or Enter/default):** Exit without routing. + +**If the user chooses "Continue and defer" (C):** +1. For each incomplete item, create a backlog entry in `ROADMAP.md` under `## Backlog` using the existing `999.x` numbering scheme: +```markdown +### Phase 999.{N}: Follow-up — Phase {src} incomplete plans (BACKLOG) + +**Goal:** Resolve plans that ran without producing summaries during Phase {src} execution +**Source phase:** {src} +**Deferred at:** {date} during /gsd-next advancement to Phase {dest} +**Plans:** +- [ ] {N}-{M}: {slug} (ran, no SUMMARY.md) +``` +2. Commit the deferral record: +```bash +node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" commit "docs: defer incomplete Phase {src} items to backlog" +``` +3. Continue routing to `determine_next_action` immediately — no second prompt. + +**If the user chooses "Force" (F):** Continue to `determine_next_action` without recording deferral. diff --git a/tests/next-safety-gates.test.cjs b/tests/next-safety-gates.test.cjs index afe7a22be..91ddf70f6 100644 --- a/tests/next-safety-gates.test.cjs +++ b/tests/next-safety-gates.test.cjs @@ -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', () => {