Files
msd-core/get-shit-done/workflows/plan-phase.md
Tom Boucher 2d32ad82be fix(plan-phase): remove agent: directive that caused OpenCode subagent dispatch (#3156) (#3206)
* feat(roadmap): parse **Mode:** field on phase sections

Adds a 'mode' field to roadmap.get-phase and roadmap.analyze outputs.
Recognizes '**Mode:** mvp' lines in phase sections; lowercased + trimmed.
Forward-compat: unrecognized values preserved verbatim, no enum check.

Foundation for --mvp flag in plan-phase (PRD: vertical-mvp-slice).

* feat(plan-phase): parse --mvp flag and resolve MVP_MODE

Resolution order: CLI flag → ROADMAP **Mode:** field → workflow.mvp_mode
config → false. Walking Skeleton gate fires for new-project Phase 1.
Wires MVP_MODE + WALKING_SKELETON into gsd-planner subagent prompt.

Per PRD vertical-mvp-slice Phase 1 (Q1, Q2, Q4).

* docs(planner): add vertical-slice planning reference

New reference loaded by gsd-planner when MVP_MODE=true. Defines slice
ordering, Walking Skeleton rules, and anti-patterns. Referenced from
plan-phase workflow MVP_MODE wiring.

* docs(planner): add SKELETON.md template

Template emitted by gsd-planner under WALKING_SKELETON=true. Captures
architectural decisions and out-of-scope list for new-project Phase 1.

* chore(inventory): register new planner references

Added planner-mvp-mode.md and skeleton-template.md to INVENTORY.md and
INVENTORY-MANIFEST.json. References now: 53.

* feat(gsd-planner): add MVP Mode Detection section

Mode-switched branch in the existing planner agent (per Q4: single agent).
Vertical-slice decomposition rules, Walking Skeleton handling, and
TDD-mode compatibility. Heavy guidance lives in references/planner-mvp-mode.md.

* test(plan-phase): add --mvp resolution-chain integration cases

Validates roadmap.get-phase --pick mode and confirms workflow.mvp_mode
default is unset in fresh projects.

* docs(changelog): announce --mvp vertical-slice planning (#2826)

* feat(mvp-phase): add /gsd mvp-phase slash command

Standalone command for vertical MVP planning. Frontmatter only;
heavyweight workflow at get-shit-done/workflows/mvp-phase.md follows
in next commit. Mirrors discuss-phase/edit-phase command shape.

* docs(planner): add user-story-template reference

Defines the canonical 'As a / I want to / So that' format and the
ROADMAP.md / PLAN.md emit rules. Used by mvp-phase workflow and
gsd-planner agent under MVP_MODE.

* docs(planner): add SPIDR splitting reference

Defines size signals, the five SPIDR axes (Spike/Paths/Interfaces/Data/Rules),
the interactive workflow, and anti-patterns. Per PRD Q3 decision: full
interactive flow, not lightweight check. Used by mvp-phase workflow.

* fix(mvp-phase): trim description to fit 100-char budget

* feat(mvp-phase): add mvp-phase workflow

Standalone workflow: phase validation -> user story prompts (As a / I want to /
So that) -> SPIDR splitting check -> ROADMAP write (Mode + Goal) -> delegation
to plan-phase. Per PRD Phase 2 (Q3 full SPIDR; Phase-2-A/B/C/D decisions).

Plan-phase auto-detects MVP via Phase 1's resolution chain, so no flags
are needed when delegating.

* feat(gsd-planner): emit user-story header in PLAN.md under MVP mode

Extends the MVP Mode Detection section (added in Phase 1) so the planner
sources the user story from ROADMAP **Goal:** and emits the bolded
**As a** / **I want to** / **so that** form as the first content under
the phase header in PLAN.md. References user-story-template.md.

* test(mvp-phase): integration smoke test for ROADMAP mutation

Validates roadmap.get-phase output after a workflow-spec'd ROADMAP write:
mode=mvp and goal=full user story. Catches schema drift between workflow
emit and parser expectation. Includes a long-story case (>120 chars) to
confirm SPIDR-rejected stories still parse correctly.

* chore(inventory): register mvp-phase command + 2 new references

Adds /gsd mvp-phase to commands list, mvp-phase workflow to workflows list,
and user-story-template.md + spidr-splitting.md to references. References
count: 53 -> 55.

* docs(changelog): announce /gsd mvp-phase command (#2826)

* fix(mvp-phase): add TEXT_MODE plain-text fallback for non-Claude runtimes (#2012)

* docs(executor): add MVP+TDD gate reference

Defines the runtime gate semantics for execute-phase when both
MVP_MODE and TDD_MODE are true: pre-task verification of failing-test
commit, end-of-phase review escalation from advisory to blocking,
behavior-adding task definition. Loaded conditionally by
execute-phase workflow and gsd-executor agent.

* feat(execute-phase): MVP+TDD runtime gate + blocking review

Resolves MVP_MODE in Step 1 (CLI flag -> roadmap mode -> config -> false).
Adds per-task gate that halts before behavior-adding tasks run if no
failing-test commit exists for the plan. Escalates end-of-phase TDD
review from advisory to blocking when both MVP_MODE and TDD_MODE active.

Also updates INVENTORY-MANIFEST.json to register execute-mvp-tdd.md
(added by Task 1) so manifest-sync tests pass.

Per PRD vertical-mvp-slice Phase 3a (decisions Phase-3-A, Phase-3-Split).

* feat(gsd-executor): add MVP+TDD Gate section

Mirrors the planner's MVP Mode Detection pattern from Phase 1.
Instructs halt-and-report when the runtime gate trips, references
execute-mvp-tdd.md for full semantics. No agent changes outside the
new section.

* test(execute-phase): add MVP+TDD resolution-chain integration cases

Validates roadmap.get-phase --pick mode and confirms workflow.mvp_mode
default is unset in fresh projects. Mirrors the Phase 1 plan-phase
resolution-chain integration test.

* chore(inventory): register execute-mvp-tdd reference

Bumps References count 55 -> 56. Registers execute-mvp-tdd.md.
Adds "init" to PROSE_ALLOWLIST in registry integration test so
bare `gsd-sdk query init` prose examples in plan docs don't
trigger the unregistered-handler guard (real commands are all
init.<subcommand>).

* docs(changelog): announce MVP+TDD runtime gate in execute-phase (#2826)

* docs(verifier): add verify-mvp-mode reference

Defines UAT framing under MVP mode: user-flow walk-through first,
technical checks deferred, coverage check as goal-backward narrowing
to the user story's outcome clause. Loaded conditionally by
verify-work workflow and gsd-verifier agent.

* feat(verify-work): MVP-mode UAT framing — user flow first

Resolves MVP_MODE from phase mode field. Under MVP mode, generates UAT
in three ordered sections: user-flow walk-through (derived from user
story), technical checks (deferred), coverage check (goal-backward).
Falls back to standard UAT generation when mode is null/absent.
User-story-format guard refuses to verify a mode:mvp phase with a
non-user-story goal.

Also updates docs/INVENTORY.md (56 references) and
docs/INVENTORY-MANIFEST.json to register verify-mvp-mode.md added
in Task 1.

Per PRD vertical-mvp-slice Phase 3b (decisions Phase-3-B,
Phase-3-Verify-Structure).

* feat(gsd-verifier): add MVP Mode Verification section

Narrows goal-backward verification to the user-story [outcome] clause
when phase mode is mvp. References verify-mvp-mode.md. Preserves
existing goal-backward methodology for non-MVP phases. User-story-format
guard refuses to verify a mode:mvp phase with a non-user-story goal.

* docs(changelog): announce MVP-mode UAT framing in verify-work (#2826)

* feat(new-project): add Vertical MVP vs Horizontal Layers mode prompt

Asks user at project init how to structure the project. Vertical MVP
emits **Mode:** mvp on every initial roadmap phase (per-phase mode
preserved per PRD Q1). Horizontal Layers falls back to standard
template — no behavioral change for existing flows.

Per PRD vertical-mvp-slice Phase 4 (decision Phase-4-Persistence).

* feat(progress): add MVP-mode user-flow display

When phase has **Mode:** mvp, progress renders user-flow status from
PLAN.md task names alongside standard task progress. Tasks that aren't
user-flow-shaped (technical-sounding) are filtered out of the user-flow
sub-block. Falls back to standard display when mode is null/absent.

Per PRD vertical-mvp-slice Phase 4 (decision Phase-4-Progress).

* feat(stats): add MVP phase count summary

Reads roadmap.analyze (which surfaces mode per phase from Phase 1) and
emits 'Phases: N total | M MVP | K standard' summary line. Suppressed
when MVP_COUNT == 0 to avoid clutter on non-MVP projects.

Per PRD vertical-mvp-slice Phase 4.

* feat(graphify): add MVP-mode visual differentiation

MVP-mode phases render with #22c55e fill color AND ' (MVP)' label
suffix — two-channel signaling for color-blind and grayscale renders.
Standard phases unchanged.

Per PRD vertical-mvp-slice Phase 4 (PRD Q5: distinct visual treatment).

* docs(changelog): announce Phase 4 discovery & progress (#2826)

* chore(release): bump dev to 1.50.0-canary.0 for first 1.50.0 canary

Sets the base version that .github/workflows/canary.yml derives the canary
tag from (strips suffix → base 1.50.0 → next available v1.50.0-canary.N).

This kicks off the 1.50.0 release train, opened by the MVP/TDD/UAT vertical
slice landed across PRs #2867, #2874, #2878, #2880, #2883.

* docs: add CANARY stream README + v1.50.0-canary.1 release notes

- docs/CANARY.md — explains the dev→@canary stream policy, install/rollback
  paths, and when (not) to install canary builds
- docs/RELEASE-v1.50.0-canary.1.md — release notes for the first 1.50.0
  canary cut: vertical MVP/TDD/UAT slice (#2867 + #2874 + #2878 + #2880 +
  #2883), opening the 1.50.0 train under PRD #2826
- docs/README.md — index entry + quick link for the canary stream

* fix(ci/canary): publish gate checks dev branch, not main

Four publish-step `if:` conditions in .github/workflows/canary.yml were
checking `github.ref == 'refs/heads/main'`. Those steps (Tag and push,
Publish to npm, Publish SDK to npm, Verify publish) therefore always
skipped on every workflow_dispatch invocation since canary runs from dev,
never main.

The workflow's own header comment is unambiguous: `dev → @canary`. The
gate was a copy-paste from release.yml (which correctly targets main for
the @next/@latest streams) that was never corrected for the canary stream.

This is why the 1.50.0-canary.1 publish hadn't materialized despite three
green workflow runs. With the gate corrected, the next dispatch will
actually publish.

* ci(release-sdk): make release-sdk.yml dispatchable from the dev branch

The workflow lives on main only, so the GitHub Actions "Use workflow
from" dropdown doesn't list dev — meaning dev → @dev publishes can't be
triggered from the dev branch directly. Add the file to dev so an
operator can dispatch it with branch=dev and tag=dev.

Per project release-stream policy: dev branch publishes canary (@dev).
This is the stream that needs the file most, since main never publishes
@dev itself (main does @next / @latest).

File is byte-identical to main's release-sdk.yml — straight propagation,
no behavioral change. Tracking issues #2925, #2929.

* docs(mvp): canary-prep concept cleanup — CONTEXT.md, mvp-concepts index, --prd interaction (#3176)

* chore(mvp): concept cleanup + cross-ref index for v1.50.0-canary.2 prep

- CONTEXT.md gains 7 MVP domain terms (MVP Mode, User Story, Walking
  Skeleton, Vertical Slice, Behavior-Adding Task, MVP+TDD Gate, SPIDR
  Splitting) so the project glossary matches the shipped surface.
- New get-shit-done/references/mvp-concepts.md indexes the six MVP
  reference files and concept-to-file map so agents and contributors
  can find the right canonical doc without grepping.
- plan-phase.md Walking Skeleton block now documents that --mvp and
  --prd compose orthogonally on Phase 1; no precedence needed.
- INVENTORY/INVENTORY-MANIFEST refreshed for the new reference (58 -> 59).

No behavior change. Canary-prep cleanup ahead of v1.50.0-canary.2.

Surfaced for follow-up (not in this PR):
- MVP_MODE resolution shell block duplicated across plan-phase,
  execute-phase, verify-work workflows (needs a shared workflow-include
  mechanism; structural change).
- Behavior-Adding Task predicate is prose-only; no shared utility.
- User Story regex hardcoded in verify-work; would benefit from a
  central definition consumed by the verifier and the mvp-phase command.

* chore(changeset): set PR number for mvp concept cleanup

* feat(mvp): centralize resolution surfaces + fix SDK roadmap mode parity (#3178)

Three new SDK query verbs replace the architectural duplication surfaced by
the v1.50.0-canary.2 review against dev tip 12c4e565:

  phase.mvp-mode <N> [--cli-flag]
    Single canonical precedence resolver (CLI flag -> ROADMAP **Mode:** mvp
    -> workflow.mvp_mode config -> false). Replaces 4-8 lines of bash that
    were duplicated across plan-phase.md, execute-phase.md, verify-work.md,
    and progress.md. Returns {active, source, roadmap_mode, config_mvp_mode,
    cli_flag_present}.

  task.is-behavior-adding <plan-file> | --task-content <xml>
    Behavior-Adding Task predicate (tdd="true" + <behavior> block + non-test
    source files in <files>). Replaces prose-only specification in
    references/execute-mvp-tdd.md; gsd-executor agent now invokes the verb
    instead of re-inlining the three checks. Returns {is_behavior_adding,
    checks, reason}.

  user-story.validate <text> | --story <text>
    Owns the canonical User Story regex /^As a .+, I want to .+, so that .+\.$/
    previously hardcoded in verify-work.md prose. Consumed by gsd-verifier
    (phase-goal guard) and /gsd-mvp-phase (interactive-prompt validation).
    Returns {valid, slots: {role, capability, outcome}, errors[]}.

Bug fix bundled: sdk/src/query/roadmap.ts searchPhaseInContent now extracts
the mode field from **Mode:**, restoring parity with roadmap.cjs:120-123.
Without this, roadmap.get-phase --pick mode returned null on the native
dispatch path even when the phase had **Mode:** mvp set, causing MVP_MODE
to silently fall through to the config/false branch in every consuming
workflow. The original PRs Phase 1 (#2885) shipped the CJS parser but the
SDK port omitted the field; this fix brings them back to parity.

Workflows + agents updated to call the verbs:
  - plan-phase.md, execute-phase.md, verify-work.md, progress.md call
    phase.mvp-mode (one line replaces the duplicated bash chains).
  - execute-phase.md MVP+TDD gate calls task.is-behavior-adding.
  - verify-work.md goal guard calls user-story.validate.
  - mvp-phase.md interactive prompt validates via user-story.validate.
  - gsd-executor agent references task.is-behavior-adding instead of prose.
  - gsd-verifier agent references user-story.validate instead of inlined regex.

Tests: 24 new vitest tests in sdk/src/query/mvp.test.ts cover all three
verbs + the regression. Two existing contract tests (progress, verify)
updated to assert on the new verb shape. All 60 existing MVP contract
tests pass; golden integration suite (38 + 42 tests) passes.

Closes #3177

* fix(canary.2): unblock release gates for v1.50.0-canary.2

Run 25451329660 (Release SDK Bundle on dev, 2026-05-06T17:41) failed at the
test-suite step with 3 deterministic content/structure gate failures, all
attributable to the MVP umbrella integration in #3178 and the docs sweep
in #3180.

Failure 1: /gsd-mvp-phase undocumented in workflows/help.md
  - tests/bug-2954-help-md-slash-command-stubs.test.cjs requires every
    shipped commands/gsd/<X>.md to have a /gsd-<X> mention in help.md
  - PR #3180 updated docs/COMMANDS.md but missed help.md (which the AI
    agents load in-product)
  - Fix: add a /gsd-mvp-phase entry to help.md right before /gsd-plan-phase

Failures 2 + 3: execute-phase.md (1727) and plan-phase.md (1714) over XL budget (1700)
  - PR #3178 added MVP-mode verb calls (phase.mvp-mode, task.is-behavior-adding,
    user-story.validate) to both workflow files, pushing them past 1700 lines
  - Fix: bump XL_BUDGET 1700 -> 1800 with inline comment pointing at the
    structural follow-up (extract MVP bodies to <workflow>/modes/mvp.md per
    the discuss-phase/modes/ precedent)
  - The structural extract is the right long-term fix but is bigger than
    canary unblock scope; will land in a follow-up after canary cycles

Local verification:
  $ node --test tests/bug-2954-help-md-slash-command-stubs.test.cjs                 tests/workflow-size-budget.test.cjs
  tests 111  pass 111  fail 0

After this lands, re-trigger Release SDK Bundle on dev for v1.50.0-canary.2.

* chore(changeset): set PR number for canary.2 unblock

* fix(codex): generate-claude-md writes to AGENTS.md on Codex runtime

When config.runtime === 'codex' or GSD_RUNTIME=codex, override the
output target to AGENTS.md regardless of claude_md_path, so Codex
projects no longer have GSD sections written to CLAUDE.md by mistake.

Fixes both the CJS (gsd-tools) and SDK (profile-output.ts) paths.
Explicit --output flags are still honoured in both paths.

Closes #3163

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(plan-phase): remove agent: directive that caused OpenCode subagent dispatch

On OpenCode, any command with `agent: <name>` in its frontmatter is
auto-dispatched to a subagent context where the Agent tool is unavailable.
plan-phase.md and mvp-phase.md both carried `agent: gsd-planner`, causing
them to run inside gsd-planner's subagent context with no ability to spawn
researcher/planner/checker subagents — the orchestrator fell back to inline
execution for all three phases.

Fix: remove `agent: gsd-planner` from both command files so they run in the
main agent context. Also replace the stale `Task` tool in allowed-tools with
`Agent` (the correct dispatcher tool name post-#3168 rename).

Adds a structural regression test that parses YAML frontmatter of every
commands/gsd/*.md file and asserts no command carries an `agent:` directive.

Closes #3156

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(mvp): address CodeRabbit workflow and contract findings

* fix(execute-phase): use registered state.update query command

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 21:51:38 -04:00

75 KiB
Raw Blame History

Create executable phase prompts (PLAN.md files) for a roadmap phase with integrated research and verification. Default flow: Research (if needed) -> Plan -> Verify -> Done. Orchestrates gsd-phase-researcher, gsd-planner, and gsd-plan-checker agents with a revision loop (max 3 iterations).

<required_reading> Read all files referenced by the invoking prompt's execution_context before starting.

@/.claude/get-shit-done/references/ui-brand.md @/.claude/get-shit-done/references/revision-loop.md @/.claude/get-shit-done/references/gate-prompts.md @/.claude/get-shit-done/references/agent-contracts.md @~/.claude/get-shit-done/references/gates.md </required_reading>

<available_agent_types> Valid GSD subagent types (use exact names — do not fall back to 'general-purpose'):

  • gsd-phase-researcher — Researches technical approaches for a phase
  • gsd-pattern-mapper — Analyzes codebase for existing patterns, produces PATTERNS.md
  • gsd-planner — Creates detailed plans from phase scope
  • gsd-plan-checker — Reviews plan quality before execution </available_agent_types>

0. Git Branch Invariant

Do not create, rename, or switch git branches during plan-phase. Branch identity is established at discuss-phase and is owned by the user's git workflow. A phase rename in ROADMAP.md is a plan-level change only — it does not mutate git branch names. If phase_slug in the init JSON differs from the current branch name, that is expected and correct; leave the branch unchanged.

1. Initialize

Load all context in one call (paths only to minimize orchestrator context):

INIT=$(gsd-sdk query init.plan-phase "$PHASE")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
AGENT_SKILLS_RESEARCHER=$(gsd-sdk query agent-skills gsd-phase-researcher)
AGENT_SKILLS_PLANNER=$(gsd-sdk query agent-skills gsd-planner)
AGENT_SKILLS_CHECKER=$(gsd-sdk query agent-skills gsd-plan-checker)
CONTEXT_WINDOW=$(gsd-sdk query config-get context_window 2>/dev/null || echo "200000")
TDD_MODE=$(gsd-sdk query config-get workflow.tdd_mode 2>/dev/null || echo "false")
MVP_MODE_CFG=$(gsd-sdk query config-get workflow.mvp_mode 2>/dev/null || echo "false")

When TDD_MODE is true, the planner agent is instructed to apply type: tdd to eligible tasks using heuristics from references/tdd.md. The planner's <required_reading> is extended to include @~/.claude/get-shit-done/references/tdd.md so gate enforcement rules are available during planning.

When CONTEXT_WINDOW >= 500000, the planner prompt includes the 3 most recent prior phase CONTEXT.md and SUMMARY.md files PLUS any phases explicitly listed in the current phase's Depends on: field in ROADMAP.md. Explicit dependencies always load regardless of recency (e.g., Phase 7 declaring Depends on: Phase 2 always sees Phase 2's context). Bounded recency keeps the planner's context budget focused on recent work.

Parse JSON for: researcher_model, planner_model, checker_model, research_enabled, plan_checker_enabled, nyquist_validation_enabled, commit_docs, text_mode, phase_found, phase_dir, phase_number, phase_name, phase_slug, padded_phase, has_research, has_context, has_reviews, has_plans, plan_count, planning_exists, roadmap_exists, phase_req_ids, response_language.

If response_language is set: Include response_language: {value} in all spawned subagent prompts so any user-facing output stays in the configured language.

File paths (for <files_to_read> blocks): state_path, roadmap_path, requirements_path, context_path, research_path, verification_path, uat_path, reviews_path. These are null if files don't exist.

If planning_exists is false: Error — run /gsd-new-project first.

2. Parse and Normalize Arguments

Extract from $ARGUMENTS: phase number (integer or decimal like 2.1), flags (--research, --skip-research, --research-phase <N>, --gaps, --skip-verify, --skip-ui, --prd <filepath>, --reviews, --text, --bounce, --skip-bounce, --chunked, --mvp).

--research-phase <N> — research-only mode (#3042 + #3044). When this flag is present, parse <N> as the phase number (overrides any positional phase argument), set RESEARCH_ONLY=true, and treat the rest of this workflow as a research-dispatch only — the planner spawn (step 8), plan-checker, verification, gaps, bounce, and post-planning-gaps blocks all skip on RESEARCH_ONLY. Use this for cross-phase research, doc review before committing to a planning approach, and correction-without-replanning loops. Replaces the deleted /gsd-research-phase command.

In research-only mode, two modifiers control behavior when RESEARCH.md already exists:

  • --research — force-refresh re-research without prompting. Re-spawns the researcher unconditionally and overwrites the existing RESEARCH.md. (This is the existing --research flag's standard "force re-research" semantics, reused here.)
  • --view — view-only: print existing RESEARCH.md to stdout, do not spawn the researcher. Sets VIEW_ONLY=true. Cheapest mode for the correction-without-replanning loop. If RESEARCH.md does not exist, error with a hint to drop --view.
RESEARCH_ONLY=false
VIEW_ONLY=false
if [[ "$ARGUMENTS" =~ --research-phase[[:space:]]+([0-9]+(\.[0-9]+)?) ]]; then
  RESEARCH_ONLY=true
  PHASE="${BASH_REMATCH[1]}"
fi
if $RESEARCH_ONLY && [[ "$ARGUMENTS" =~ (^|[[:space:]])--view([[:space:]]|$) ]]; then
  VIEW_ONLY=true
fi

Set TEXT_MODE=true if --text is present in $ARGUMENTS OR text_mode from init JSON is true. When TEXT_MODE is active, replace every AskUserQuestion call with a plain-text numbered list and ask the user to type their choice number. This is required for Claude Code remote sessions (/rc mode) where TUI menus don't work through the Claude App.

MVP_MODE resolution. Resolve MVP_MODE once via the centralized phase.mvp-mode query verb. Precedence (first hit wins): CLI flag → ROADMAP.md **Mode:** mvp → workflow.mvp_mode config → false. The verb is the single source of truth — do not re-implement the chain.

MVP_FLAG_ARG=""
if [[ "$ARGUMENTS" =~ (^|[[:space:]])--mvp([[:space:]]|$) ]]; then MVP_FLAG_ARG="--cli-flag"; fi

Defer the phase.mvp-mode query until PHASE is finalized (after explicit argument parsing/fallback phase detection + validation). The verb returns true|false. Full result also exposes source (cli_flag | roadmap | config | none) for diagnostics. The mode is all-or-nothing per phase (PRD decision Q1) — never selective per task.

Walking Skeleton gate. When MVP_MODE=true AND phase_number == "01" AND there are zero prior phase summaries (new project), the planner runs in Walking Skeleton mode (per PRD decision Q2 — new projects only). Detect with:

WALKING_SKELETON=false
if [ "$MVP_MODE" = "true" ] && [ "$padded_phase" = "01" ]; then
  PRIOR_SUMMARIES=$(gsd-sdk query phases.list --pick summaries_total 2>/dev/null || echo "0")
  if [ "$PRIOR_SUMMARIES" = "0" ]; then WALKING_SKELETON=true; fi
fi

When WALKING_SKELETON=true:

  • Planner is instructed to produce SKELETON.md in the phase directory alongside PLAN.md. The template lives at @~/.claude/get-shit-done/references/skeleton-template.md.
  • The plan must scaffold project + routing + one real DB read/write + one real UI interaction + dev deployment — the thinnest possible end-to-end working slice.

Interaction with --prd <filepath>. --mvp and --prd compose. The PRD express path (Step 3.5) creates CONTEXT.md from the PRD file and continues to research; the Walking Skeleton gate fires independently from the conditions above. When both are active on Phase 1 of a new project, the planner receives WALKING_SKELETON=true and PRD-derived context simultaneously — the PRD informs what the skeleton should prove. No precedence is needed; the two signals are orthogonal. See references/mvp-concepts.md for the broader interaction map.

Extract --prd <filepath> from $ARGUMENTS. If present, set PRD_FILE to the filepath.

If no phase number: Detect next unplanned phase from roadmap.

If phase_found is false: Validate phase exists in ROADMAP.md. If valid, create the directory using phase_slug and padded_phase from init:

mkdir -p ".planning/phases/${padded_phase}-${phase_slug}"

Existing artifacts from init: has_research, has_plans, plan_count.

Set CHUNKED_MODE from flag or config:

CHUNKED_CFG=$(gsd-sdk query config-get workflow.plan_chunked 2>/dev/null || echo "false")
CHUNKED_MODE=false
if [[ "$ARGUMENTS" =~ --chunked ]] || [[ "$CHUNKED_CFG" == "true" ]]; then
  CHUNKED_MODE=true
fi

2.5. Validate --reviews Prerequisite

Skip if: No --reviews flag.

If --reviews AND --gaps: Error — cannot combine --reviews with --gaps. These are conflicting modes.

If --reviews AND has_reviews is false (no REVIEWS.md in phase dir):

Error:

No REVIEWS.md found for Phase {N}. Run reviews first:

/gsd-review --phase {N}

Then re-run /gsd-plan-phase {N} --reviews

Exit workflow.

3. Validate Phase

PHASE_INFO=$(gsd-sdk query roadmap.get-phase "${PHASE}")

If found is false: Error with available phases. If found is true: Extract phase_number, phase_name, goal from JSON.

Now that PHASE is finalized, resolve MVP mode:

MVP_MODE=$(gsd-sdk query phase.mvp-mode "${PHASE}" $MVP_FLAG_ARG --pick active)

3.5. Handle PRD Express Path

Skip if: No --prd flag in arguments.

If --prd <filepath> provided:

  1. Read the PRD file:
PRD_CONTENT=$(cat "$PRD_FILE" 2>/dev/null)
if [ -z "$PRD_CONTENT" ]; then
  echo "Error: PRD file not found: $PRD_FILE"
  exit 1
fi
  1. Display banner:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 GSD ► PRD EXPRESS PATH
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Using PRD: {PRD_FILE}
Generating CONTEXT.md from requirements...
  1. 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
  2. Write CONTEXT.md:

# 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*
  1. Commit:
gsd-sdk query commit "docs(${padded_phase}): generate context from PRD" --files "${phase_dir}/${padded_phase}-CONTEXT.md"
  1. 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.

4. Load CONTEXT.md

Skip if: PRD express path was used (CONTEXT.md already created in step 3.5).

Check context_path from init JSON.

If context_path is not null, display: Using phase context from: ${context_path}

If context_path is null (no CONTEXT.md exists):

Read discuss mode for context gate label:

DISCUSS_MODE=$(gsd-sdk query config-get workflow.discuss_mode 2>/dev/null || echo "discuss")

If TEXT_MODE is true, present as a plain-text numbered list:

No CONTEXT.md found for Phase {X}. Plans will use research and requirements only — your design preferences won't be included.

1. Continue without context — Plan using research + requirements only
[If DISCUSS_MODE is "assumptions":]
2. Gather context (assumptions mode) — Analyze codebase and surface assumptions before planning
[If DISCUSS_MODE is "discuss" or unset:]
2. Run discuss-phase first — Capture design decisions before planning

Enter number:

Otherwise use AskUserQuestion:

  • header: "No context"
  • question: "No CONTEXT.md found for Phase {X}. Plans will use research and requirements only — your design preferences won't be included. Continue or capture context first?"
  • options:
    • "Continue without context" — Plan using research + requirements only If DISCUSS_MODE is "assumptions":
    • "Gather context (assumptions mode)" — Analyze codebase and surface assumptions before planning If DISCUSS_MODE is "discuss" (or unset):
    • "Run discuss-phase first" — Capture design decisions before planning

If "Continue without context": Proceed to step 5. If "Run discuss-phase first": IMPORTANT: Do NOT invoke discuss-phase as a nested Skill/Task call — AskUserQuestion does not work correctly in nested subcontexts (#1009). Instead, display the command and exit so the user runs it as a top-level command:

Run this command first, then re-run /gsd-plan-phase {X} ${GSD_WS}:

/gsd-discuss-phase {X} ${GSD_WS}

Exit the plan-phase workflow. Do not continue.

4.5. Check AI-SPEC

Skip if: ai_integration_phase_enabled from config is false, or --skip-ai-spec flag provided.

AI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-AI-SPEC.md 2>/dev/null | head -1)
AI_PHASE_CFG=$(gsd-sdk query config-get workflow.ai_integration_phase 2>/dev/null || echo "true")

Skip if AI_PHASE_CFG is false.

If AI_SPEC_FILE is empty: Check phase goal for AI keywords:

echo "${phase_goal}" | grep -qi "agent\|llm\|rag\|chatbot\|embedding\|langchain\|llamaindex\|crewai\|langgraph\|openai\|anthropic\|vector\|eval\|ai system"

If AI keywords detected AND no AI-SPEC.md:

◆ Note: This phase appears to involve AI system development.
  Consider running /gsd-ai-integration-phase {N} before planning to:
  - Select the right framework for your use case
  - Research its docs and best practices
  - Design an evaluation strategy

  Continue planning without AI-SPEC? (non-blocking — /gsd-ai-integration-phase can be run after)

Use AskUserQuestion with options:

  • "Continue — plan without AI-SPEC"
  • "Stop — I'll run /gsd-ai-integration-phase {N} first"

If "Stop": Exit with /gsd-ai-integration-phase {N} reminder. If "Continue": Proceed. (Non-blocking — planner will note AI-SPEC is absent.)

If AI_SPEC_FILE is non-empty: Extract framework for planner context:

FRAMEWORK_LINE=$(grep "Selected Framework:" "${AI_SPEC_FILE}" | head -1)

Pass ai_spec_path and framework_line to planner in step 7 so it can reference the AI design contract.

5. Handle Research

Skip if: --gaps flag or --skip-research flag or --reviews flag.

5.0. Research-Only Modifiers (--view, --research, prompt)

Skip if: RESEARCH_ONLY is false.

Three branches in research-only mode (--research-phase <N>):

  1. --view (or user picks "View" in the prompt below): print RESEARCH.md to stdout, no spawn, exit. If RESEARCH.md is missing, error with: --view requires an existing RESEARCH.md; drop --view to spawn the researcher.
  2. --research (force-refresh): re-spawn researcher unconditionally — fall through to "Spawn gsd-phase-researcher" below.
  3. Neither flag AND has_research=true: emit RESEARCH.md already exists for Phase ${PHASE}. and prompt the user with three choices: 1. Update — re-spawn researcher and refresh RESEARCH.md, 2. View — print existing RESEARCH.md and exit (no spawn), 3. Skip — exit without spawning or printing. Map "Update" → fall through to spawn, "View" → set VIEW_ONLY=true and emit RESEARCH.md as in (1), "Skip" → exit cleanly. Mirrors the deleted /gsd-research-phase standalone's existing-artifact menu (#3042 parity).
if [[ "$VIEW_ONLY" == "true" ]]; then
  [[ -f "$research_path" ]] || { echo "Error: --view requires an existing RESEARCH.md (Phase ${PHASE}). Drop --view to spawn the researcher."; exit 1; }
  cat "$research_path"; exit 0
fi

5.1. Standard Research Decision

Skip if RESEARCH_ONLY=true (the research-only mode in 5.0 already determined the path: spawn or exit). Without this guard, an LLM following the workflow could fall through into "use existing, skip to step 6" → planner spawn, violating the research-only contract. CR #3045 finding: this gate makes the early-exit unreachable from any non-research-only branch.

If has_research is true (from init) AND no --research flag: Use existing, skip to step 6.

If RESEARCH.md missing OR --research flag:

If no explicit flag (--research or --skip-research) and not --auto: Ask the user whether to research, with a contextual recommendation based on the phase:

If TEXT_MODE is true, present as a plain-text numbered list:

Research before planning Phase {X}: {phase_name}?

1. Research first (Recommended) — Investigate domain, patterns, and dependencies before planning. Best for new features, unfamiliar integrations, or architectural changes.
2. Skip research — Plan directly from context and requirements. Best for bug fixes, simple refactors, or well-understood tasks.

Enter number:

Otherwise use AskUserQuestion:

AskUserQuestion([
  {
    question: "Research before planning Phase {X}: {phase_name}?",
    header: "Research",
    multiSelect: false,
    options: [
      { label: "Research first (Recommended)", description: "Investigate domain, patterns, and dependencies before planning. Best for new features, unfamiliar integrations, or architectural changes." },
      { label: "Skip research", description: "Plan directly from context and requirements. Best for bug fixes, simple refactors, or well-understood tasks." }
    ]
  }
])

If user selects "Skip research": skip to step 6.

If --auto and research_enabled is false: Skip research silently (preserves automated behavior).

Display banner:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 GSD ► RESEARCHING PHASE {X}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

◆ Spawning researcher...

Spawn gsd-phase-researcher

PHASE_DESC=$(gsd-sdk query roadmap.get-phase "${PHASE}" --pick section)

Research prompt:

<objective>
Research how to implement Phase {phase_number}: {phase_name}
Answer: "What do I need to know to PLAN this phase well?"
</objective>

<files_to_read>
- {context_path} (USER DECISIONS from /gsd-discuss-phase)
- {requirements_path} (Project requirements)
- {state_path} (Project decisions and history)
</files_to_read>

${AGENT_SKILLS_RESEARCHER}

<additional_context>
**Phase description:** {phase_description}
**Phase requirement IDs (MUST address):** {phase_req_ids}

**Project instructions:** Read ./CLAUDE.md if exists — follow project-specific guidelines
**Project skills:** Check .claude/skills/ or .agents/skills/ directory (if either exists) — read SKILL.md files, research should account for project skill patterns
</additional_context>

<output>
Write to: {phase_dir}/{phase_num}-RESEARCH.md
</output>
Agent(
  prompt=research_prompt,
  subagent_type="gsd-phase-researcher",
  model="{researcher_model}",
  description="Research Phase {phase}"
)

ORCHESTRATOR RULE — CODEX RUNTIME: After calling Agent() above, stop working on this task immediately. Do not read more files, edit code, or run tests related to this task while the subagent is active. Wait for the subagent to return its result. This prevents duplicate work, conflicting edits, and wasted context. Only resume when the subagent result is available.

Handle Researcher Return

  • ## RESEARCH COMPLETE: Display confirmation, continue to step 6
  • ## RESEARCH BLOCKED: Display blocker, offer: 1) Provide context, 2) Skip research, 3) Abort

Research-Only Early Exit (--research-phase)

Skip if: RESEARCH_ONLY is false (the default).

If RESEARCH_ONLY=true: the user invoked /gsd-plan-phase --research-phase <N> for research-only mode. Do not continue to Section 5.5+ (validation strategy, planner, plan-checker, verification, gaps, bounce, post-planning-gaps). Print the research-complete summary and exit cleanly:

✓ Research-only mode complete (#3042)

  Phase:       ${PHASE}
  RESEARCH.md: ${research_path}

Re-run /gsd-plan-phase ${PHASE} to plan the phase using this research,
or /gsd-plan-phase ${PHASE} --research to refresh research and plan.

This exits the workflow. The planner / plan-checker / verifier blocks below are skipped.

5.5. Create Validation Strategy

Skip if nyquist_validation_enabled is false OR research_enabled is false.

If research_enabled is false and nyquist_validation_enabled is true: warn "Nyquist validation enabled but research disabled — VALIDATION.md cannot be created without RESEARCH.md. Plans will lack validation requirements (Dimension 8)." Continue to step 6.

But Nyquist is not applicable for this run when all of the following are true:

  • research_enabled is false
  • has_research is false
  • no --research flag was provided

In that case: skip validation-strategy creation entirely. Do not expect RESEARCH.md or VALIDATION.md for this run, and continue to Step 6.

grep -l "## Validation Architecture" "${PHASE_DIR}"/*-RESEARCH.md 2>/dev/null || true

If found:

  1. Read template: ~/.claude/get-shit-done/templates/VALIDATION.md
  2. Write to ${PHASE_DIR}/${PADDED_PHASE}-VALIDATION.md (use Write tool)
  3. Fill frontmatter: {N} → phase number, {phase-slug} → slug, {date} → current date
  4. Verify:
test -f "${PHASE_DIR}/${PADDED_PHASE}-VALIDATION.md" && echo "VALIDATION_CREATED=true" || echo "VALIDATION_CREATED=false"
  1. If VALIDATION_CREATED=false: STOP — do not proceed to Step 6
  2. If commit_docs: commit "docs(phase-${PHASE}): add validation strategy"

If not found: Warn and continue — plans may fail Dimension 8.

5.55. Security Threat Model Gate

Skip if workflow.security_enforcement is explicitly false. Absent = enabled.

SECURITY_CFG=$(gsd-sdk query config-get workflow.security_enforcement --raw 2>/dev/null || echo "true")
SECURITY_ASVS=$(gsd-sdk query config-get workflow.security_asvs_level --raw 2>/dev/null || echo "1")
SECURITY_BLOCK=$(gsd-sdk query config-get workflow.security_block_on --raw 2>/dev/null || echo "high")

If SECURITY_CFG is false: Skip to step 5.6.

If SECURITY_CFG is true: Display banner:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 GSD ► SECURITY THREAT MODEL REQUIRED (ASVS L{SECURITY_ASVS})
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Each PLAN.md must include a <threat_model> block.
Block on: {SECURITY_BLOCK} severity threats.
Opt out: set security_enforcement: false in .planning/config.json

Continue to step 5.6. Security config is passed to the planner in step 8.

5.6. UI Design Contract Gate

Skip if workflow.ui_phase is explicitly false AND workflow.ui_safety_gate is explicitly false in .planning/config.json. If keys are absent, treat as enabled.

UI_PHASE_CFG=$(gsd-sdk query config-get workflow.ui_phase 2>/dev/null || echo "true")
UI_GATE_CFG=$(gsd-sdk query config-get workflow.ui_safety_gate 2>/dev/null || echo "true")

If both are false: Skip to step 6.

Check if phase has frontend indicators:

PHASE_SECTION=$(gsd-sdk query roadmap.get-phase "${PHASE}" 2>/dev/null)
echo "$PHASE_SECTION" | grep -iE "UI|interface|frontend|component|layout|page|screen|view|form|dashboard|widget" > /dev/null 2>&1
HAS_UI=$?

If HAS_UI is 0 (frontend indicators found):

Check for existing UI-SPEC:

UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)

If UI-SPEC.md found: Set UI_SPEC_PATH=$UI_SPEC_FILE. Display: Using UI design contract: ${UI_SPEC_PATH}

If UI-SPEC.md missing AND --skip-ui flag is present in $ARGUMENTS: Skip silently to step 6.

If UI-SPEC.md missing AND UI_GATE_CFG is true:

Read ephemeral chain flag (same field as check.auto-mode → auto_chain_active):

AUTO_CHAIN=$(gsd-sdk query check auto-mode --pick auto_chain_active 2>/dev/null || echo "false")

If AUTO_CHAIN is true (running inside a --chain or --auto pipeline):

Auto-generate UI-SPEC without prompting:

Skill(skill="gsd-ui-phase", args="${PHASE} --auto ${GSD_WS}")

After gsd-ui-phase returns, re-read:

UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
UI_SPEC_PATH="${UI_SPEC_FILE}"

Continue to step 6.

If AUTO_CHAIN is false (manual invocation):

Output this markdown directly (not as a code block):

## ⚠ UI-SPEC.md missing for Phase {N}
▶ Recommended next step:
`/gsd-ui-phase {N} ${GSD_WS}` — generate UI design contract before planning
───────────────────────────────────────────────
Also available:
- `/gsd-plan-phase {N} --skip-ui ${GSD_WS}` — plan without UI-SPEC (not recommended for frontend phases)

Exit the plan-phase workflow. Do not continue.

If HAS_UI is 1 (no frontend indicators): Skip silently to step 5.7.

5.7. Schema Push Detection Gate

Detects schema-relevant files in the phase scope and injects a mandatory [BLOCKING] schema push task into the plan. Prevents false-positive verification where build/types pass because TypeScript types come from config, not the live database.

Check if any files in the phase scope match schema patterns:

PHASE_SECTION=$(gsd-sdk query roadmap.get-phase "${PHASE}" --pick section 2>/dev/null)

Scan PHASE_SECTION, CONTEXT.md (if loaded), and RESEARCH.md (if exists) for file paths matching these ORM patterns:

ORM File Patterns
Payload CMS src/collections/**/*.ts, src/globals/**/*.ts
Prisma prisma/schema.prisma, prisma/schema/*.prisma
Drizzle drizzle/schema.ts, src/db/schema.ts, drizzle/*.ts
Supabase supabase/migrations/*.sql
TypeORM src/entities/**/*.ts, src/migrations/**/*.ts

Also check if any existing PLAN.md files for this phase already reference these file patterns in files_modified.

If schema-relevant files detected:

Set SCHEMA_PUSH_REQUIRED=true and SCHEMA_ORM={detected_orm}.

Determine the push command for the detected ORM:

ORM Push Command Non-TTY Workaround
Payload CMS npx payload migrate CI=true PAYLOAD_MIGRATING=true npx payload migrate
Prisma npx prisma db push npx prisma db push --accept-data-loss (if destructive)
Drizzle npx drizzle-kit push npx drizzle-kit push
Supabase supabase db push Set SUPABASE_ACCESS_TOKEN env var
TypeORM npx typeorm migration:run npx typeorm migration:run -d src/data-source.ts

Inject the following into the planner prompt (step 8) as an additional constraint:

<schema_push_requirement>
**[BLOCKING] Schema Push Required**

This phase modifies schema-relevant files ({detected_files}). The planner MUST include
a `[BLOCKING]` task that runs the database schema push command AFTER all schema file
modifications are complete but BEFORE verification.

- ORM detected: {SCHEMA_ORM}
- Push command: {push_command}
- Non-TTY workaround: {env_hint}
- If push requires interactive prompts that cannot be suppressed, flag the task for
  manual intervention with `autonomous: false`

This task is mandatory — the phase CANNOT pass verification without it. Build and
type checks will pass without the push (types come from config, not the live database),
creating a false-positive verification state.
</schema_push_requirement>

Display: Schema files detected ({SCHEMA_ORM}) — [BLOCKING] push task will be injected into plans

If no schema-relevant files detected: Skip silently to step 6.

6. Check Existing Plans

ls "${PHASE_DIR}"/*-PLAN.md 2>/dev/null || true

If exists AND --reviews flag: Skip prompt — go straight to replanning (the purpose of --reviews is to replan with review feedback).

If exists AND no --reviews flag: Offer: 1) Add more plans, 2) View existing, 3) Replan from scratch.

7. Use Context Paths from INIT

Extract from INIT JSON:

_gsd_field() { node -e "const o=JSON.parse(process.argv[1]); const v=o[process.argv[2]]; process.stdout.write(v==null?'':String(v))" "$1" "$2"; }
STATE_PATH=$(_gsd_field "$INIT" state_path)
ROADMAP_PATH=$(_gsd_field "$INIT" roadmap_path)
REQUIREMENTS_PATH=$(_gsd_field "$INIT" requirements_path)
RESEARCH_PATH=$(_gsd_field "$INIT" research_path)
VERIFICATION_PATH=$(_gsd_field "$INIT" verification_path)
UAT_PATH=$(_gsd_field "$INIT" uat_path)
CONTEXT_PATH=$(_gsd_field "$INIT" context_path)
REVIEWS_PATH=$(_gsd_field "$INIT" reviews_path)
PATTERNS_PATH=$(_gsd_field "$INIT" patterns_path)

# Detect spike/sketch findings skills (project-local)
SPIKE_FINDINGS_PATH=$(ls ./.claude/skills/spike-findings-*/SKILL.md 2>/dev/null | head -1 || true)
SKETCH_FINDINGS_PATH=$(ls ./.claude/skills/sketch-findings-*/SKILL.md 2>/dev/null | head -1 || true)

7.5. Verify Nyquist Artifacts

Skip if nyquist_validation_enabled is false OR research_enabled is false.

Also skip if all of the following are true:

  • research_enabled is false
  • has_research is false
  • no --research flag was provided

In that no-research path, Nyquist artifacts are not required for this run.

VALIDATION_EXISTS=$(ls "${PHASE_DIR}"/*-VALIDATION.md 2>/dev/null | head -1)

If missing and Nyquist is still enabled/applicable — ask user:

  1. Re-run: /gsd-plan-phase {PHASE} --research ${GSD_WS}
  2. Disable Nyquist with the exact command: gsd-sdk query config-set workflow.nyquist_validation false
  3. Continue anyway (plans fail Dimension 8)

Proceed to Step 7.8 (or Step 8 if pattern mapper is disabled) only if user selects 2 or 3.

7.8. Spawn gsd-pattern-mapper Agent (Optional)

Skip if workflow.pattern_mapper is explicitly set to false in config.json (absent key = enabled). Also skip if no CONTEXT.md and no RESEARCH.md exist for this phase (nothing to extract file lists from).

Check config:

PATTERN_MAPPER_CFG=$(gsd-sdk query config-get workflow.pattern_mapper 2>/dev/null || echo "true")

If PATTERN_MAPPER_CFG is false: Skip to step 8.

If PATTERNS.md already exists (PATTERNS_PATH is non-empty from step 7): Skip to step 8 (use existing).

Display banner:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 GSD ► PATTERN MAPPING PHASE {X}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

◆ Spawning pattern mapper...

Pattern mapper prompt:

<pattern_mapping_context>
**Phase:** {phase_number} - {phase_name}
**Phase directory:** {phase_dir}
**Padded phase:** {padded_phase}

<files_to_read>
- {context_path} (USER DECISIONS from /gsd-discuss-phase)
- {research_path} (Technical Research)
</files_to_read>

**Output file:** {phase_dir}/{padded_phase}-PATTERNS.md

Extract the list of files to be created/modified from CONTEXT.md and RESEARCH.md. For each file, classify by role and data flow, find the closest existing analog in the codebase, extract concrete code excerpts, and produce PATTERNS.md.
</pattern_mapping_context>

Spawn with:

Agent(
  prompt="{above}",
  subagent_type="gsd-pattern-mapper",
  model="{researcher_model}",
)

ORCHESTRATOR RULE — CODEX RUNTIME: After calling Agent() above, stop working on this task immediately. Do not read more files, edit code, or run tests related to this task while the subagent is active. Wait for the subagent to return its result. This prevents duplicate work, conflicting edits, and wasted context. Only resume when the subagent result is available.

Handle return:

  • ## PATTERN MAPPING COMPLETE: Update PATTERNS_PATH to the created file path, continue to step 8.
  • Any error or empty return: Log warning, continue to step 8 without patterns (non-blocking).

After pattern mapper completes, update the path variable:

PATTERNS_PATH="${PHASE_DIR}/${PADDED_PHASE}-PATTERNS.md"

8. Spawn gsd-planner Agent

Display banner:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 GSD ► PLANNING PHASE {X}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

◆ Spawning planner...

Planner prompt:

<planning_context>
**Phase:** {phase_number}
**Mode:** {standard | gap_closure | reviews}

<files_to_read>
- {state_path} (Project State)
- {roadmap_path} (Roadmap)
- {requirements_path} (Requirements)
- {context_path} (USER DECISIONS from /gsd-discuss-phase)
- {research_path} (Technical Research)
- {PATTERNS_PATH} (Pattern Map — analog files and code excerpts, if exists)
- {verification_path} (Verification Gaps - if --gaps)
- {uat_path} (UAT Gaps - if --gaps)
- {reviews_path} (Cross-AI Review Feedback - if --reviews)
- {UI_SPEC_PATH} (UI Design Contract — visual/interaction specs, if exists)
- {SPIKE_FINDINGS_PATH} (Spike Findings — validated patterns, constraints, landmines from experiments, if exists)
- {SKETCH_FINDINGS_PATH} (Sketch Findings — validated design decisions, CSS patterns, visual direction, if exists)
${CONTEXT_WINDOW >= 500000 ? `
**Cross-phase context (1M model enrichment):**
- CONTEXT.md files from the 3 most recent completed phases (locked decisions — maintain consistency)
- SUMMARY.md files from the 3 most recent completed phases (what was built — reuse patterns, avoid duplication)
- LEARNINGS.md files from the 3 most recent completed phases (structured decisions, patterns, lessons, surprises — skip silently if a phase has no LEARNINGS.md; prefix each block with \`[from Phase N LEARNINGS]\` for source attribution; if total size exceeds 15% of context budget, drop oldest first)
- CONTEXT.md, SUMMARY.md, and LEARNINGS.md from any phases listed in the current phase's "Depends on:" field in ROADMAP.md (regardless of recency — explicit dependencies always load, deduplicated against the 3 most recent)
- Skip all other prior phases to stay within context budget
` : ''}
</files_to_read>

${AGENT_SKILLS_PLANNER}

**Phase requirement IDs (every ID MUST appear in a plan's `requirements` field):** {phase_req_ids}

**Project instructions:** Read ./CLAUDE.md if exists — follow project-specific guidelines
**Project skills:** Check .claude/skills/ or .agents/skills/ directory (if either exists) — read SKILL.md files, plans should account for project skill rules

${TDD_MODE === 'true' ? `
<tdd_mode_active>
**TDD Mode is ENABLED.** Apply TDD heuristics from @~/.claude/get-shit-done/references/tdd.md to all eligible tasks:
- Business logic with defined I/O → type: tdd
- API endpoints with request/response contracts → type: tdd
- Data transformations, validation, algorithms → type: tdd
- UI, config, glue code, CRUD → standard plan (type: execute)
Each TDD plan gets one feature with RED/GREEN/REFACTOR gate sequence.
</tdd_mode_active>
` : ''}

**MVP_MODE:** ${MVP_MODE} (when true, follow vertical-slice rules from `@~/.claude/get-shit-done/references/planner-mvp-mode.md`; when false, ignore MVP guidance entirely.)
**WALKING_SKELETON:** ${WALKING_SKELETON} (when true, the first deliverable must be a Walking Skeleton — produce SKELETON.md alongside PLAN.md.)

${MVP_MODE === 'true' ? `
<mvp_mode_active>
**MVP Mode is ENABLED.** Follow vertical-slice planning rules from @~/.claude/get-shit-done/references/planner-mvp-mode.md. Each plan must deliver a complete vertical slice — thin end-to-end functionality rather than horizontal layers.
</mvp_mode_active>
` : ''}
</planning_context>

<downstream_consumer>
Output consumed by /gsd-execute-phase. Plans need:
- Frontmatter (wave, depends_on, files_modified, autonomous)
- Tasks in XML format with read_first and acceptance_criteria fields (MANDATORY on every task)
- Verification criteria
- must_haves for goal-backward verification
</downstream_consumer>

<deep_work_rules>
## Anti-Shallow Execution Rules (MANDATORY)

Every task MUST include these fields — they are NOT optional:

1. **`<read_first>`** — Files the executor MUST read before touching anything. Always include:
   - The file being modified (so executor sees current state, not assumptions)
   - Any "source of truth" file referenced in CONTEXT.md (reference implementations, existing patterns, config files, schemas)
   - Any file whose patterns, signatures, types, or conventions must be replicated or respected

2. **`<acceptance_criteria>`** — Verifiable conditions that prove the task was done correctly. Rules:
   - Every criterion must be checkable with grep, file read, test command, or CLI output
   - NEVER use subjective language ("looks correct", "properly configured", "consistent with")
   - ALWAYS include exact strings, patterns, values, or command outputs that must be present
   - Examples:
     - Code: `auth.py contains def verify_token(` / `test_auth.py exits 0`
     - Config: `.env.example contains DATABASE_URL=` / `Dockerfile contains HEALTHCHECK`
     - Docs: `README.md contains '## Installation'` / `API.md lists all endpoints`
     - Infra: `deploy.yml has rollback step` / `docker-compose.yml has healthcheck for db`

3. **`<action>`** — Must include CONCRETE values, not references. Rules:
   - NEVER say "align X with Y", "match X to Y", "update to be consistent" without specifying the exact target state
   - ALWAYS include the actual values: config keys, function signatures, SQL statements, class names, import paths, env vars, etc.
   - If CONTEXT.md has a comparison table or expected values, copy them into the action verbatim
   - The executor should be able to complete the task from the action text alone, without needing to read CONTEXT.md or reference files (read_first is for verification, not discovery)

**Why this matters:** Executor agents work from the plan text. Vague instructions like "update the config to match production" produce shallow one-line changes. Concrete instructions like "add DATABASE_URL=postgresql://... , set POOL_SIZE=20, add REDIS_URL=redis://..." produce complete work. The cost of verbose plans is far less than the cost of re-doing shallow execution.
</deep_work_rules>

<quality_gate>
- [ ] PLAN.md files created in phase directory
- [ ] Each plan has valid frontmatter
- [ ] Tasks are specific and actionable
- [ ] Every task has `<read_first>` with at least the file being modified
- [ ] Every task has `<acceptance_criteria>` with grep-verifiable conditions
- [ ] Every `<action>` contains concrete values (no "align X with Y" without specifying what)
- [ ] Dependencies correctly identified
- [ ] Waves assigned for parallel execution
- [ ] must_haves derived from phase goal
</quality_gate>

If CHUNKED_MODE is false (default): Spawn the planner as a single long-lived Agent:

Agent(
  prompt=filled_prompt,
  subagent_type="gsd-planner",
  model="{planner_model}",
  description="Plan Phase {phase}"
)

ORCHESTRATOR RULE — CODEX RUNTIME: After calling Agent() above, stop working on this task immediately. Do not read more files, edit code, or run tests related to this task while the subagent is active. Wait for the subagent to return its result. This prevents duplicate work, conflicting edits, and wasted context. Only resume when the subagent result is available.

If CHUNKED_MODE is true: Skip the Agent() call above — proceed to step 8.5 instead.

8.5. Chunked Planning Mode

Skip if CHUNKED_MODE is false.

Chunked mode splits the single long-lived planner Agent run into a short outline Agent run followed by N short per-plan Agent runs. Each run is bounded to ~3–5 min; each plan is committed individually for crash resilience. If any run hangs and the terminal is force-killed, rerunning /gsd-plan-phase {N} --chunked resumes from the last successfully committed plan.

Intended for new or in-progress chunked runs. To recover plans already written by a prior non-chunked run, use step 6's "Add more plans" or proceed directly to /gsd-execute-phase — don't start a fresh chunked run over existing non-chunked plans.

8.5.1 Outline Phase (outline-only mode, ~2 min)

Resume detection: If ${PHASE_DIR}/${PADDED_PHASE}-PLAN-OUTLINE.md already exists and is valid (contains the ## OUTLINE COMPLETE marker), skip this sub-step — the outline already exists from a previous run. Proceed directly to 8.5.2.

OUTLINE_FILE="${PHASE_DIR}/${PADDED_PHASE}-PLAN-OUTLINE.md"
if [[ -f "$OUTLINE_FILE" ]] && grep -q "^## OUTLINE COMPLETE" "$OUTLINE_FILE"; then
  # reuse existing outline — skip to 8.5.2
fi

Display:

◆ Chunked mode: spawning outline planner...

Spawn the planner in outline-only mode — it must write only the outline manifest, not any PLAN.md files:

Agent(
  prompt="{same planning_context as step 8, plus:}

  **Chunked mode: outline-only.**
  Do NOT write any PLAN.md files in this Task.
  Write only: {PHASE_DIR}/{PADDED_PHASE}-PLAN-OUTLINE.md

  The outline must be a markdown table with columns:
  Plan ID | Objective | Wave | Depends On | Requirements

  Return: ## OUTLINE COMPLETE with plan count.",
  subagent_type="gsd-planner",
  model="{planner_model}",
  description="Outline Phase {phase} (chunked)"
)

ORCHESTRATOR RULE — CODEX RUNTIME: After calling Agent() above, stop working on this task immediately. Do not read more files, edit code, or run tests related to this task while the subagent is active. Wait for the subagent to return its result. This prevents duplicate work, conflicting edits, and wasted context. Only resume when the subagent result is available.

Handle return:

  • ## OUTLINE COMPLETE: Read PLAN-OUTLINE.md, extract plan list. Continue to 8.5.2.
  • Any other return or empty: Display error. Offer: 1) Retry outline, 2) Stop.

8.5.2 Per-Plan Tasks (single-plan mode, ~3-5 min each)

For each plan entry extracted from PLAN-OUTLINE.md:

  1. Resume check: If ${PHASE_DIR}/{plan_id}-PLAN.md already exists on disk and has valid YAML frontmatter (opening --- delimiter present), skip this plan (do not overwrite completed work — resume safety).

    PLAN_FILE="${PHASE_DIR}/${plan_id}-PLAN.md"
    if [[ -f "$PLAN_FILE" ]] && head -1 "$PLAN_FILE" | grep -q '^---'; then
      continue  # plan already written, skip
    fi
    
  2. Display:

    ◆ Chunked mode: planning {plan_id} ({k}/{N})...
    
  3. Spawn the planner in single-plan mode — it must write exactly one PLAN.md file:

    Agent(
      prompt="{same planning_context as step 8, plus:}
    
      **Chunked mode: single-plan.**
      Write exactly ONE plan file: {PHASE_DIR}/{plan_id}-PLAN.md
      Plan to write: {plan_id} — {objective}
      Wave: {wave} | Depends on: {depends_on}
      Phase requirement IDs to cover in this plan: {plan_requirements}
    
      Return: ## PLAN COMPLETE with the plan ID.",
      subagent_type="gsd-planner",
      model="{planner_model}",
      description="Plan {plan_id} (chunked {k}/{N})"
    )
    

    ORCHESTRATOR RULE — CODEX RUNTIME: After calling Agent() above, stop working on this task immediately. Do not read more files, edit code, or run tests related to this task while the subagent is active. Wait for the subagent to return its result. This prevents duplicate work, conflicting edits, and wasted context. Only resume when the subagent result is available.

  4. Verify disk: Check ${PHASE_DIR}/{plan_id}-PLAN.md exists. If missing: offer 1) Retry, 2) Stop.

  5. Commit per-plan:

    gsd-sdk query commit "docs(${PADDED_PHASE}): plan ${plan_id} (chunked)" --files "${PHASE_DIR}/${plan_id}-PLAN.md"
    

After all N plans are written and committed, treat this as ## PLANNING COMPLETE and continue to step 9.

9. Handle Planner Return

  • ## PLANNING COMPLETE: Display plan count. If --skip-verify or plan_checker_enabled is false (from init): skip to step 13. Otherwise: step 10.
  • ## PHASE SPLIT RECOMMENDED: The planner determined the phase exceeds the context budget for full-fidelity implementation of all source items. Handle in step 9b.
  • ## ⚠ Source Audit: Unplanned Items Found: The planner's multi-source coverage audit found items from REQUIREMENTS.md, RESEARCH.md, ROADMAP goal, or CONTEXT.md decisions that are not covered by any plan. Handle in step 9c.
  • ## CHECKPOINT REACHED: Present to user, get response, spawn continuation (step 12)
  • ## PLANNING INCONCLUSIVE: Show attempts, offer: Add context / Retry / Manual
  • Empty / truncated / no recognized marker: → Filesystem fallback (step 9a).

9a. Filesystem Fallback (Planner)

Triggered when: Agent() returns but the return contains no recognized marker (## PLANNING COMPLETE, ## PHASE SPLIT RECOMMENDED, ## ⚠ Source Audit, ## CHECKPOINT REACHED, ## PLANNING INCONCLUSIVE).

DISK_PLANS=$(ls "${PHASE_DIR}"/*-PLAN.md 2>/dev/null | wc -l | tr -d ' ')

If DISK_PLANS > 0: The planner wrote plans to disk but the Agent() return was empty or truncated (the Windows stdio hang pattern — the subagent finished but the return never arrived). Display:

◆ Planner wrote {DISK_PLANS} plan(s) to disk but did not emit a PLANNING COMPLETE marker.
  This is a known Windows stdio hang pattern — work is likely recoverable.

  Plans found on disk:
  {ls output of *-PLAN.md}

Offer 3 options:

  1. Accept plans — treat as ## PLANNING COMPLETE and continue through step 9 ## PLANNING COMPLETE handling (so --skip-verify / plan_checker_enabled=false are honored — may skip to step 13 rather than step 10)
  2. Retry planner — re-spawn the planner with the same prompt (return to step 8)
  3. Stop — exit; user can re-run /gsd-plan-phase {N} to resume

If DISK_PLANS is 0 and no marker: The planner produced no output. Treat as ## PLANNING INCONCLUSIVE and handle accordingly.

9b. Handle Phase Split Recommendation

When the planner returns ## PHASE SPLIT RECOMMENDED, it means the phase's source items exceed the context budget for full-fidelity implementation. The planner proposes groupings.

Extract from planner return:

  • Proposed sub-phases (e.g., "17a: processing core (D-01 to D-19)", "17b: billing + config UX (D-20 to D-27)")
  • Which source items (REQ-IDs, D-XX decisions, RESEARCH items) go in each sub-phase
  • Why the split is necessary (context cost estimate, file count)

Present to user:

## Phase {X} exceeds context budget for full-fidelity implementation

The planner found {N} source items that exceed the context budget when
planned at full fidelity. Instead of reducing scope, we recommend splitting:

**Option 1: Split into sub-phases**
- Phase {X}a: {name} — {items} ({N} source items, ~{P}% context)
- Phase {X}b: {name} — {items} ({M} source items, ~{Q}% context)

**Option 2: Proceed anyway** (planner will attempt all, quality may degrade past 50% context)

**Option 3: Prioritize** — you choose which items to implement now,
rest become a follow-up phase

Use AskUserQuestion with these 3 options.

If "Split": Use /gsd-phase --insert to create the sub-phases, then replan each. If "Proceed": Return to planner with instruction to attempt all items at full fidelity, accepting more plans/tasks. If "Prioritize": Use AskUserQuestion (multiSelect) to let user pick which items are "now" vs "later". Create CONTEXT.md for each sub-phase with the selected items.

9c. Handle Source Audit Gaps

When the planner returns ## ⚠ Source Audit: Unplanned Items Found, it means items from REQUIREMENTS.md, RESEARCH.md, ROADMAP goal, or CONTEXT.md decisions have no corresponding plan.

Extract from planner return:

  • Each unplanned item with its source artifact and section
  • The planner's suggested options (A: add plan, B: split phase, C: defer with confirmation)

Present each gap to user. For each unplanned item:

## ⚠ Unplanned: {item description}

Source: {RESEARCH.md / REQUIREMENTS.md / ROADMAP goal / CONTEXT.md}
Details: {why the planner flagged this}

Options:
1. Add a plan to cover this item (recommended)
2. Split phase — move to a sub-phase with related items
3. Defer — add to backlog (developer confirms this is intentional)

Use AskUserQuestion for each gap (or batch if multiple gaps).

If "Add plan": Return to planner (step 8) with instruction to add plans covering the missing items, preserving existing plans. If "Split": Use /gsd-phase --insert for overflow items, then replan. If "Defer": Record in CONTEXT.md ## Deferred Ideas with developer's confirmation. Proceed to step 10.

10. Spawn gsd-plan-checker Agent

Display banner:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 GSD ► VERIFYING PLANS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

◆ Spawning plan checker...

Checker prompt:

<verification_context>
**Phase:** {phase_number}
**Phase Goal:** {goal from ROADMAP}

<files_to_read>
- {PHASE_DIR}/*-PLAN.md (Plans to verify)
- {roadmap_path} (Roadmap)
- {requirements_path} (Requirements)
- {context_path} (USER DECISIONS from /gsd-discuss-phase)
- {research_path} (Technical Research — includes Validation Architecture)
</files_to_read>

${AGENT_SKILLS_CHECKER}

**Phase requirement IDs (MUST ALL be covered):** {phase_req_ids}

**Project instructions:** Read ./CLAUDE.md if exists — verify plans honor project guidelines
**Project skills:** Check .claude/skills/ or .agents/skills/ directory (if either exists) — verify plans account for project skill rules
</verification_context>

<expected_output>
- ## VERIFICATION PASSED — all checks pass
- ## ISSUES FOUND — structured issue list
</expected_output>
Agent(
  prompt=checker_prompt,
  subagent_type="gsd-plan-checker",
  model="{checker_model}",
  description="Verify Phase {phase} plans"
)

ORCHESTRATOR RULE — CODEX RUNTIME: After calling Agent() above, stop working on this task immediately. Do not read more files, edit code, or run tests related to this task while the subagent is active. Wait for the subagent to return its result. This prevents duplicate work, conflicting edits, and wasted context. Only resume when the subagent result is available.

11. Handle Checker Return

  • ## VERIFICATION PASSED: Display confirmation, proceed to step 13.
  • ## ISSUES FOUND: Display issues, check iteration count, proceed to step 12.
  • Empty / truncated / no recognized marker: → Filesystem fallback (step 11a).

Thinking partner for architectural tradeoffs (conditional): If features.thinking_partner is enabled, scan the checker's issues for architectural tradeoff keywords ("architecture", "approach", "strategy", "pattern", "vs", "alternative"). If found:

The plan-checker flagged an architectural decision point:
{issue description}

Brief analysis:
- Option A: {approach_from_plan} — {pros/cons}
- Option B: {alternative_approach} — {pros/cons}
- Recommendation: {choice} aligned with {phase_goal}

Apply this to the revision? [Yes] / [No, I'll decide]

If yes: include the recommendation in the revision prompt. If no: proceed to revision loop as normal. If thinking_partner disabled: skip this block entirely.

11a. Filesystem Fallback (Checker)

Triggered when: Checker Agent() returns but the return contains neither ## VERIFICATION PASSED nor ## ISSUES FOUND.

DISK_PLANS=$(ls "${PHASE_DIR}"/*-PLAN.md 2>/dev/null | wc -l | tr -d ' ')

If DISK_PLANS > 0: Plans exist on disk; the checker return was empty or truncated (the Windows stdio hang pattern — the subagent finished but the return never arrived). Display:

◆ Checker return was empty or truncated. {DISK_PLANS} plan(s) exist on disk.
  This is a known Windows stdio hang pattern — checker may have completed without returning.

Offer 3 options:

  1. Accept verification — treat as ## VERIFICATION PASSED and continue to step 13
  2. Retry checker — re-spawn the checker with the same prompt (return to step 10)
  3. Stop — exit; user can re-run /gsd-plan-phase {N} to resume

If DISK_PLANS is 0: No plans on disk — something is seriously wrong. Display error and stop.

12. Revision Loop (Max 3 Iterations)

Track iteration_count (starts at 1 after initial plan + check). Track prev_issue_count (initialized to Infinity before the loop begins). Track stall_reentry_count (starts at 0; incremented each time "Adjust approach" re-enters step 8).

If iteration_count < 3:

Parse issue count from checker return: count BLOCKER + WARNING entries in the YAML issues block (structured output from gsd-plan-checker). If the checker's return contains no YAML issues block (i.e., the plan was approved with no issues), treat issue_count as 0 and skip the stall check — the plan passed. Proceed to step 13.

Display: Revision iteration {N}/3 -- {blocker_count} blockers, {warning_count} warnings

Stall detection: If issue_count >= prev_issue_count: Display: Revision loop stalled — issue count not decreasing ({issue_count} issues remain after {N} iterations)

If stall_reentry_count < 2: Ask user: Question: "Issues remain after {N} revision attempts with no progress. Proceed with current output?" Options: "Proceed anyway" | "Adjust approach" If "Proceed anyway": accept current plans and continue to step 13. If "Adjust approach": increment stall_reentry_count, open freeform discussion, then re-enter step 8 (full replanning). Note: re-entry resets iteration_count and prev_issue_count but stall_reentry_count persists across re-entries and is capped at 2.

If stall_reentry_count >= 2: Display: Stall persists after 2 re-planning attempts. The following issues could not be resolved automatically: List the remaining issues from the checker. Suggest: "Consider resolving these issues manually or running /gsd-debug to investigate root causes." Options: "Proceed anyway" | "Abandon" If "Proceed anyway": accept current plans and continue to step 13. If "Abandon": stop workflow.

Set prev_issue_count = issue_count.

Revision prompt:

<revision_context>
**Phase:** {phase_number}
**Mode:** revision

<files_to_read>
- {PHASE_DIR}/*-PLAN.md (Existing plans)
- {context_path} (USER DECISIONS from /gsd-discuss-phase)
</files_to_read>

${AGENT_SKILLS_PLANNER}

**Checker issues:** {structured_issues_from_checker}
</revision_context>

<instructions>
Make targeted updates to address checker issues.
Do NOT replan from scratch unless issues are fundamental.
Return what changed.
</instructions>
Agent(
  prompt=revision_prompt,
  subagent_type="gsd-planner",
  model="{planner_model}",
  description="Revise Phase {phase} plans"
)

ORCHESTRATOR RULE — CODEX RUNTIME: After calling Agent() above, stop working on this task immediately. Do not read more files, edit code, or run tests related to this task while the subagent is active. Wait for the subagent to return its result. This prevents duplicate work, conflicting edits, and wasted context. Only resume when the subagent result is available.

After planner returns -> spawn checker again (step 10), increment iteration_count.

If iteration_count >= 3:

Display: Max iterations reached. {N} issues remain: + issue list

Offer: 1) Force proceed, 2) Provide guidance and retry, 3) Abandon

12.5. Plan Bounce (Optional External Refinement)

Skip if: --skip-bounce flag, --gaps flag, or bounce is not activated.

Activation: Bounce runs when --bounce flag is present OR workflow.plan_bounce config is true. The --skip-bounce flag always wins (disables bounce even if config enables it). The --gaps flag also disables bounce (gap-closure mode should not modify plans externally).

Prerequisites: workflow.plan_bounce_script must be set to a valid script path. If bounce is activated but no script is configured, display warning and skip:

⚠ Plan bounce activated but no script configured.
Set workflow.plan_bounce_script to the path of your refinement script.
Skipping bounce step.

Read pass count:

BOUNCE_PASSES=$(gsd-sdk query config-get workflow.plan_bounce_passes 2>/dev/null || echo "2")
BOUNCE_SCRIPT=$(gsd-sdk query config-get workflow.plan_bounce_script 2>/dev/null | jq -r '.' 2>/dev/null || true)

Display banner:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 GSD ► BOUNCING PLANS (External Refinement)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Script: ${BOUNCE_SCRIPT}
Max passes: ${BOUNCE_PASSES}

For each PLAN.md file in the phase directory:

  1. Backup: Copy *-PLAN.md to *-PLAN.pre-bounce.md
cp "${PLAN_FILE}" "${PLAN_FILE%.md}.pre-bounce.md"
  1. Invoke bounce script:
"${BOUNCE_SCRIPT}" "${PLAN_FILE}" "${BOUNCE_PASSES}"
  1. Validate bounced plan — YAML frontmatter integrity: After the script returns, check that the bounced file still has valid YAML frontmatter (opening and closing --- delimiters with parseable content between them). If the bounced plan breaks YAML frontmatter validation, restore the original from the pre-bounce.md backup and continue to the next plan:
⚠ Bounced plan ${PLAN_FILE} has broken YAML frontmatter — restoring original from pre-bounce backup.
  1. Handle script failure: If the bounce script exits non-zero, restore the original plan from the pre-bounce.md backup and continue to the next plan:
⚠ Bounce script failed for ${PLAN_FILE} (exit code ${EXIT_CODE}) — restoring original from pre-bounce backup.

After all plans are bounced:

  1. Re-run plan checker on bounced plans: Spawn gsd-plan-checker (same as step 10) on all modified plans. If a bounced plan fails the checker, restore original from its pre-bounce.md backup:
⚠ Bounced plan ${PLAN_FILE} failed checker validation — restoring original from pre-bounce backup.
  1. Commit surviving bounced plans: If at least one plan survived both the frontmatter validation and the checker re-run, commit the changes:
gsd-sdk query commit "refactor(${padded_phase}): bounce plans through external refinement" --files "${PHASE_DIR}/*-PLAN.md"

Display summary:

Plan bounce complete: {survived}/{total} plans refined

Clean up: Remove all *-PLAN.pre-bounce.md backup files after the bounce step completes (whether plans survived or were restored).

13. Requirements Coverage Gate

After plans pass the checker (or checker is skipped), verify that all phase requirements are covered by at least one plan.

Skip if: phase_req_ids is null or TBD (no requirements mapped to this phase).

Step 1: Extract requirement IDs claimed by plans

# Collect all requirement IDs from plan frontmatter
PLAN_REQS=$(grep -h "requirements_addressed\|requirements:" ${PHASE_DIR}/*-PLAN.md 2>/dev/null | tr -d '[]' | tr ',' '\n' | sed 's/^[[:space:]]*//' | sort -u)

Step 2: Compare against phase requirements from ROADMAP

For each REQ-ID in phase_req_ids:

  • If REQ-ID appears in PLAN_REQS → covered ✓
  • If REQ-ID does NOT appear in any plan → uncovered ✗

Step 3: Check CONTEXT.md features against plan objectives

Read CONTEXT.md <decisions> section. Extract feature/capability names. Check each against plan <objective> blocks. Features not mentioned in any plan objective → potentially dropped.

Step 4: Report

If all requirements covered and no dropped features:

✓ Requirements coverage: {N}/{N} REQ-IDs covered by plans

→ Proceed to step 14.

If gaps found:

## ⚠ Requirements Coverage Gap

{M} of {N} phase requirements are not assigned to any plan:

| REQ-ID | Description | Plans |
|--------|-------------|-------|
| {id} | {from REQUIREMENTS.md} | None |

{K} CONTEXT.md features not found in plan objectives:
- {feature_name} — described in CONTEXT.md but no plan covers it

Options:
1. Re-plan to include missing requirements (recommended)
2. Move uncovered requirements to next phase
3. Proceed anyway — accept coverage gaps

If TEXT_MODE is true, present as a plain-text numbered list (options already shown in the block above). Otherwise use AskUserQuestion to present the options.

13a. Decision Coverage Gate

After the requirements coverage gate passes, verify that every trackable decision captured by discuss-phase in CONTEXT.md <decisions> is referenced by at least one plan. This is the translation gate from issue #2492 — its job is to refuse to mark a phase planned when a discuss-phase decision silently dropped on the way into the plans.

Skip if workflow.context_coverage_gate is explicitly set to false (absent key = enabled). Also skip if no CONTEXT.md exists for this phase (nothing to translate) or if its <decisions> block is empty.

GATE_CFG=$(gsd-sdk query config-get workflow.context_coverage_gate 2>/dev/null || echo "true")
if [ "$GATE_CFG" != "false" ]; then
  GATE_RESULT=$(gsd-sdk query check.decision-coverage-plan "${PHASE_DIR}" "${CONTEXT_PATH}")
  # BLOCKING: refuse to mark phase planned when a trackable decision is uncovered.
  # `passed: true` covers both real-pass and skipped cases (gate disabled / no CONTEXT.md /
  # no trackable decisions). Verify-phase counterpart deliberately omits this exit-1 — that
  # gate is non-blocking by design (review finding F15).
  echo "$GATE_RESULT" | jq -e '.data.passed == true' >/dev/null || {
    echo "$GATE_RESULT" | jq -r '.data.message'
    exit 1
  }
fi

The handler returns JSON:

{
  "passed": true,
  "skipped": false,
  "total":  2,
  "covered": 2,
  "uncovered": [ { "id": "D-01", "text": "...", "category": "..." } ],
  "message": "..."
}

If passed is true (or skipped is true): Display ✓ Decision coverage: {M}/{N} CONTEXT.md decisions covered by plans (or (skipped — gate disabled) / (skipped — no decisions)) and proceed to step 13b.

If passed is false: Display the handler's message block. It already names each uncovered decision (D-NN | category | text) and tells the user what to do — cite the id in a relevant plan's must_haves / truths, or move the decision under ### Claude's Discretion / tag it [informational] if it should not be tracked. Then offer:

Options:
1. Re-plan to cover missing decisions (recommended)
2. Edit CONTEXT.md to mark dropped decisions as [informational] / Discretion
3. Proceed anyway — accept the coverage gap

If TEXT_MODE is true, present as a plain-text numbered list. Otherwise use AskUserQuestion. Selecting "Proceed anyway" continues to step 13b but records the override in STATE.md so verify-phase can re-surface it.

Why this gate blocks: failing here is cheap. The plans are the contract between discuss-phase and execute-phase; if a decision isn't visible in any plan, no executor will implement it. Catching that now beats discovering it after thousands of dollars of execution.

13b. Record Planning Completion in STATE.md

After plans pass all gates, record that planning is complete so STATE.md reflects the new phase status:

gsd-sdk query state.planned-phase --phase "${PHASE_NUMBER}" --name "${PHASE_NAME}" --plans "${PLAN_COUNT}"

This updates STATUS to "Ready to execute", sets the correct plan count, and timestamps Last Activity.

13c. Annotate ROADMAP with Wave Dependencies and Cross-cutting Constraints

After plans are finalized, annotate the ROADMAP.md plan list for this phase with:

  • Wave dependency notes — a bold header before each wave group ("Wave 2 (blocked on Wave 1 completion)")
  • Cross-cutting constraints — a "Cross-cutting constraints:" subsection listing must_haves.truths entries that appear in 2 or more plans

This step is derived entirely from existing PLAN frontmatter — no extra LLM pass is required.

gsd-sdk query roadmap.annotate-dependencies "${PHASE_NUMBER}"

This operation is idempotent: if wave headers or cross-cutting constraints already exist in the ROADMAP phase section, the command returns without modifying the file. Skip this step if plan_count is 0.

13d. Commit Plans if commit_docs is true

If commit_docs is true (from the init JSON parsed in step 1), commit the generated plan artifacts (including any ROADMAP.md annotations from step 13c):

gsd-sdk query commit "docs(${PADDED_PHASE}): create phase plan" --files "${PHASE_DIR}"/*-PLAN.md .planning/STATE.md .planning/ROADMAP.md

This commits all PLAN.md files for the phase plus the updated STATE.md and ROADMAP.md to version-control the planning artifacts. Skip this step if commit_docs is false.

13e. Post-Planning Gap Analysis

After all plans are generated, committed, and the Requirements Coverage Gate (§13) has run, emit a single unified gap report covering both REQUIREMENTS.md and the CONTEXT.md <decisions> section. This is a proactive, post-hoc report — it does not block phase advancement and does not re-plan. It exists so that any requirement or decision that slipped through the per-plan checks is surfaced in one place before execution begins.

Skip if: workflow.post_planning_gaps is false. Default is true.

POST_PLANNING_GAPS=$(gsd-sdk query config-get workflow.post_planning_gaps --default true 2>/dev/null || echo true)
if [ "$POST_PLANNING_GAPS" = "true" ]; then
  node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" gap-analysis --phase-dir "${PHASE_DIR}"
fi

(gsd-tools.cjs gap-analysis reads .planning/REQUIREMENTS.md, ${PHASE_DIR}/CONTEXT.md, and ${PHASE_DIR}/*-PLAN.md, then prints a markdown table with one row per REQ-ID and D-ID. Word-boundary matching prevents REQ-1 from being mistaken for REQ-10.)

Output format (deterministic; sorted REQUIREMENTS.md → CONTEXT.md, then natural sort within source):

## Post-Planning Gap Analysis

| Source | Item | Status |
|--------|------|--------|
| REQUIREMENTS.md | REQ-01 | ✓ Covered |
| REQUIREMENTS.md | REQ-02 | ✗ Not covered |
| CONTEXT.md | D-01 | ✓ Covered |
| CONTEXT.md | D-02 | ✗ Not covered |

⚠ N items not covered by any plan

Skip-gracefully behavior:

  • REQUIREMENTS.md missing → CONTEXT-only report.
  • CONTEXT.md missing → REQUIREMENTS-only report.
  • Both missing or <decisions> block missing → "No requirements or decisions to check" line, no error.

This step is non-blocking. If items are reported as not covered, the user may re-run /gsd-plan-phase --gaps to add plans, or proceed to execute-phase as-is.

14. Present Final Status

Route to <offer_next> OR auto_advance depending on flags/config.

15. Auto-Advance Check

Check for auto-advance trigger using values already loaded in step 1:

  1. Parse --auto and --chain flags from $ARGUMENTS
  2. Use auto_chain_active and auto_advance from the INIT JSON parsed in step 1 — do not issue additional config-get calls for these values (they are already present in the init output). Issuing redundant config-get calls for values already in INIT can cause infinite read loops on some runtimes.
  3. Sync chain flag with intent — if user invoked manually (no --auto and no --chain), clear the ephemeral chain flag from any previous interrupted --auto chain. This does NOT touch workflow.auto_advance (the user's persistent settings preference):
    if [[ ! "$ARGUMENTS" =~ --auto ]] && [[ ! "$ARGUMENTS" =~ --chain ]]; then
      gsd-sdk query config-set workflow._auto_chain_active false || true
    fi
    

Set local variables from INIT (parsed once in step 1):

  • AUTO_CHAIN = auto_chain_active from INIT JSON (boolean, default false)
  • AUTO_CFG = auto_advance from INIT JSON (boolean, default false)

If --auto or --chain flag present AND AUTO_CHAIN is not true: Persist chain flag to config (handles direct invocation without prior discuss-phase):

if ([[ "$ARGUMENTS" =~ --auto ]] || [[ "$ARGUMENTS" =~ --chain ]]) && [[ "$AUTO_CHAIN" != "true" ]]; then
  gsd-sdk query config-set workflow._auto_chain_active true
fi

If --auto or --chain flag present OR AUTO_CHAIN is true OR AUTO_CFG is true:

Display banner:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 GSD ► AUTO-ADVANCING TO EXECUTE
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Plans ready. Launching execute-phase...

Launch execute-phase using the Skill tool to avoid nested Task sessions (which cause runtime freezes due to deep agent nesting):

Skill(skill="gsd-execute-phase", args="${PHASE} --auto --no-transition ${GSD_WS}")

The --no-transition flag tells execute-phase to return status after verification instead of chaining further. This keeps the auto-advance chain flat — each phase runs at the same nesting level rather than spawning deeper Task agents.

Handle execute-phase return:

  • PHASE COMPLETE → Display final summary:
    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
     GSD ► PHASE ${PHASE} COMPLETE ✓
    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
    
    Auto-advance pipeline finished.
    
    Next: /gsd-discuss-phase ${NEXT_PHASE} --auto ${GSD_WS}
    
  • GAPS FOUND / VERIFICATION FAILED → Display result, stop chain:
    Auto-advance stopped: Execution needs review.
    
    Review the output above and continue manually:
    /gsd-execute-phase ${PHASE} ${GSD_WS}
    

If neither --auto nor config enabled: Route to <offer_next> (existing behavior).

<offer_next> Output this markdown directly (not as a code block):

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ GSD ► PHASE {X} PLANNED ✓ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Phase {X}: {Name} — {N} plan(s) in {M} wave(s)

Wave Plans What it builds
1 01, 02 [objectives]
2 03 [objective]

Research: {Completed | Used existing | Skipped} Verification: {Passed | Passed with override | Skipped}

───────────────────────────────────────────────────────────────

▶ Next Up — [${PROJECT_CODE}] ${PROJECT_TITLE}

Execute Phase {X} — run all {N} plans

/clear then:

/gsd-execute-phase {X} ${GSD_WS}

───────────────────────────────────────────────────────────────

Also available:

  • cat .planning/phases/{phase-dir}/*-PLAN.md — review plans
  • /gsd-plan-phase {X} --research — re-research first
  • /gsd-review --phase {X} --all — peer review plans with external AIs
  • /gsd-plan-phase {X} --reviews — replan incorporating review feedback

─────────────────────────────────────────────────────────────── </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:
    # 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:
    # 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

</windows_troubleshooting>

<success_criteria>

  • .planning/ directory validated
  • Phase validated against roadmap
  • Phase directory created if needed
  • CONTEXT.md loaded early (step 4) and passed to ALL agents
  • Research completed (unless --skip-research or --gaps or exists)
  • gsd-phase-researcher spawned with CONTEXT.md
  • Existing plans checked
  • gsd-planner spawned with CONTEXT.md + RESEARCH.md
  • Plans created (PLANNING COMPLETE or CHECKPOINT handled)
  • gsd-plan-checker spawned with CONTEXT.md
  • Verification passed OR user override OR max iterations with user decision
  • User sees status between agent spawns
  • User knows next steps </success_criteria>