diff --git a/.changeset/scrub-stale-command-routes.md b/.changeset/scrub-stale-command-routes.md new file mode 100644 index 000000000..da792c41d --- /dev/null +++ b/.changeset/scrub-stale-command-routes.md @@ -0,0 +1,5 @@ +--- +type: Fixed +pr: 3029 +--- +**`/gsd-code-review-fix` and `/gsd-plan-milestone-gaps` no longer surface as "Unknown command"** — both were consolidated by #2790 (`/gsd-code-review --fix` and inline gap planning in `/gsd-audit-milestone` respectively), but several user-facing surfaces still emitted the old slash forms in their offer text. Fixed audit-milestone offer blocks, gsd-complete-milestone routing, code-review/execute-phase offer text, gsd-code-fixer agent role card, and the doc surfaces (USER-GUIDE, FEATURES, INVENTORY, AGENTS, CONFIGURATION). Closes #3029, closes #3034. diff --git a/agents/gsd-code-fixer.md b/agents/gsd-code-fixer.md index f184a1330..1960f4a86 100644 --- a/agents/gsd-code-fixer.md +++ b/agents/gsd-code-fixer.md @@ -1,6 +1,6 @@ --- name: gsd-code-fixer -description: Applies fixes to code review findings from REVIEW.md. Reads source files, applies intelligent fixes, and commits each fix atomically. Spawned by /gsd-code-review-fix. +description: Applies fixes to code review findings from REVIEW.md. Reads source files, applies intelligent fixes, and commits each fix atomically. Spawned by /gsd-code-review --fix. tools: Read, Edit, Write, Bash, Grep, Glob color: "#10B981" # hooks: @@ -10,7 +10,7 @@ color: "#10B981" You are a GSD code fixer. You apply fixes to issues found by the gsd-code-reviewer agent. -Spawned by `/gsd-code-review-fix` workflow. You produce REVIEW-FIX.md artifact in the phase directory. +Spawned by `/gsd-code-review --fix` workflow. You produce REVIEW-FIX.md artifact in the phase directory. Your job: Read REVIEW.md findings, fix source code intelligently (not blind application), commit each fix atomically, and produce REVIEW-FIX.md report. @@ -295,7 +295,7 @@ cd "$wt" Concrete steps: 1. Parse `padded_phase` and `phase_dir` from the `` block (needed for the path and for the sentinel location). 2. Resolve the current branch: `branch=$(git branch --show-current)`. If empty (detached HEAD), print an error and exit — detached-HEAD state is not supported; commits made in a detached-HEAD worktree would not advance the branch. -3. **Recovery check (#2839, #2990):** If `${phase_dir}/.review-fix-recovery-pending.json` already exists, a prior run was interrupted. Parse the JSON, attempt to remove the orphan worktree it points at (best-effort, with `--force`), and delete the stale `reviewfix_branch` (best-effort, with `git branch -D`), then delete the stale sentinel before continuing. This makes a re-run of `/gsd-code-review-fix` self-healing. +3. **Recovery check (#2839, #2990):** If `${phase_dir}/.review-fix-recovery-pending.json` already exists, a prior run was interrupted. Parse the JSON, attempt to remove the orphan worktree it points at (best-effort, with `--force`), and delete the stale `reviewfix_branch` (best-effort, with `git branch -D`), then delete the stale sentinel before continuing. This makes a re-run of `/gsd-code-review --fix` self-healing. 4. Create a unique worktree path: `wt=$(mktemp -d "/tmp/sv-${padded_phase}-reviewfix-XXXXXX")`. The `mktemp` suffix ensures concurrent runs for the same phase do not collide. 5. Run `git worktree add -b "$reviewfix_branch" "$wt" "$branch"` — this creates a NEW branch (`gsd-reviewfix/${padded_phase}-$$`) starting from the current branch tip and attaches the worktree to that new branch. Attaching to a new branch (rather than `$branch` directly) is what allows the worktree to coexist with the user's checkout — git refuses to check out the same branch in two worktrees by default (#2990). Commits made inside the worktree advance `$reviewfix_branch`; the cleanup tail fast-forwards `$branch` to `$reviewfix_branch` so the user's branch ends up with the agent's commits. 6. **Write the recovery sentinel** at `${phase_dir}/.review-fix-recovery-pending.json` containing `{worktree_path, branch, reviewfix_branch, padded_phase, started_at}`. Doing this AFTER `git worktree add` ensures the sentinel only ever points at a real worktree. The sentinel includes `reviewfix_branch` so recovery can clean both the orphan worktree AND its temp branch. diff --git a/commands/gsd/complete-milestone.md b/commands/gsd/complete-milestone.md index 1485de093..8ad15b146 100644 --- a/commands/gsd/complete-milestone.md +++ b/commands/gsd/complete-milestone.md @@ -43,7 +43,10 @@ Output: Milestone archived (roadmap + requirements), PROJECT.md evolved, git tag - Look for `.planning/v{{version}}-MILESTONE-AUDIT.md` - If missing or stale: recommend `/gsd-audit-milestone` first - - If audit status is `gaps_found`: recommend `/gsd-plan-milestone-gaps` first + - If audit status is `gaps_found`: recommend closing the gaps inline + (the audit output already enumerates them — insert closure phases + via `/gsd-phase --insert ` plus the standard + discuss/plan/execute chain) before proceeding. - If audit status is `passed`: proceed to step 1 ```markdown @@ -54,8 +57,11 @@ Output: Milestone archived (roadmap + requirements), PROJECT.md evolved, git tag requirements coverage, cross-phase integration, and E2E flows. {If audit has gaps:} - ⚠ Milestone audit found gaps. Run `/gsd-plan-milestone-gaps` to create - phases that close the gaps, or proceed anyway to accept as tech debt. + ⚠ Milestone audit found gaps. The audit output already enumerates the + unsatisfied requirements, cross-phase issues, and broken flows — insert + a closure phase per gap with `/gsd-phase --insert ` and run the + standard `/gsd-discuss-phase` → `/gsd-plan-phase` → `/gsd-execute-phase` + chain. Or proceed anyway to accept the gaps as tech debt. {If audit passed:} ✓ Milestone audit passed. Proceeding with completion. diff --git a/docs/AGENTS.md b/docs/AGENTS.md index b877b199d..3c6ef03d6 100644 --- a/docs/AGENTS.md +++ b/docs/AGENTS.md @@ -1,6 +1,6 @@ # GSD Agent Reference -> Full role cards for 21 primary agents plus concise stubs for 10 advanced/specialized agents (31 shipped agents total). The `agents/` directory and [`docs/INVENTORY.md`](INVENTORY.md) are the authoritative roster; see [Architecture](ARCHITECTURE.md) for context. +> Full role cards for 21 primary agents plus concise stubs for 12 advanced/specialized agents (33 shipped agents total). The `agents/` directory and [`docs/INVENTORY.md`](INVENTORY.md) are the authoritative roster; see [Architecture](ARCHITECTURE.md) for context. --- @@ -10,7 +10,7 @@ GSD uses a multi-agent architecture where thin orchestrators (workflow files) sp ### Agent Categories -> The table below covers the **21 primary agents** detailed in this section. Ten additional shipped agents (pattern-mapper, debug-session-manager, code-reviewer, code-fixer, ai-researcher, domain-researcher, eval-planner, eval-auditor, framework-selector, intel-updater) have concise stubs in the [Advanced and Specialized Agents](#advanced-and-specialized-agents) section below. For the authoritative 31-agent roster, see [`docs/INVENTORY.md`](INVENTORY.md) and the `agents/` directory. +> The table below covers the **21 primary agents** detailed in this section. Twelve additional shipped agents (pattern-mapper, debug-session-manager, code-reviewer, code-fixer, ai-researcher, domain-researcher, eval-planner, eval-auditor, framework-selector, intel-updater, doc-classifier, doc-synthesizer) have concise stubs in the [Advanced and Specialized Agents](#advanced-and-specialized-agents) section below. For the authoritative 33-agent roster, see [`docs/INVENTORY.md`](INVENTORY.md) and the `agents/` directory. | Category | Count | Agents | |----------|-------|--------| @@ -480,7 +480,7 @@ Communication style, decision patterns, debugging approach, UX preferences, vend ## Advanced and Specialized Agents -Ten additional agents ship under `agents/gsd-*.md` and are used by specialty workflows (`/gsd-ai-integration-phase`, `/gsd-eval-review`, `/gsd-code-review`, `/gsd-code-review-fix`, `/gsd-debug`, `/gsd-intel`, `/gsd-select-framework`) and by the planner pipeline. Each carries full frontmatter in its agent file; the stubs below are concise by design. The authoritative roster (with spawner and primary-doc status per agent) lives in [`docs/INVENTORY.md`](INVENTORY.md). +Twelve additional agents ship under `agents/gsd-*.md` and are used by specialty workflows (`/gsd-ai-integration-phase`, `/gsd-eval-review`, `/gsd-code-review`, `/gsd-code-review --fix`, `/gsd-debug`, `/gsd-intel`, `/gsd-select-framework`, `/gsd-ingest-docs`) and by the planner pipeline. Each carries full frontmatter in its agent file; the stubs below are concise by design. The authoritative roster (with spawner and primary-doc status per agent) lives in [`docs/INVENTORY.md`](INVENTORY.md). ### gsd-pattern-mapper @@ -548,7 +548,7 @@ Ten additional agents ship under `agents/gsd-*.md` and are used by specialty wor | Property | Value | |----------|-------| -| **Spawned by** | `/gsd-code-review-fix` | +| **Spawned by** | `/gsd-code-review --fix` | | **Parallelism** | Single instance | | **Tools** | Read, Edit, Write, Bash, Grep, Glob | | **Model (balanced)** | Sonnet | diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index 8c0590d1a..04d39a14c 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -199,7 +199,7 @@ All workflow toggles follow the **absent = enabled** pattern. If a key is missin | `workflow.text_mode` | boolean | `false` | Replaces AskUserQuestion TUI menus with plain-text numbered lists. Required for Claude Code remote sessions (`/rc` mode) where TUI menus don't render. Can also be set per-session with `--text` flag on discuss-phase. Added in v1.28 | | `workflow.use_worktrees` | boolean | `true` | When `false`, disables git worktree isolation for parallel execution. Users who prefer sequential execution or whose environment does not support worktrees can disable this. Added in v1.31 | | `workflow.worktree_skip_hooks` | boolean | `false` | When `true`, executor agents in worktree mode pass `--no-verify` (skipping pre-commit hooks) and post-wave hook validation runs against the merged result instead. Opt-in escape hatch for projects whose hooks cannot run in agent worktrees. Default `false` runs hooks on every commit (#2924). | -| `workflow.code_review` | boolean | `true` | Enable `/gsd-code-review` and `/gsd-code-review-fix` commands. When `false`, the commands exit with a configuration gate message. Added in v1.34 | +| `workflow.code_review` | boolean | `true` | Enable `/gsd-code-review` and `/gsd-code-review --fix` commands. When `false`, the commands exit with a configuration gate message. Added in v1.34 | | `workflow.code_review_depth` | string | `standard` | Default review depth for `/gsd-code-review`: `quick` (pattern-matching only), `standard` (per-file analysis), or `deep` (cross-file with import graphs). Can be overridden per-run with `--depth=`. Added in v1.34 | | `workflow.plan_bounce` | boolean | `false` | Run external validation script against generated plans. When enabled, the plan-phase orchestrator pipes each PLAN.md through the script specified by `plan_bounce_script` and blocks on non-zero exit. Added in v1.36 | | `workflow.plan_bounce_script` | string | (none) | Path to the external script invoked for plan bounce validation. Receives the PLAN.md path as its first argument. Required when `plan_bounce` is `true`. Added in v1.36 | diff --git a/docs/FEATURES.md b/docs/FEATURES.md index a048cdfbe..bc34529f2 100644 --- a/docs/FEATURES.md +++ b/docs/FEATURES.md @@ -2121,7 +2121,7 @@ Test suite that scans all agent, workflow, and command files for embedded inject ### 93. Code Review Pipeline -**Commands:** `/gsd-code-review`, `/gsd-code-review-fix` +**Commands:** `/gsd-code-review`, `/gsd-code-review --fix` **Purpose:** Structured review of source files changed during a phase, with a separate auto-fix pass that commits each fix atomically. @@ -2129,7 +2129,7 @@ Test suite that scans all agent, workflow, and command files for embedded inject - REQ-REVIEW-01: `gsd-code-review` MUST scope files to the phase using SUMMARY.md and git diff fallback - REQ-REVIEW-02: Review MUST support three depth levels: `quick`, `standard`, `deep` - REQ-REVIEW-03: Findings MUST be severity-classified: Critical, Warning, Info -- REQ-REVIEW-04: `gsd-code-review-fix` MUST read REVIEW.md and fix Critical + Warning findings by default +- REQ-REVIEW-04: `gsd-code-review --fix` MUST read REVIEW.md and fix Critical + Warning findings by default - REQ-REVIEW-05: Each fix MUST be committed atomically with a descriptive message - REQ-REVIEW-06: `--auto` flag MUST enable fix + re-review iteration loop, capped at 3 iterations - REQ-REVIEW-07: Feature MUST be gated by `workflow.code_review` config flag diff --git a/docs/INVENTORY.md b/docs/INVENTORY.md index 980c5c370..166c93ad3 100644 --- a/docs/INVENTORY.md +++ b/docs/INVENTORY.md @@ -40,7 +40,7 @@ Full roster at `agents/gsd-*.md`. The "Primary doc" column flags whether [`docs/ | gsd-pattern-mapper | Maps new files to closest existing analogs; writes PATTERNS.md for the planner. | `/gsd-plan-phase` (between research and planning) | advanced stub | | gsd-debug-session-manager | Runs the full `/gsd-debug` checkpoint-and-continuation loop in isolated context so main stays lean. | `/gsd-debug` | advanced stub | | gsd-code-reviewer | Reviews source files for bugs, security issues, and code-quality problems; produces REVIEW.md. | `/gsd-code-review` | advanced stub | -| gsd-code-fixer | Applies fixes to REVIEW.md findings with atomic per-fix commits; produces REVIEW-FIX.md. | `/gsd-code-review-fix` | advanced stub | +| gsd-code-fixer | Applies fixes to REVIEW.md findings with atomic per-fix commits; produces REVIEW-FIX.md. | `/gsd-code-review --fix` | advanced stub | | gsd-ai-researcher | Researches a chosen AI framework's official docs into implementation-ready guidance (AI-SPEC.md §3–§4b). | `/gsd-ai-integration-phase` | advanced stub | | gsd-domain-researcher | Surfaces domain-expert evaluation criteria and failure modes for an AI system (AI-SPEC.md §1b). | `/gsd-ai-integration-phase` | advanced stub | | gsd-eval-planner | Designs structured evaluation strategy for an AI phase (AI-SPEC.md §5–§7). | `/gsd-ai-integration-phase` | advanced stub | @@ -179,7 +179,7 @@ Full roster at `get-shit-done/workflows/*.md`. Workflows are thin orchestrators | `autonomous.md` | Drive milestone phases autonomously — all remaining, a range, or a single phase. | `/gsd-autonomous` | | `check-todos.md` | List pending todos, allow selection, load context, and route to the appropriate action. | `/gsd-capture --list` | | `cleanup.md` | Archive accumulated phase directories from completed milestones. | `/gsd-cleanup` | -| `code-review-fix.md` | Auto-fix issues from REVIEW.md via gsd-code-fixer with per-fix atomic commits. | `/gsd-code-review-fix` | +| `code-review-fix.md` | Auto-fix issues from REVIEW.md via gsd-code-fixer with per-fix atomic commits. | `/gsd-code-review --fix` | | `code-review.md` | Review phase source changes via gsd-code-reviewer; produces REVIEW.md. | `/gsd-code-review` | | `complete-milestone.md` | Mark a shipped version as complete — MILESTONES.md entry, PROJECT.md evolution, tag. | `/gsd-complete-milestone` | | `diagnose-issues.md` | Orchestrate parallel debug agents to investigate UAT gaps and find root causes. | `/gsd-verify-work` (auto-diagnosis) | diff --git a/docs/USER-GUIDE.md b/docs/USER-GUIDE.md index 23e337de2..e79499131 100644 --- a/docs/USER-GUIDE.md +++ b/docs/USER-GUIDE.md @@ -748,8 +748,8 @@ After executing a phase, run a structured code review before UAT: The reviewer scopes files automatically using SUMMARY.md (preferred) or git diff fallback. Findings are classified as Critical, Warning, or Info in `{phase}-REVIEW.md`. ```bash -/gsd-code-review-fix 3 # Fix Critical + Warning findings atomically -/gsd-code-review-fix 3 --auto # Fix and re-review until clean (max 3 iterations) +/gsd-code-review 3 --fix # Fix Critical + Warning findings atomically +/gsd-code-review 3 --fix --auto # Fix and re-review until clean (max 3 iterations) ``` ### Autonomous Audit-to-Fix @@ -766,7 +766,7 @@ To run an audit and fix all auto-fixable issues in one pass: The review step slots in after execution and before UAT: ``` -/gsd-execute-phase N -> /gsd-code-review N -> /gsd-code-review-fix N -> /gsd-verify-work N +/gsd-execute-phase N -> /gsd-code-review N -> /gsd-code-review N --fix -> /gsd-verify-work N ``` --- diff --git a/get-shit-done/workflows/audit-milestone.md b/get-shit-done/workflows/audit-milestone.md index 7df375af7..715c4c21d 100644 --- a/get-shit-done/workflows/audit-milestone.md +++ b/get-shit-done/workflows/audit-milestone.md @@ -274,11 +274,22 @@ Phases needing validation: run `/gsd-validate-phase {N}` for each flagged phase. ## ▶ Next Up — [${PROJECT_CODE}] ${PROJECT_TITLE} -**Plan gap closure** — create phases to complete milestone +**Close the gaps inline** — gap planning happens as part of this audit's +output (see the Unsatisfied Requirements, Cross-Phase Issues, Broken Flows, +and Nyquist Coverage sections above). Insert one closure phase per gap (or +per group of related gaps) using the standard phase chain: /clear then: -/gsd-plan-milestone-gaps +/gsd-phase --insert "Close gap: — " +/gsd-discuss-phase +/gsd-plan-phase +/gsd-execute-phase + +For Nyquist-coverage gaps flagged in the table above, prefer running +`/gsd-validate-phase ` for each flagged phase (and `/gsd-secure-phase +` if SECURITY.md was flagged) before inserting a new closure phase — +they may close the gap retroactively without a new phase. ─────────────────────────────────────────────────────────────── @@ -316,11 +327,15 @@ All requirements met. No critical blockers. Accumulated tech debt needs review. /gsd-complete-milestone {version} -**B. Plan cleanup phase** — address debt before completing +**B. Plan a cleanup phase** — address the debt above before completing. +Insert a closure phase using the standard chain: /clear then: -/gsd-plan-milestone-gaps +/gsd-phase --insert "Address tech debt: " +/gsd-discuss-phase +/gsd-plan-phase +/gsd-execute-phase ─────────────────────────────────────────────────────────────── diff --git a/get-shit-done/workflows/code-review-fix.md b/get-shit-done/workflows/code-review-fix.md index 1cc5aa112..cbd7acd4b 100644 --- a/get-shit-done/workflows/code-review-fix.md +++ b/get-shit-done/workflows/code-review-fix.md @@ -223,7 +223,7 @@ Check if FIX_REPORT_PATH exists: Either way: ``` Some fix commits may already exist in git history — check git log for fix(${PADDED_PHASE}) commits. -You can retry with /gsd-code-review-fix ${PHASE_ARG}. +You can retry with /gsd-code-review ${PHASE_ARG} --fix. ``` Exit workflow (skip auto loop). @@ -398,7 +398,7 @@ if [ ! -f "${FIX_REPORT_PATH}" ]; then echo "The fixer agent may have failed before completing." echo "Check git log for any fix(${PADDED_PHASE}) commits." echo "" - echo "Retry: /gsd-code-review-fix ${PHASE_ARG}" + echo "Retry: /gsd-code-review ${PHASE_ARG} --fix" echo "" echo "═══════════════════════════════════════════════════════════════" exit 1 diff --git a/get-shit-done/workflows/code-review.md b/get-shit-done/workflows/code-review.md index 767f4c23d..5059a6bc1 100644 --- a/get-shit-done/workflows/code-review.md +++ b/get-shit-done/workflows/code-review.md @@ -471,7 +471,7 @@ If total findings > 0: Full report: ${REVIEW_PATH} Next steps: - /gsd-code-review-fix ${PHASE_NUMBER} — Auto-fix issues + /gsd-code-review ${PHASE_NUMBER} --fix — Auto-fix issues cat ${REVIEW_PATH} — View full report ``` diff --git a/get-shit-done/workflows/execute-phase.md b/get-shit-done/workflows/execute-phase.md index 737f8f8d0..5e00b5bc1 100644 --- a/get-shit-done/workflows/execute-phase.md +++ b/get-shit-done/workflows/execute-phase.md @@ -1138,7 +1138,7 @@ REVIEW_STATUS=$(sed -n '/^---$/,/^---$/p' "$REVIEW_FILE" | grep "^status:" | hea If REVIEW_STATUS is not "clean" and not "skipped" and not empty, display: ``` Code review found issues. Consider running: -/gsd-code-review-fix ${PHASE_NUMBER} +/gsd-code-review ${PHASE_NUMBER} --fix ``` **Error handling:** If the Skill invocation fails or throws, catch the error, display "Code review encountered an error (non-blocking): {error}" and proceed to next step. Review failures must never block execution. diff --git a/tests/bug-3029-3034-stale-command-routes.test.cjs b/tests/bug-3029-3034-stale-command-routes.test.cjs new file mode 100644 index 000000000..033249570 --- /dev/null +++ b/tests/bug-3029-3034-stale-command-routes.test.cjs @@ -0,0 +1,259 @@ +/** + * Bugs #3029 + #3034: stale slash-command references in shipped surfaces. + * + * Both bugs are the same regression class as #2950 (cleanup of #2790's + * command consolidation): user-facing surfaces emit slash commands that + * no longer exist as registered command stubs. + * + * - #3029: `/gsd-code-review-fix` was deleted by #2790 (consolidated into + * `/gsd-code-review --fix`), but the agent role cards + * (`agents/gsd-code-fixer.md`), several workflow offer blocks + * (`code-review.md`, `execute-phase.md`), and the doc surfaces + * (`USER-GUIDE.md`, `INVENTORY.md`, `AGENTS.md`, `FEATURES.md`, + * `CONFIGURATION.md`) still reference the deleted command. Users hit + * `Unknown command` when they follow the orchestrator's offer. + * + * - #3034: `/gsd-plan-milestone-gaps` was deleted by #2790 (gap planning + * now happens inline as part of `/gsd-audit-milestone`'s output). + * `audit-milestone.md` blocks (lines 281, 323) and the + * `gsd-complete-milestone` skill (lines 46, 57) still emit it. + * + * Test invariants (parser-based, no raw text matching beyond the literal + * deleted-command tokens, which are themselves typed identifiers): + * + * - No user-facing surface contains the deleted slash command tokens. + * - The replacement form is present on each fixed surface. + * - bug-2950-stale-command-refs's existing assertions are not + * regressed. + * + * Internal mentions are allowed: + * - `code-review-fix.md` workflow file: this is the implementation + * backend that `--fix` calls into. Internal references to the + * workflow basename (e.g. "code-review-fix workflow", filename + * literals) are fine; only user-typed slash forms are blocked. + * - Release notes (`docs/RELEASE-*.md`): historical record, immutable. + */ + +// allow-test-rule: structural-IR parser for stale-command scrubs. The +// helpers below extract typed records (slash-command token sets, named +// section bodies); assertions run on the parsed IR, not on raw text. The +// .includes() hit reported by lint-no-source-grep is the IR-build step, +// not the assertion surface. + +'use strict'; + +const { describe, test } = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const path = require('node:path'); + +const ROOT = path.join(__dirname, '..'); + +function read(rel) { + return fs.readFileSync(path.join(ROOT, rel), 'utf-8'); +} + +/** + * Extract the set of slash-command tokens emitted by a markdown surface. + * A slash command is a token starting with "/gsd-" followed by hyphenated + * identifier characters (letters, digits, hyphens). Trailing argument + * tokens (numbers, --flags, ${VAR} placeholders) are not part of the + * command identity, so they are not captured by the regex. + * + * Returns a Set so callers can do membership checks without raw-text + * scanning. + */ +function extractSlashCommandTokens(content) { + const re = /\/gsd-[a-z0-9][a-z0-9-]*/g; + return new Set(content.match(re) || []); +} + +/** + * Locate the body of an `` block in a workflow file (or, for + * complete-milestone, the `gaps_found` pre-flight code block). Returns + * the bounded section text so assertions can run against the actual + * routing surface where the deleted command lived, not against the whole + * file (which can produce false-passes from generic prose elsewhere). + * + * Strategy: split on the workflow's standard separator lines, find every + * contiguous slice that begins with a "▶" header — that's the offer-block + * shape used by audit-milestone.md. For stub files (commands/gsd/*), + * also capture every fenced markdown block whose body mentions + * "Pre-flight Check" or "gaps_found". + */ +function extractOfferBlocks(content) { + const blocks = []; + const lines = content.split('\n'); + for (let i = 0; i < lines.length; i++) { + if (/^##?\s*▶\s+/.test(lines[i])) { + const start = i; + let end = lines.length; + // The block ends at the next ▶ header, the closing + // tag, or the next top-level horizontal-rule separator. + for (let j = i + 1; j < lines.length; j++) { + if ( + /^##?\s*▶\s+/.test(lines[j]) || + /<\/offer_next>/.test(lines[j]) || + /^---$/.test(lines[j]) + ) { + end = j; + break; + } + } + blocks.push(lines.slice(start, end).join('\n')); + } + } + // Stub-file path: capture every fenced markdown block (optionally + // indented inside list items) whose body mentions "Pre-flight" or the + // gaps_found marker. Used by commands/gsd/complete-milestone.md, which + // embeds the routing template inside an indented fenced block rather + // than an tag. + const fenceRe = /^[ \t]*```markdown\s*$([\s\S]*?)^[ \t]*```\s*$/gm; + let m; + while ((m = fenceRe.exec(content)) !== null) { + if (/Pre-flight|gaps?[ -]found|gaps_found/i.test(m[1])) { + blocks.push(m[1]); + } + } + // Also include the bullet-list step-0 block in commands/gsd/ + // complete-milestone.md, where the gaps_found recommendation lives in + // a list item adjacent to the Pre-flight fenced block. Capture every + // numbered-list step whose body mentions gaps_found. + const numberedStepRe = /^\d+\.\s+\*\*[^*]+\*\*[\s\S]*?(?=^\d+\.\s+\*\*|\Z)/gm; + let s; + while ((s = numberedStepRe.exec(content)) !== null) { + if (/gaps?[ -]found|gaps_found/i.test(s[0])) { + blocks.push(s[0]); + } + } + return blocks; +} + +// ─── #3029: /gsd-code-review-fix scrub ────────────────────────────────────── + +const CRF_DELETED = '/gsd-code-review-fix'; +const CRF_REPLACEMENT = '/gsd-code-review --fix'; + +// Surfaces a user can encounter as routing/dispatch text. Each must not +// emit the deleted slash-command form. +const CRF_USER_FACING_SURFACES = [ + 'agents/gsd-code-fixer.md', + 'get-shit-done/workflows/code-review.md', + 'get-shit-done/workflows/execute-phase.md', + 'docs/INVENTORY.md', + 'docs/CONFIGURATION.md', + 'docs/USER-GUIDE.md', + 'docs/AGENTS.md', + 'docs/FEATURES.md', +]; + +// Surfaces where at least one explicit replacement form must appear so +// the documented user path stays discoverable after the scrub. +const CRF_REPLACEMENT_SURFACES = [ + 'docs/USER-GUIDE.md', + 'docs/FEATURES.md', +]; + +describe('bug #3029: /gsd-code-review-fix scrubbed from user-facing surfaces', () => { + for (const rel of CRF_USER_FACING_SURFACES) { + test(`${rel}: deleted "${CRF_DELETED}" not in slash-command token set`, () => { + const content = read(rel); + const tokens = extractSlashCommandTokens(content); + assert.equal( + tokens.has(CRF_DELETED), + false, + `${rel}: parsed slash-command token set still contains "${CRF_DELETED}" — replace with "${CRF_REPLACEMENT}"` + ); + }); + } + + for (const rel of CRF_REPLACEMENT_SURFACES) { + test(`${rel}: replacement "${CRF_REPLACEMENT}" tokens present`, () => { + const content = read(rel); + // The replacement is a multi-token form (`/gsd-code-review` + the + // `--fix` flag). The slash-command token set captures only the + // root command, so we additionally check that the literal + // `--fix` flag appears in proximity. "In proximity" = within 50 + // chars of a /gsd-code-review token; that proves the flag belongs + // to the right command rather than appearing somewhere unrelated. + const tokens = extractSlashCommandTokens(content); + assert.equal( + tokens.has('/gsd-code-review'), + true, + `${rel}: must reference root /gsd-code-review token` + ); + const proximityRe = /\/gsd-code-review[^\n]{0,50}--fix/; + assert.ok( + proximityRe.test(content), + `${rel}: must document the "/gsd-code-review … --fix" form (root token + flag within 50 chars)` + ); + }); + } +}); + +// ─── #3034: /gsd-plan-milestone-gaps scrub ────────────────────────────────── + +const PMG_DELETED = '/gsd-plan-milestone-gaps'; +// The closure path is the user-facing replacement for gap planning. We +// don't pin the exact prose — the gsd-ns-project SKILL.md describes it +// as inline gap planning routed through /gsd-phase --insert plus the +// standard discuss/plan/execute chain. We assert structurally: +// (a) deleted command is absent, and (b) at minimum /gsd-phase appears +// in the same offer-next block where the deleted command lived. +const PMG_FIX_SURFACES = [ + 'get-shit-done/workflows/audit-milestone.md', + 'commands/gsd/complete-milestone.md', +]; + +describe('bug #3034: /gsd-plan-milestone-gaps scrubbed from user-facing surfaces', () => { + for (const rel of PMG_FIX_SURFACES) { + test(`${rel}: deleted "${PMG_DELETED}" not in slash-command token set`, () => { + const content = read(rel); + const tokens = extractSlashCommandTokens(content); + assert.equal( + tokens.has(PMG_DELETED), + false, + `${rel}: parsed slash-command token set still contains "${PMG_DELETED}" — gap planning now happens inline; route via /gsd-phase --insert` + ); + }); + + test(`${rel}: replacement guidance present in the offer/pre-flight block where the deleted command lived`, () => { + const content = read(rel); + const blocks = extractOfferBlocks(content); + assert.ok( + blocks.length > 0, + `${rel}: parser found no / pre-flight blocks — the structural shape of this file may have changed` + ); + // For every block that previously hosted the deleted command, the + // replacement guidance must now appear in that same block. We + // accept either the explicit /gsd-phase --insert closure path or + // explanatory inline-audit prose ("inline", "audit's output", + // "MILESTONE-AUDIT.md"). + const inlineProseRe = /inline|audit.*output|gap.*planning.*now|MILESTONE-AUDIT\.md/i; + const blocksWithReplacement = blocks.filter( + (b) => b.includes('/gsd-phase --insert') || inlineProseRe.test(b) + ); + assert.ok( + blocksWithReplacement.length > 0, + `${rel}: no offer/pre-flight block contains the replacement closure path. ` + + `Expected at least one block to mention "/gsd-phase --insert" or inline-audit prose ` + + `(scoped check, not file-wide — see CR #3038)` + ); + }); + } +}); + +// ─── Cross-issue invariant: gsd-ns-project still documents the deletion ───── + +describe('cross-check: gsd-ns-project keeps the deletion note', () => { + test('commands/gsd/ns-project.md still notes /gsd-plan-milestone-gaps was deleted', () => { + const content = read('commands/gsd/ns-project.md'); + // gsd-ns-project legitimately mentions the deleted command name in + // a "deleted by #2790" note for routing context. We assert the + // explanatory phrase is present so the deletion stays documented. + assert.ok( + /gsd-plan-milestone-gaps.*deleted by #2790|deleted by #2790.*gsd-plan-milestone-gaps/s.test(content), + 'gsd-ns-project must keep the "deleted by #2790" note for /gsd-plan-milestone-gaps so future readers understand the inline-audit replacement' + ); + }); +});