refactor(#1852): lazy-split plan-phase.md into steps/ (#1934)

* refactor(#1852): lazy-split plan-phase.md into steps/

Extract 3 self-contained, rarely-hit sections into gsd-core/workflows/plan-phase/steps/
via lazy 'Read and execute' pointers (mirrors execute-phase/steps/, ADR-1610 progressive
disclosure — no eager @-import): closed-phase-gate (1.5), prd-express-path (3.5),
windows-troubleshooting. Byte-invariant: plan-phase.md 94459 -> 89775 (-4684), each step
< 32 KiB anchor, resolved instructions unchanged.

Scope note: only 3 sections were extractable. plan-phase.md is guarded by a dense net of
content-presence tests (plan-bounce/enh-3209/phase6-planning-capabilities assert specific
sections/flags inline) that block extracting the larger blocks without also refactoring
those tests — durable low-80s headroom is deferred pending maintainer re-scope (issue #1852).

Cascade: size:baseline regen, 16 golden-install-parity fixtures regen, INVENTORY in sync.
Full plan-phase test surface green (4129/4130, 0 fail); lint:ci exit 0.

* docs(changeset): Changed fragment for #1934 (plan-phase lazy-split)

* docs(changeset): mark #1934 fragment docs-exempt (internal workflow refactor)

* fix(#1852): add gsd_run launcher preamble to prd-express-path step

The extracted prd-express-path.md calls gsd_run but the canonical launcher
preamble lived in the parent plan-phase.md — runtime-launcher-parity (#373)
walks workflows/ recursively and requires every .md using gsd_run to carry
exactly one preamble + the $HOME/.claude fallback arm (same as the existing
execute-phase/steps/ files). Injected via scripts/sync-runtime-launcher.cjs;
golden fixtures + size baseline regenerated. plan-phase.md unchanged (89775).

---------

Co-authored-by: Tom Boucher <trekkie@nomorestars.com>
This commit is contained in:
Rezolv
2026-07-02 23:30:19 -04:00
committed by GitHub
parent 5b5dbe059b
commit 9d7c046eae
22 changed files with 242 additions and 175 deletions

View File

@@ -91,46 +91,7 @@ Parse JSON for: `researcher_model`, `planner_model`, `checker_model`, `research_
## 1.5. Closed-Phase Gate (#3569)
The init JSON includes `phase_status` — one of `Pending | Planned | In Progress | Executed | Complete | Needs Review`. `Complete` means the phase has all summaries AND a `VERIFICATION.md` with `status: passed`. Replanning a closed phase silently rewrites plan docs that no longer match the shipped code, so the workflow must hard-stop here unless the operator explicitly overrides.
Parse `phase_status` from the init JSON, then:
```bash
FORCE_REPLAN=false
if [[ "$ARGUMENTS" =~ (^|[[:space:]])--force([[:space:]]|$) ]]; then
FORCE_REPLAN=true
fi
if [ "${phase_status}" = "Complete" ]; then
if [[ "$ARGUMENTS" =~ (^|[[:space:]])--reviews([[:space:]]|$) ]]; then
# --reviews on a closed phase is never legitimate — concerns belong in a
# new phase or issue against the closed phase's commits.
cat <<EOF >&2
Phase ${phase_number} (${phase_name}) is already CLOSED (VERIFICATION status: passed).
/gsd:plan-phase --reviews cannot replan a closed phase. If the review surfaced
real concerns, open a follow-up phase or file an issue against the closed
phase's commits. There is no --force override for --reviews on a closed phase.
EOF
exit 1
fi
if [ "$FORCE_REPLAN" != "true" ]; then
cat <<EOF >&2
Phase ${phase_number} (${phase_name}) is already CLOSED (VERIFICATION status: passed).
Replanning a closed phase will overwrite plan docs that no longer match the
shipped code. If you intentionally want to replan over closed work, re-run
with: /gsd:plan-phase ${phase_number} --force
Otherwise, to view what shipped, see: ${verification_path}
EOF
exit 1
fi
# FORCE_REPLAN=true: continue, but emit a banner so the operator sees the
# decision in the transcript and in any committed plan docs.
echo "WARNING: Replanning CLOSED phase ${phase_number} under --force. Verify the closeout was wrong before committing new plan docs." >&2
fi
```
The gate fires only on `Complete`. `Executed` and `Needs Review` are not gated — those states mean planning was finished but verification did not pass, and replanning is a legitimate next step.
Read and execute `gsd-core/workflows/plan-phase/steps/closed-phase-gate.md` — it parses `phase_status` from the init JSON, sets `FORCE_REPLAN` from `$ARGUMENTS`, and hard-stops replanning a `Complete` phase: `--reviews` on a closed phase is never overridable (exit 1), and replanning otherwise requires `--force` (else exit 1, pointing at `${verification_path}`); under `--force` it continues but emits a WARNING banner. Only `Complete` is gated — `Executed` / `Needs Review` are legitimate replans.
## 2. Parse and Normalize Arguments
@@ -249,103 +210,7 @@ MVP_MODE=$(gsd_run query phase.mvp-mode "${PHASE}" $MVP_FLAG_ARG --pick active)
**If `--prd <filepath>` provided:**
1. Read the PRD file:
```bash
PRD_CONTENT=$(cat "$PRD_FILE" 2>/dev/null)
if [ -z "$PRD_CONTENT" ]; then
echo "Error: PRD file not found: $PRD_FILE"
exit 1
fi
```
2. Display banner:
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
GSD ► PRD EXPRESS PATH
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Using PRD: {PRD_FILE}
Generating CONTEXT.md from requirements...
```
3. Parse the PRD content and generate CONTEXT.md. The orchestrator should:
- Extract all requirements, user stories, acceptance criteria, and constraints from the PRD
- Map each to a locked decision (everything in the PRD is treated as a locked decision)
- Identify any areas the PRD doesn't cover and mark as "Claude's Discretion"
- **Extract canonical refs** from ROADMAP.md for this phase, plus any specs/ADRs referenced in the PRD — expand to full file paths (MANDATORY)
- Create CONTEXT.md in the phase directory
4. Write CONTEXT.md:
```markdown
# Phase [X]: [Name] - Context
**Gathered:** [date]
**Status:** Ready for planning
**Source:** PRD Express Path ({PRD_FILE})
<domain>
## Phase Boundary
[Extracted from PRD — what this phase delivers]
</domain>
<decisions>
## Implementation Decisions
{For each requirement/story/criterion in the PRD:}
### [Category derived from content]
- [Requirement as locked decision]
### Claude's Discretion
[Areas not covered by PRD — implementation details, technical choices]
</decisions>
<canonical_refs>
## Canonical References
**Downstream agents MUST read these before planning or implementing.**
[MANDATORY. Extract from ROADMAP.md and any docs referenced in the PRD.
Use full relative paths. Group by topic area.]
### [Topic area]
- `path/to/spec-or-adr.md` — [What it decides/defines]
[If no external specs: "No external specs — requirements fully captured in decisions above"]
</canonical_refs>
<specifics>
## Specific Ideas
[Any specific references, examples, or concrete requirements from PRD]
</specifics>
<deferred>
## Deferred Ideas
[Items in PRD explicitly marked as future/v2/out-of-scope]
[If none: "None — PRD covers phase scope"]
</deferred>
---
*Phase: XX-name*
*Context gathered: [date] via PRD Express Path*
```
5. Commit:
```bash
gsd_run query commit "docs(${padded_phase}): generate context from PRD" --files "${phase_dir}/${padded_phase}-CONTEXT.md"
```
6. Set `context_content` to the generated CONTEXT.md content and continue to step 5 (Handle Research).
**Effect:** This completely bypasses step 4 (Load CONTEXT.md) since we just created it. The rest of the workflow (research, planning, verification) proceeds normally with the PRD-derived context.
Read and execute `gsd-core/workflows/plan-phase/steps/prd-express-path.md` — it reads the PRD (`$PRD_FILE`), generates `CONTEXT.md` (every PRD requirement/story/criterion → locked decision, uncovered areas → "Claude's Discretion", canonical refs extracted from ROADMAP.md + PRD-referenced specs), commits it, sets `context_content`, and bypasses step 4 (Load CONTEXT.md). The rest of the workflow proceeds normally with the PRD-derived context.
## 3.6. Handle ADR Ingest Express Path
@@ -1730,27 +1595,7 @@ Verification: {Passed | Passed with override | Skipped}
</offer_next>
<windows_troubleshooting>
**Windows users:** If plan-phase freezes during agent spawning (common on Windows due to
stdio deadlocks with MCP servers — see Claude Code issue anthropics/claude-code#28126):
1. **Force-kill:** Close the terminal (Ctrl+C may not work)
2. **Clean up orphaned processes:**
```powershell
# Kill orphaned node processes from stale MCP servers
Get-Process node -ErrorAction SilentlyContinue | Where-Object {$_.StartTime -lt (Get-Date).AddHours(-1)} | Stop-Process -Force
```
3. **Clean up stale task directories:**
```powershell
# Remove stale subagent task dirs (Claude Code never cleans these on crash)
Remove-Item -Recurse -Force "$env:USERPROFILE\.claude\tasks\*" -ErrorAction SilentlyContinue
```
4. **Reduce MCP server count:** Temporarily disable non-essential MCP servers in settings.json
5. **Retry:** Restart Claude Code and run `/gsd:plan-phase` again
If freezes persist, try `--skip-research` to reduce the agent chain from 3 to 2 agents:
```
/gsd:plan-phase N --skip-research
```
Read `gsd-core/workflows/plan-phase/steps/windows-troubleshooting.md` if plan-phase freezes on Windows during agent spawning (stdio deadlocks with MCP servers, anthropics/claude-code#28126) — it covers force-kill, orphaned-node cleanup, stale task-dir cleanup, reducing the MCP server count, and the `--skip-research` fallback.
</windows_troubleshooting>
<success_criteria>

View File

@@ -0,0 +1,42 @@
# Closed-Phase Gate (#3569)
The init JSON includes `phase_status` — one of `Pending | Planned | In Progress | Executed | Complete | Needs Review`. `Complete` means the phase has all summaries AND a `VERIFICATION.md` with `status: passed`. Replanning a closed phase silently rewrites plan docs that no longer match the shipped code, so the workflow must hard-stop here unless the operator explicitly overrides.
Parse `phase_status` from the init JSON, then:
```bash
FORCE_REPLAN=false
if [[ "$ARGUMENTS" =~ (^|[[:space:]])--force([[:space:]]|$) ]]; then
FORCE_REPLAN=true
fi
if [ "${phase_status}" = "Complete" ]; then
if [[ "$ARGUMENTS" =~ (^|[[:space:]])--reviews([[:space:]]|$) ]]; then
# --reviews on a closed phase is never legitimate — concerns belong in a
# new phase or issue against the closed phase's commits.
cat <<EOF >&2
Phase ${phase_number} (${phase_name}) is already CLOSED (VERIFICATION status: passed).
/gsd:plan-phase --reviews cannot replan a closed phase. If the review surfaced
real concerns, open a follow-up phase or file an issue against the closed
phase's commits. There is no --force override for --reviews on a closed phase.
EOF
exit 1
fi
if [ "$FORCE_REPLAN" != "true" ]; then
cat <<EOF >&2
Phase ${phase_number} (${phase_name}) is already CLOSED (VERIFICATION status: passed).
Replanning a closed phase will overwrite plan docs that no longer match the
shipped code. If you intentionally want to replan over closed work, re-run
with: /gsd:plan-phase ${phase_number} --force
Otherwise, to view what shipped, see: ${verification_path}
EOF
exit 1
fi
# FORCE_REPLAN=true: continue, but emit a banner so the operator sees the
# decision in the transcript and in any committed plan docs.
echo "WARNING: Replanning CLOSED phase ${phase_number} under --force. Verify the closeout was wrong before committing new plan docs." >&2
fi
```
The gate fires only on `Complete`. `Executed` and `Needs Review` are not gated — those states mean planning was finished but verification did not pass, and replanning is a legitimate next step.

View File

@@ -0,0 +1,102 @@
# PRD Express Path — generate CONTEXT.md from a PRD
Runs when `--prd <filepath>` is provided (§3.5 of `plan-phase.md`).
1. Read the PRD file:
```bash
PRD_CONTENT=$(cat "$PRD_FILE" 2>/dev/null)
if [ -z "$PRD_CONTENT" ]; then
echo "Error: PRD file not found: $PRD_FILE"
exit 1
fi
```
2. Display banner:
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
GSD ► PRD EXPRESS PATH
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Using PRD: {PRD_FILE}
Generating CONTEXT.md from requirements...
```
3. Parse the PRD content and generate CONTEXT.md. The orchestrator should:
- Extract all requirements, user stories, acceptance criteria, and constraints from the PRD
- Map each to a locked decision (everything in the PRD is treated as a locked decision)
- Identify any areas the PRD doesn't cover and mark as "Claude's Discretion"
- **Extract canonical refs** from ROADMAP.md for this phase, plus any specs/ADRs referenced in the PRD — expand to full file paths (MANDATORY)
- Create CONTEXT.md in the phase directory
4. Write CONTEXT.md:
```markdown
# Phase [X]: [Name] - Context
**Gathered:** [date]
**Status:** Ready for planning
**Source:** PRD Express Path ({PRD_FILE})
<domain>
## Phase Boundary
[Extracted from PRD — what this phase delivers]
</domain>
<decisions>
## Implementation Decisions
{For each requirement/story/criterion in the PRD:}
### [Category derived from content]
- [Requirement as locked decision]
### Claude's Discretion
[Areas not covered by PRD — implementation details, technical choices]
</decisions>
<canonical_refs>
## Canonical References
**Downstream agents MUST read these before planning or implementing.**
[MANDATORY. Extract from ROADMAP.md and any docs referenced in the PRD.
Use full relative paths. Group by topic area.]
### [Topic area]
- `path/to/spec-or-adr.md` — [What it decides/defines]
[If no external specs: "No external specs — requirements fully captured in decisions above"]
</canonical_refs>
<specifics>
## Specific Ideas
[Any specific references, examples, or concrete requirements from PRD]
</specifics>
<deferred>
## Deferred Ideas
[Items in PRD explicitly marked as future/v2/out-of-scope]
[If none: "None — PRD covers phase scope"]
</deferred>
---
*Phase: XX-name*
*Context gathered: [date] via PRD Express Path*
```
5. Commit:
```bash
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "$HOME/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="$HOME/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
gsd_run query commit "docs(${padded_phase}): generate context from PRD" --files "${phase_dir}/${padded_phase}-CONTEXT.md"
```
6. Set `context_content` to the generated CONTEXT.md content and continue to step 5 (Handle Research).
**Effect:** This completely bypasses step 4 (Load CONTEXT.md) since we just created it. The rest of the workflow (research, planning, verification) proceeds normally with the PRD-derived context.

View File

@@ -0,0 +1,23 @@
# Windows Troubleshooting
**Windows users:** If plan-phase freezes during agent spawning (common on Windows due to
stdio deadlocks with MCP servers — see Claude Code issue anthropics/claude-code#28126):
1. **Force-kill:** Close the terminal (Ctrl+C may not work)
2. **Clean up orphaned processes:**
```powershell
# Kill orphaned node processes from stale MCP servers
Get-Process node -ErrorAction SilentlyContinue | Where-Object {$_.StartTime -lt (Get-Date).AddHours(-1)} | Stop-Process -Force
```
3. **Clean up stale task directories:**
```powershell
# Remove stale subagent task dirs (Claude Code never cleans these on crash)
Remove-Item -Recurse -Force "$env:USERPROFILE\.claude\tasks\*" -ErrorAction SilentlyContinue
```
4. **Reduce MCP server count:** Temporarily disable non-essential MCP servers in settings.json
5. **Retry:** Restart Claude Code and run `/gsd:plan-phase` again
If freezes persist, try `--skip-research` to reduce the agent chain from 3 to 2 agents:
```
/gsd:plan-phase N --skip-research
```