* fix(3668): isolate --local install from global gsd-sdk - `buildGsdSdkVersionMismatchReport` now accepts `opts.isLocal`; when true it sets `fix_command` to `npx get-shit-done-cc@latest --claude --local` instead of `npm install -g …`, removing the misleading global upgrade suggestion for local installs. - Propagate `isLocal` from `installSdkIfNeeded` into the mismatch report builder so the right fix_command reaches the renderer. - Export `buildGsdSdkVersionMismatchReport` and `renderGsdSdkVersionMismatchReport` so tests can assert on the IR contract directly. - Add `command -v gsd-sdk … elif node "$GSD_TOOLS"` preflight SDK resolution block to all 69 workflow files that called bare `gsd-sdk` with no fallback, matching the pattern established in update.md, execute-phase.md, and quick.md. - Add `tests/bug-3668-local-install-sdk-soft-dep.test.cjs` with 5 tests covering Defects 1-3, including a CI lint guard that blocks future workflow regressions. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * changeset: add Fixed entry for #3668 * fix(3668): add allow-test-rule to suppress false lint-no-source-grep violation The test reads workflow .md files (product content) to assert structural invariants — not .cjs source files. The file-presence check is the only viable IR for markdown guard patterns. Add the // allow-test-rule annotation so lint-no-source-grep passes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(3668): fix do.md false-positive and discuss-phase.md size overflow Two CI failures introduced by the 69-workflow preflight block: 1. do.md: the path `bin/gsd-tools.cjs` contains `/gsd-tools` which the bug-2954 parity test regex `/\/gsd[:-]([a-z][a-z0-9-]*)/g` mistakenly extracts as a slash command named `tools`. Fix: store the shim filename in _GSD_SHIM_NAME so the path construction no longer contains a static `/gsd-tools` literal. Also wire $GSD_SDK into the actual query call. 2. discuss-phase.md: the file was at 499 lines (the 500-line budget from #2551). Adding the 11-line preflight block pushed it to 510, failing workflow-size-budget.test.cjs. Fix: compress the 11-line preflight + 2-line invocations into 3 lines (one-liner guard + two $GSD_SDK calls) returning the file to 499 lines while retaining the command -v guard required by bug-3668-local-install-sdk-soft-dep.test.cjs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(3668): wire \$GSD_SDK through all workflow callsites (#3797) PR #3797 introduced the resolution preflight block (setting \$GSD_SDK) in 69 workflows but left every downstream gsd-sdk callsite using the bare command. On local-only installs the preflight exits cleanly, then the very next line fails with 'command not found'. This is the structural gap the Codex review flagged. Changes: - 687 bare `gsd-sdk` callsites replaced with `\$GSD_SDK` across 75 workflow files (all bash/sh fenced blocks excluding the resolution guard blocks themselves) - execute-phase.md: was missing the preflight block entirely — added the standard 11-line resolution block at the initialize step - execute-phase.md: inline `if command -v gsd-sdk` availability guard (legacy #3384 fallback) replaced with `\$GSD_SDK` + error fallback since the new preflight guarantees SDK availability or exits 1 - 6 sub-workflow files (discuss-phase/modes/*, execute-phase/steps/*) that have no preflight of their own but use \$GSD_SDK — these are loaded by parent workflows that set the variable; callsites updated to use \$GSD_SDK so they work when variable is in scope Transformation script used: /private/tmp/fw2.js (regex-based fence parser with segment join invariant verification — preserves all blank lines and prose formatting). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(3668): upgrade CI guard to detect bare callsite routing (#3797) The previous Defect 3 test checked that 'command -v gsd-sdk' appeared as a string in the file — a guard-presence check, not a callsite-routing check. A workflow with the preflight block but 40 bare gsd-sdk calls below it passed the old test. This is exactly the bug state PR #3797 was supposed to fix. Upgraded test: - Parses each workflow file into markdown segments using a regex-based fence extractor (preserves all content invariantly) - Skips bash/sh blocks that contain 'command -v gsd-sdk' (those are resolution guards — bare references there are expected) - Flags any remaining bash/sh block line that invokes gsd-sdk without the \$ prefix (isBareGsdSdkInvocation predicate) - Counter-test proves the predicate correctly flags real callsite lines and correctly exempts guard assignments, comments, and \$GSD_SDK refs Also adds helper functions parseMarkdownSegments, isBareGsdSdkInvocation, and findMdFiles which are used by both the upgraded Defect 3 test and the counter-test. This test would have caught the originally-shipped bug: the preflight block was present but callsites still used bare gsd-sdk. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(tests): update workflow content tests to accept \$GSD_SDK callsite form (#3797) Six regression tests assert on the exact textual pattern of gsd-sdk calls inside workflow .md files. After the #3797 callsite replacement (687 bare `gsd-sdk` invocations replaced with `\$GSD_SDK`), these tests failed because they searched for the literal string `gsd-sdk query <cmd>` which no longer appears at callsites. Updated each test to accept both the pre-#3797 bare form and the post-#3797 variable form using `(?:\$GSD_SDK|gsd-sdk)` regex alternation (or two-branch `includes()` checks for non-regex assertions). The structural invariants each test enforces are unchanged — we're accepting the same behavioral contract through the new callsite surface. Tests fixed: - bug-2334-quick-gsd-sdk-preflight: find init.quick call via \$GSD_SDK or bare - bug-2661-roadmap-sync-parallel: roadmap.update-plan-progress call pattern - bug-3360-codex-execute-phase-worktrees: RUNTIME config-get call detection - bug-3381-verify-work-workstream: init.verify-work / phase.mvp-mode calls - enh-2433-todo-phase-linking: commit call in new-milestone.md - enh-2792-namespace-skills: validate.context invocation in context_check step Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(tests): update remaining workflow content tests to accept \$GSD_SDK form (#3797) After #3797 callsite replacement, ultraplan-phase.test.cjs and worktree-cleanup.test.cjs still assert bare gsd-sdk form. Update to accept either \$GSD_SDK or gsd-sdk. Also trim the execute-phase.md preflight comment to stay within the XL line-count budget (1810). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(3668): adopt inline-per-fence SDK resolution + restore safety semantics The brief offered three options: (a) inline preflight block per fence (b) wrapper script (c) shared shell fragment sourced at the top 71 of 72 workflow files already had inline preflight blocks (just broken ones). Option (b)/(c) would have required changes to install.js + a new shared artifact, with significant risk of breaking the install pipeline. Option (a) was the path of least resistance and least new blast radius. **BLOCKER 1+2+3 (quick.md — GSD_SDK never assigned):** - quick.md had 12 `$GSD_SDK` references but zero `GSD_SDK=` assignments. - Added proper local-first preflight block with `git rev-parse --show-toplevel` path (not the broken `CLAUDE_FILE_PATHS` which is always empty in Claude Code). - Each Bash fence in Claude Code runs as a fresh `bash -c`, so env vars don't persist. The preflight block must appear in every fence that uses $GSD_SDK. **BLOCKER 4 (execute-phase.md — || exit 1 dropped):** - Restored `|| exit 1` after every `worktree.cleanup-wave` call. SDK safety refusals (drift detection #3174, deletion block #2384) must surface, not be swallowed by the old `|| { fallback }` branch. **F5 (verify-work.md untyped fence):** - Changed bare `gsd-sdk` in an untyped fence to `$GSD_SDK`. - Changed fence tag from untyped to `bash`. **F6 (non-recursive readdirSync):** - Defect 2 test now uses `findMdFiles` (recursive) to cover workflow subdirectories, not the flat `fs.readdirSync` that missed subdirs. **F7 (lint misses untyped fences):** - `parseMarkdownSegments` now treats `lang === ''` fences as bash-fences. **F8 (missing propagation test):** - Added two propagation tests in the Defect 3 describe block. **F9 (priority inverted — global before local):** - All 72 workflow files now check `[ -f "$GSD_TOOLS" ]` before `command -v gsd-sdk`. - Path: `$(git rev-parse --show-toplevel 2>/dev/null || pwd)/get-shit-done/bin/gsd-tools.cjs` **F10/F11 (broken quoting):** - Changed `GSD_SDK="node "$GSD_TOOLS""` → `GSD_SDK="node $GSD_TOOLS"` across all files. **SDK-absence fallback removal:** - The old `|| { STATE_BACKUP=...; while IFS=...WAS_DELETED...; done }` fallback code was dead — preflight now exits if neither local nor global SDK exists. Removed from quick.md, execute-phase.md. Tests updated to verify SDK delegation rather than inline shell mechanics. **Tests updated:** - bug-2384, bug-2501, bug-2838, bug-3091, bug-3195, bug-3521, bug-3668, worktree-cleanup — all updated to reflect SDK delegation contract. - Defect 2 test now uses bash-fence scan (not raw content) to skip docs-only gsd-sdk prose references (e.g. discuss-phase/modes/text.md). Closes #3668 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(3668): restore _GSD_SHIM_NAME indirection in do.md to prevent false-positive The top commit re-introduced a literal /get-shit-done/bin/gsd-tools.cjs path in do.md, causing bug-2954 test to match /gsd-tools as an unshipped slash command. Restore the _GSD_SHIM_NAME variable indirection (from ff9939e5) to break the literal path while preserving local-first preference order. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(tests): update worktree.test.cjs to accept SDK delegation contract (#3797) Mirror the contract update already applied to worktree-cleanup.test.cjs: - pre-merge deletion check tests: accept worktree.cleanup-wave + deletion mention as valid (inline --diff-filter=D was in the removed shell fallback) - quick.md bug-2431 tests (lock-aware, unlock retry, residual warning): accept worktree.cleanup-wave delegation as sufficient (these safety behaviors are now handled internally by the SDK cleanup-wave command) execute-phase.md tests unchanged: it retains inline .git/worktrees/, locked, git worktree unlock, and Residual worktree in its cleanup-tail snippet. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(3668): refactor bug-2384 and bug-2838 from grep to structured assertions Replace content.includes() on readFileSync-bound variables with parser functions that split lines and return typed boolean fields, matching the project's no-source-grep contract (lint-no-source-grep rule F/G). --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
20 KiB
<required_reading> Read all files referenced by the invoking prompt's execution_context before starting. </required_reading>
**Load progress context (paths only):**# SDK resolution: prefer local gsd-tools.cjs, fall back to global gsd-sdk (#3668)
GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/gsd-tools.cjs"
if [ -f "$GSD_TOOLS" ]; then
GSD_SDK="node $GSD_TOOLS"
elif command -v gsd-sdk >/dev/null 2>&1; then
GSD_SDK="gsd-sdk"
else
echo "ERROR: gsd-sdk not found on PATH and $GSD_TOOLS does not exist." >&2
echo "Run: npx get-shit-done-cc@latest --claude --local" >&2
exit 1
fi
INIT=$($GSD_SDK query init.progress)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
Extract from init JSON: project_exists, roadmap_exists, state_exists, phases, current_phase, next_phase, milestone_version, completed_count, phase_count, paused_at, state_path, roadmap_path, project_path, config_path.
DISCUSS_MODE=$($GSD_SDK query config-get workflow.discuss_mode 2>/dev/null || echo "discuss")
If project_exists is false (no .planning/ directory):
No planning structure found.
Run /gsd:new-project to start a new project.
Exit.
If missing STATE.md: suggest /gsd:new-project.
If ROADMAP.md missing but PROJECT.md exists:
This means a milestone was completed and archived. Go to Route F (between milestones).
If missing both ROADMAP.md and PROJECT.md: suggest /gsd:new-project.
Instead of reading full files, use targeted tools to get only the data needed for the report:
ROADMAP=$(gsd-sdk query roadmap.analyze)STATE=$(gsd-sdk query state-snapshot)
This minimizes orchestrator context usage.
**Get comprehensive roadmap analysis (replaces manual parsing):**ROADMAP=$($GSD_SDK query roadmap.analyze)
This returns structured JSON with:
- All phases with disk status (complete/partial/planned/empty/no_directory)
- Goal and dependencies per phase
- Plan and summary counts per phase
- Aggregated stats: total plans, summaries, progress percent
- Current and next phase identification
Use this instead of manually reading/parsing ROADMAP.md.
**Gather recent work context:**- Find the 2-3 most recent SUMMARY.md files
- Use
summary-extractfor efficient parsing:$GSD_SDK query summary-extract <path> --fields one_liner - This shows "what we've been working on"
- Use
current_phaseandnext_phasefrom$ROADMAP - Note
paused_atif work was paused (from$STATE) - Count pending todos: use
init todosorlist-todos - Check for active debug sessions:
(ls .planning/debug/*.md 2>/dev/null || true) | grep -v resolved | wc -l
Generate progress bar from gsd-sdk query progress / progress.json, then present rich status report:
# Get formatted progress bar
PROGRESS_BAR=$($GSD_SDK query progress.bar --raw)
Present:
# [Project Name]
**Progress:** {PROGRESS_BAR}
**Profile:** [quality/balanced/budget/inherit]
**Discuss mode:** {DISCUSS_MODE}
## Recent Work
- [Phase X, Plan Y]: [what was accomplished - 1 line from summary-extract]
- [Phase X, Plan Z]: [what was accomplished - 1 line from summary-extract]
## Current Position
Phase [N] of [total]: [phase-name]
Plan [M] of [phase-total]: [status]
CONTEXT: [✓ if has_context | - if not]
## Key Decisions Made
- [extract from $STATE.decisions[]]
- [e.g. jq -r '.decisions[].decision' from state-snapshot]
## Blockers/Concerns
- [extract from $STATE.blockers[]]
- [e.g. jq -r '.blockers[].text' from state-snapshot]
## Pending Todos
- [count] pending — /gsd:capture --list to review
## Active Debug Sessions
- [count] active — /gsd:debug to continue
(Only show this section if count > 0)
## What's Next
[Next phase/plan objective from roadmap analyze]
Resolve MVP_MODE per phase via the centralized resolver. progress has no --mvp CLI flag (mode is inherited from the planned phase), so we omit --cli-flag:
MVP_MODE=$($GSD_SDK query phase.mvp-mode "${PHASE_NUMBER}" --pick active)
When MVP_MODE=true, the per-phase progress block adds a user-flow status sub-block sourced from the phase's PLAN.md task names. Each task whose name reads like a user-visible capability (e.g., "Register flow", "Login flow", "Password reset") is rendered as a status line:
Phase 1 — User Auth MVP
✅ Walking Skeleton complete ← from SKELETON.md existence
✅ Register flow working ← from PLAN.md task with summary
✅ Login flow working ← from PLAN.md task with summary
🔄 Password reset (in progress) ← from PLAN.md task without summary
⬜ Email verification ← from PLAN.md task not yet started
User-flow filter: Tasks whose names are technical-sounding ("Wire DB schema", "Create migration", "Bump deps") are NOT rendered as user-flow status lines. Heuristic: a task name is user-flow-shaped if it ends in "flow", "page", "screen", or starts with a verb the user would recognize ("Register", "Login", "Upload", "View"). Tasks that fail the heuristic still count toward the standard task progress total but don't appear in the user-flow sub-block.
When MVP_MODE=false (mode is null, absent, or the phase has no **Mode:** line), fall back to the standard display path — no behavioral change.
Step 1: Count plans, summaries, and issues in current phase
List files in the current phase directory:
(ls -1 .planning/phases/[current-phase-dir]/*-PLAN.md 2>/dev/null || true) | wc -l
(ls -1 .planning/phases/[current-phase-dir]/*-SUMMARY.md 2>/dev/null || true) | wc -l
(ls -1 .planning/phases/[current-phase-dir]/*-UAT.md 2>/dev/null || true) | wc -l
State: "This phase has {X} plans, {Y} summaries."
Step 1.5: Check for unaddressed UAT gaps
Check for UAT.md files with status "diagnosed" (has gaps needing fixes).
# Check for diagnosed UAT with gaps or partial (incomplete) testing
grep -l "status: diagnosed\|status: partial" .planning/phases/[current-phase-dir]/*-UAT.md 2>/dev/null || true
Track:
uat_with_gaps: UAT.md files with status "diagnosed" (gaps need fixing)uat_partial: UAT.md files with status "partial" (incomplete testing)
Step 1.6: Cross-phase health check
Scan ALL phases in the current milestone for outstanding verification debt using the CLI (which respects milestone boundaries via getMilestonePhaseFilter):
DEBT=$($GSD_SDK query audit-uat --raw 2>/dev/null)
Parse JSON for summary.total_items and summary.total_files.
Track: outstanding_debt — summary.total_items from the audit.
If outstanding_debt > 0: Add a warning section to the progress report output (in the report step), placed between "## What's Next" and the route suggestion:
## Verification Debt ({N} files across prior phases)
| Phase | File | Issue |
|-------|------|-------|
| {phase} | {filename} | {pending_count} pending, {skipped_count} skipped, {blocked_count} blocked |
| {phase} | {filename} | human_needed — {count} items |
Review: `/gsd:audit-uat ${GSD_WS}` — full cross-phase audit
Resume testing: `/gsd:verify-work {phase} ${GSD_WS}` — retest specific phase
This is a WARNING, not a blocker — routing proceeds normally. The debt is visible so the user can make an informed choice.
Step 2: Route based on counts
| Condition | Meaning | Action |
|---|---|---|
| uat_partial > 0 | UAT testing incomplete | Go to Route E.2 |
| uat_with_gaps > 0 | UAT gaps need fix plans | Go to Route E |
| summaries < plans | Unexecuted plans exist | Go to Route A |
| summaries = plans AND plans > 0 | Phase complete | Go to Step 3 |
| plans = 0 | Phase not yet planned | Go to Route B |
Route A: Unexecuted plan exists
Find the first PLAN.md without matching SUMMARY.md.
Read its <objective> section.
---
## ▶ Next Up — [${PROJECT_CODE}] ${PROJECT_TITLE}
**{phase}-{plan}: [Plan Name]** — [objective summary from PLAN.md]
`/clear` then:
`/gsd:execute-phase {phase} ${GSD_WS}`
---
Route B: Phase needs planning
Check if {phase_num}-CONTEXT.md exists in phase directory.
Check if current phase has UI indicators:
PHASE_SECTION=$($GSD_SDK query roadmap.get-phase "${CURRENT_PHASE}" 2>/dev/null)
PHASE_HAS_UI=$(echo "$PHASE_SECTION" | grep -qi "UI hint.*yes" && echo "true" || echo "false")
If CONTEXT.md exists:
---
## ▶ Next Up — [${PROJECT_CODE}] ${PROJECT_TITLE}
**Phase {N}: {Name}** — {Goal from ROADMAP.md}
<sub>✓ Context gathered, ready to plan</sub>
`/clear` then:
`/gsd:plan-phase {phase-number} ${GSD_WS}`
---
If CONTEXT.md does NOT exist AND phase has UI (PHASE_HAS_UI is true):
---
## ▶ Next Up — [${PROJECT_CODE}] ${PROJECT_TITLE}
**Phase {N}: {Name}** — {Goal from ROADMAP.md}
`/clear` then:
`/gsd:discuss-phase {phase}` — gather context and clarify approach
---
**Also available:**
- `/gsd:ui-phase {phase}` — generate UI design contract (recommended for frontend phases)
- `/gsd:plan-phase {phase}` — skip discussion, plan directly
- `/gsd:discuss-phase {phase}` — include assumptions check before planning
---
If CONTEXT.md does NOT exist AND phase has no UI:
---
## ▶ Next Up — [${PROJECT_CODE}] ${PROJECT_TITLE}
**Phase {N}: {Name}** — {Goal from ROADMAP.md}
`/clear` then:
`/gsd:discuss-phase {phase} ${GSD_WS}` — gather context and clarify approach
---
**Also available:**
- `/gsd:plan-phase {phase} ${GSD_WS}` — skip discussion, plan directly
- `/gsd:discuss-phase {phase} ${GSD_WS}` — include assumptions check before planning
---
Route E: UAT gaps need fix plans
UAT.md exists with gaps (diagnosed issues). User needs to plan fixes.
---
## ⚠ UAT Gaps Found
**{phase_num}-UAT.md** has {N} gaps requiring fixes.
`/clear` then:
`/gsd:plan-phase {phase} --gaps ${GSD_WS}`
---
**Also available:**
- `/gsd:execute-phase {phase} ${GSD_WS}` — execute phase plans
- `/gsd:verify-work {phase} ${GSD_WS}` — run more UAT testing
---
Route E.2: UAT testing incomplete (partial)
UAT.md exists with status: partial — testing session ended before all items resolved.
---
## Incomplete UAT Testing
**{phase_num}-UAT.md** has {N} unresolved tests (pending, blocked, or skipped).
`/clear` then:
`/gsd:verify-work {phase} ${GSD_WS}` — resume testing from where you left off
---
**Also available:**
- `/gsd:audit-uat ${GSD_WS}` — full cross-phase UAT audit
- `/gsd:execute-phase {phase} ${GSD_WS}` — execute phase plans
---
Step 3: Check milestone status (only when phase complete)
Read ROADMAP.md and identify:
- Current phase number
- All phase numbers in the current milestone section
Count total phases and identify the highest phase number.
State: "Current phase is {X}. Milestone has {N} phases (highest: {Y})."
Route based on milestone status:
| Condition | Meaning | Action |
|---|---|---|
| current phase < highest phase | More phases remain | Go to Route C |
| current phase = highest phase | Milestone complete | Go to Route D |
Route C: Phase complete, more phases remain
Read ROADMAP.md to get the next phase's name and goal.
Check if next phase has UI indicators:
NEXT_PHASE_SECTION=$($GSD_SDK query roadmap.get-phase "$((Z+1))" 2>/dev/null)
NEXT_HAS_UI=$(echo "$NEXT_PHASE_SECTION" | grep -qi "UI hint.*yes" && echo "true" || echo "false")
If next phase has UI (NEXT_HAS_UI is true):
---
## ✓ Phase {Z} Complete
## ▶ Next Up — [${PROJECT_CODE}] ${PROJECT_TITLE}
**Phase {Z+1}: {Name}** — {Goal from ROADMAP.md}
`/clear` then:
`/gsd:discuss-phase {Z+1}` — gather context and clarify approach
---
**Also available:**
- `/gsd:ui-phase {Z+1}` — generate UI design contract (recommended for frontend phases)
- `/gsd:plan-phase {Z+1}` — skip discussion, plan directly
- `/gsd:verify-work {Z}` — user acceptance test before continuing
---
If next phase has no UI:
---
## ✓ Phase {Z} Complete
## ▶ Next Up — [${PROJECT_CODE}] ${PROJECT_TITLE}
**Phase {Z+1}: {Name}** — {Goal from ROADMAP.md}
`/clear` then:
`/gsd:discuss-phase {Z+1} ${GSD_WS}` — gather context and clarify approach
---
**Also available:**
- `/gsd:plan-phase {Z+1} ${GSD_WS}` — skip discussion, plan directly
- `/gsd:verify-work {Z} ${GSD_WS}` — user acceptance test before continuing
---
Route D: Milestone complete
---
## 🎉 Milestone Complete
All {N} phases finished!
## ▶ Next Up — [${PROJECT_CODE}] ${PROJECT_TITLE}
**Complete Milestone** — archive and prepare for next
`/clear` then:
`/gsd:complete-milestone ${GSD_WS}`
---
**Also available:**
- `/gsd:verify-work ${GSD_WS}` — user acceptance test before completing milestone
---
Route F: Between milestones (ROADMAP.md missing, PROJECT.md exists)
A milestone was completed and archived. Ready to start the next milestone cycle.
Read MILESTONES.md to find the last completed milestone version.
---
## ✓ Milestone v{X.Y} Complete
Ready to plan the next milestone.
## ▶ Next Up — [${PROJECT_CODE}] ${PROJECT_TITLE}
**Start Next Milestone** — questioning → research → requirements → roadmap
`/clear` then:
`/gsd:new-milestone ${GSD_WS}`
---
- Phase complete but next phase not planned → offer
/gsd:plan-phase [next] ${GSD_WS} - All work complete → offer milestone completion
- Blockers present → highlight before offering to continue
- Handoff file exists → mention it, offer
/gsd:resume-work ${GSD_WS}
If --forensic is NOT present in ARGUMENTS: skip this step entirely. Default progress behavior (standard report + routing) is unchanged.
If --forensic IS present: after the standard report and routing suggestion have been displayed, append the following audit section.
Forensic Integrity Audit
Running 6 deep checks against project state...
Run each check in order. For each check, emit ✓ (pass) or ⚠ (warning) with concrete evidence when a problem is found.
Check 1 — STATE vs artifact consistency
Read STATE.md status / stopped_at fields (from the STATE snapshot already loaded). Compare against the artifact count from the roadmap analysis. If STATE.md claims the current phase is pending/mid-flight but the artifact count shows it as complete (all PLAN.md files have matching SUMMARY.md files), flag inconsistency. Emit:
- ✓
STATE.md consistent with artifact count— if both agree - ⚠
STATE.md claims [status] but artifact count shows phase complete— with the specific values
Check 2 — Orphaned handoff files
Check for existence of:
ls .planning/HANDOFF.json .planning/phases/*/.continue-here.md .planning/phases/*/*HANDOFF*.md 2>/dev/null || true
Also check .planning/continue-here.md.
Emit:
- ✓
No orphaned handoff files— if none found - ⚠
Orphaned handoff files found— list each file path, add:→ Work was paused mid-flight. Read the handoff before continuing.
Check 3 — Deferred scope drift
Search phase artifacts (CONTEXT.md, DISCUSSION-LOG.md, BUG-BRIEF.md, VERIFICATION.md, SUMMARY.md, HANDOFF.md files under .planning/phases/) for patterns:
grep -rl "defer to Phase\|future phase\|out of scope Phase\|deferred to Phase" .planning/phases/ 2>/dev/null || true
For each match, extract the referenced phase number. Cross-reference against ROADMAP.md phase list. If the referenced phase number is NOT in ROADMAP.md, flag as deferred scope not captured.
Emit:
- ✓
All deferred scope captured in ROADMAP— if no mismatches - ⚠
Deferred scope references phase(s) not in ROADMAP— list: file, reference text, missing phase number
Check 4 — Memory-flagged pending work
Check if .planning/MEMORY.md or .planning/memory/ exists:
ls .planning/MEMORY.md .planning/memory/*.md 2>/dev/null || true
If found, grep for entries containing: pending, status, deferred, not yet run, backfill, blocking.
Emit:
- ✓
No memory entries flagging pending work— if none found or no MEMORY.md - ⚠
Memory entries flag pending/deferred work— list the matching lines (max 5, truncated at 80 chars)
Check 5 — Blocking operational todos
Check for pending todos:
ls .planning/todos/pending/*.md 2>/dev/null || true
For files found, scan for keywords indicating operational blockers: script, credential, API key, manual, verification, setup, configure, run .
Emit:
- ✓
No blocking operational todos— if no pending todos or none match operational keywords - ⚠
Blocking operational todos found— list the file names and matching keywords (max 5)
Check 6 — Uncommitted code
git status --porcelain 2>/dev/null | grep -v "^??" | grep -v "^.planning\/" | grep -v "^\.\." | head -10
If output is non-empty (modified/staged files outside .planning/), flag as uncommitted code.
Emit:
- ✓
Working tree clean— if no modified files outside.planning/ - ⚠
Uncommitted changes in source files— list up to 10 file paths
After all 6 checks, display the verdict:
If all 6 checks passed:
### Verdict: CLEAN
The standard progress report is trustworthy — proceed with the routing suggestion above.
If 1 or more checks failed:
### Verdict: N INTEGRITY ISSUE(S) FOUND
The standard progress report may not reflect true project state.
Review the flagged items above before acting on the routing suggestion.
Then for each failed check, add a concrete next action:
- Check 2 (orphaned handoff):
Read the handoff file(s) and resume from where work was paused: /gsd:resume-work ${GSD_WS} - Check 3 (deferred scope):
Add the missing phases to ROADMAP.md or update the deferred references - Check 4 (memory pending):
Review the flagged memory entries and resolve or clear them - Check 5 (blocking todos):
Complete the operational steps in .planning/todos/pending/ before continuing - Check 6 (uncommitted code):
Commit or stash the uncommitted changes before advancing - Check 1 (STATE inconsistency):
Run /gsd:verify-work ${PHASE} ${GSD_WS} to reconcile state
<success_criteria>
- Rich context provided (recent work, decisions, issues)
- Current position clear with visual progress
- What's next clearly explained
- Smart routing: /gsd:execute-phase if plans exist, /gsd:plan-phase if not
- User confirms before any action
- Seamless handoff to appropriate gsd command </success_criteria>