Deep plan
This skill should be used when the user asks to 'deep plan', 'plan a phase deeply', 'run deep-plan', or wants CE-quality implementation planning after /gsd-discuss-phase. Bridges GSD context with code-grounded research, implementation units, test scenarios, and optional feasibility review. Produces GSD-compatible PLAN.md files.From its SKILL.md
npx -y skills add schylerchase/deep-plan-plugin --skill deep-planAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
40.7 KB, as published. Nobody here has run it
Deep Plan: GSD Context + CE Implementation Planning
<purpose> Bridge GSD's strategic context (CONTEXT.md, RESEARCH.md, ROADMAP.md) with CE's code-grounded research and implementation-unit planning. Produces a GSD-compatible PLAN.md with CE-quality detail: implementation units with file paths, test scenarios, risk analysis, and optional feasibility validation.Use this instead of /gsd-plan-phase when you want deeper code analysis before planning.
</purpose>
<when_to_use>
- After
/gsd-discuss-phasehas produced a CONTEXT.md for the target phase - When the phase involves significant code changes (extraction, refactoring, new features)
- When you want CE-style implementation units with test scenarios
- When you want optional feasibility review to catch deployment/build issues </when_to_use>
Step 1 detects caveman and builds an override map for three v2 signals forcing full prose (see references/caveman-rule.md).
Caveman: optional plugin by Julius Brussee (MIT), installed separately. </compatibility>
<process><progress_protocol>
Progress Reporting Protocol
Deep-plan must be visually distinguishable from GSD and CE throughout execution. Follow these patterns at every step.
Opening banner — Display after phase is confirmed (end of Step 2). Open-bordered style matches GSD (no right border to misalign with long phase names):
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Deep Plan — Phase {N}: {phase_name}
GSD context → CE research → Implementation plan
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Step headers — Display at the start of every step:
── deep-plan [{current}/{total}] {Step Name} ──────────
Total = 12 with --review, 11 without (feasibility review skipped). Both fractional steps (3.5 already-done check and 9.5 routing decision) are each counted as one task; their announce headers read [3.5/{total}] and [9.5/{total}]. Steps that announce headers: 3, 3.5, 4, 5, 6, 7, 8, 9, 9.5, 10, 11, 12 (skip 11 when --review is absent). Step 1 (Caveman Setup) and Step 2 (Parse Arguments) do their setup work silently and do not announce.
Detail lines — 1-2 lines after each header showing what was found/done. Use ✓/✗ for availability.
Task tracking — After phase confirmation, use TaskCreate to create all step tasks upfront (prefixed "Deep Plan:"). Mark each in_progress when starting, completed when done.
Subagent attribution — Before CE agent calls, state what deep-plan pre-fed and what CE will focus on. After return, summarize new findings count.
See references/progress-templates.md for full output examples per step.
</progress_protocol>
Detect caveman deterministically — do not guess from context:
CAVEMAN_INSTALLED=$(claude plugin list 2>/dev/null | grep -q "caveman@caveman" && echo yes || echo no)
If CAVEMAN_INSTALLED=yes, read references/caveman-rule.md and build an override map for four v2 signals requiring full prose: HIGH feasibility findings, AskUserQuestion blocks, mid-flight scope pivots, and routing-decision banner output (Step 9.5). If no, skip silently — no override map, no further caveman references this run.
Config resolution
Resolve deep_plan.model_routing config — once per run, fall back to defaults silently when absent and with a banner notice when malformed:
PLANNING_EXISTS=$(test -d .planning && echo yes || echo no)
GSD_TOOLS_EXISTS=$(test -f "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" && echo yes || echo no)
CONFIG_HAS_MODEL_ROUTING=$(node -e "
const fs = require('fs');
const path = process.cwd() + '/.planning/config.json';
try {
if (!fs.existsSync(path)) {
process.stdout.write('no');
process.exit(0);
}
const c = JSON.parse(fs.readFileSync(path, 'utf8'));
const block = c?.deep_plan?.model_routing;
process.stdout.write(block && typeof block === 'object' && !Array.isArray(block) ? 'yes' : 'no');
} catch (e) {
process.stdout.write('error');
}
" 2>/dev/null)
CONFIG_READ=$(node -e "
const fs = require('fs');
const path = process.cwd() + '/.planning/config.json';
const typeOf = (x) => x === null ? 'null' : (Array.isArray(x) ? 'array' : typeof x);
try {
if (!fs.existsSync(path)) {
console.log('{}');
console.log('');
process.exit(0);
}
const c = JSON.parse(fs.readFileSync(path, 'utf8'));
const block = c?.deep_plan?.model_routing ?? {};
if (block && typeof block === 'object' && !Array.isArray(block)) {
console.log(JSON.stringify(block));
console.log('');
} else {
console.log('{}');
console.log('config: used defaults for deep_plan.model_routing (malformed: expected object, got ' + typeOf(block) + ')');
}
} catch (e) {
console.log('{}');
console.log('config: malformed JSON, using all defaults');
}
" 2>/dev/null)
CONFIG_BLOCK=$(printf '%s\n' "$CONFIG_READ" | sed -n '1p')
CONFIG_READ_NOTICE=$(printf '%s\n' "$CONFIG_READ" | sed -n '2p')
If PLANNING_EXISTS=yes, GSD_TOOLS_EXISTS=yes, and CONFIG_HAS_MODEL_ROUTING=no, the model_routing block is missing. Read references/setup-wizard.md for: the inline missing-config trigger, the six-question first-run flow, text-mode fallback prompts, GSD profile verification, structured JSON write helper, and the exact deep_plan.model_routing schema to write. Run the inline setup wizard before resolving config for the rest of this deep-plan invocation. After the wizard writes .planning/config.json, re-run the CONFIG_READ snippet above so CONFIG_BLOCK reflects the newly written block.
If PLANNING_EXISTS=no or GSD_TOOLS_EXISTS=no, skip the inline wizard entirely and let Step 2's prerequisite gate print the existing actionable error. Do not create .planning/config.json outside an initialized GSD project.
If CONFIG_HAS_MODEL_ROUTING=error, do not run the wizard automatically. Preserve Phase 9 lenient behavior: use all defaults, keep CONFIG_READ_NOTICE, and point the user at /deep-plan-doctor for repair.
Read references/config.md for: the JSON schema, default values sourced from references/scoring.md, the deep-merge rules per D-08, lenient field-level fallback narrative per D-09, and the resolved-config object shape per D-12.
Apply the resolution algorithm from references/config.md: deep-merge CONFIG_BLOCK into the defaults sourced from references/scoring.md; include CONFIG_READ_NOTICE first when present; for each malformed field or out-of-range numeric override, fall back to the default and append a one-line banner notice (cap at 3 lines, summarize remainder as +N more, see /deep-plan-doctor). Compute _source per the rule in references/config.md ## Resolved-Config Object Shape: "defaults" when no schema leaves came from config, "config" when every schema leaf was provided and validated cleanly, "merged" when the user provided some fields and defaults backfilled the rest (or any field was rejected via lenient fallback).
Hold the resolved-config object in skill scope as resolved_config. Downstream steps consume it:
- Step 9.5 reads
resolved_config.biasfor the threshold map. - Step 9.5 reads
resolved_config.weight_overrides.{formula, signals}andresolved_config.context_thresholdsfor any user-overridden scoring math. - Phase 11 (future) reads
resolved_config.gsd_profile_at_setupfor PLAN.md frontmatter. - Phase 12 (future) doctor reads
resolved_config.gsd_profile_at_setupto detect drift against current GSD profile.
When resolved_config.pin is non-null, Step 9.5 bypasses scoring entirely (D-02) — the routing banner reads model={pin} pinned=true (scoring skipped) instead of the score breakdown.
Banner notices (one line per malformed field, max 3) ride the existing routing-decision banner exemption from caveman compression — no new caveman v2 signal is needed.
Enforced at: questions (Steps 2, 7), pivots (Step 8), routing decision (Step 9.5), feasibility (Step 11). </step>
<step name="parse_args"> ## Step 2: Parse Arguments and Auto-Detect PhaseBefore anything else in this step, check prerequisites:
Run three checks via Bash (run all three, collect failures, report together):
- GSD installed:
test -f "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" - CE installed:
claude plugin list 2>&1 | grep -q compound-engineering .planning/exists:test -d .planning/
If any failed, print every failure with its fix command.
Error format (only print lines for missing prerequisites):
deep-plan requires GSD. Install: see GSD Discord #getting-started
deep-plan requires Compound Engineering. Install: claude plugin marketplace add EveryInc/compound-engineering-plugin && claude plugin install compound-engineering
No .planning/ directory found. Run /gsd-new-project to initialize.
First-run recovery prompt
If GSD is missing, do not hard-stop immediately. GSD is commonly installed in another shell while the Claude Code session remains open.
Ask:
GSD is required before deep-plan can read phase context.
1. I installed GSD — retry checks
2. Show install steps and stop
3. Run /deep-plan-doctor --install after I fix it
When AskUserQuestion is available, use equivalent options:
- "I installed GSD — retry checks" — re-run all three prerequisite checks
- "Show install steps and stop" — print the GSD Discord install note and stop
- "Run doctor after install" — print
Run: /deep-plan-doctor --installand stop
If the user chooses retry, re-run all three checks. If they now pass, continue silently. If GSD is still missing, show the same recovery prompt again, up to 3 retry attempts, then stop with the install note.
If CE or .planning/ is missing but GSD is present, stop after printing the missing prerequisite lines; those fixes have their own explicit commands/workflows and should not be retried inside /deep-plan.
If all pass, continue silently.
Extract from the invocation:
phase— the phase number (e.g., "18", "11.1")--reviewflag — run feasibility review after planning--skip-researchflag — skip CE repo-research-analyst--textflag — use plain-text numbered lists instead of AskUserQuestion
Text mode detection (runs once, applies to all prompts):
Text mode is active if either condition is true:
--textflag was passed in argumentsworkflow.text_modeistruein GSD config:TEXT_MODE=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config-get workflow.text_mode 2>/dev/null)
Store result as text_mode (boolean). When active, all user prompts in this session use plain-text numbered lists instead of AskUserQuestion.
If no phase argument provided, auto-detect:
ROADMAP_STATE=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" roadmap analyze 2>/dev/null)
Find the best candidate phase by priority:
- Has CONTEXT.md but no PLAN.md — ready for deep-plan now
- No CONTEXT.md, no PLAN.md — needs discuss-phase first
If no candidate matches either priority, all phases already have plans:
All phases in roadmap have plans. Nothing to plan. Run /gsd-add-phase to add new work.
Stop execution.
Detection priority — why this order:
Has CONTEXT.md but no PLAN.mdwins because the user explicitly ran discuss-phase to lock decisions; the next step is planning, and that's exactly what deep-plan does.No CONTEXT.md, no PLAN.mdis the fallback for phases that need discuss-phase first; deep-plan suggests running it rather than planning blind.- Phases with both CONTEXT.md and PLAN.md are skipped — they're already planned. Use
--reviewor/gsd-execute-phaseto proceed. - The confirmation prompt below always runs after auto-detect; the user can override the choice with "Different phase".
Scope-mismatch check (runs when phase was auto-detected):
Compute task_hint: any free-text args not consumed by phase number or known flags (--review, --skip-research, --text). Empty string if none.
Fire the scope-check if the phase was auto-detected AND (task_hint is non-empty OR the detected phase has no CONTEXT.md). Skip it otherwise (explicit phase arg, or happy path with CONTEXT.md and no hint) — fall straight through to the existing three-option prompt below.
If text_mode is active, present as a plain-text numbered list:
Detected Phase {N}: {phase_name}. Your request mentions: "{task_hint}"
How should this be scoped?
1. Plan Phase {N} as detected
2. Expand Phase {N} scope to include this work (runs /gsd-discuss-phase)
3. Insert a decimal hotfix phase like {N}.1 (runs /gsd-add-phase)
4. Route to /gsd-quick (tactical fix, no phase needed)
Type a number to choose. Parse response (number or free text). If invalid, re-prompt.
Otherwise, use AskUserQuestion with four options mirroring the list above.
Routing:
- Option 1 → fall through to the existing three-option confirmation below
- Option 2 → print
Run: /gsd-discuss-phase {N} "{task_hint}"and stop (no banner, no TaskCreate, no state writes) - Option 3 → print
Run: /gsd-add-phase(decimal hotfix flow) and stop - Option 4 → print
Run: /gsd-quick "{task_hint}"and stop
If text_mode is active, present as a plain-text numbered list instead of AskUserQuestion:
1. Yes, plan Phase {N}
2. Different phase
3. Run /gsd-discuss-phase {N} first
Type a number to choose:
Parse the user's response (number or free text describing their choice). If invalid, re-prompt.
Otherwise, use AskUserQuestion with options:
- "Yes, plan Phase {N}" — proceed with detected phase
- "Different phase" — ask which one
- If no CONTEXT.md: "Run /gsd-discuss-phase {N} first" — launch discuss-phase, then return
After phase confirmed:
- Display the opening banner (see progress protocol)
- TaskCreate one task per remaining step (prefix "Deep Plan:"), e.g., "Deep Plan: Load GSD context"
- Total tasks = 11 without
--review, 12 with--review. The denominator counts every step that announces a[N/{total}]header: 3, 3.5, 4, 5, 6, 7, 8, 9, 9.5, 10, 12 (11 entries) without--review; add 11 (feasibility review) for the 12-entry--reviewtotal. Step 1 (Caveman Setup) and Step 2 (Parse Arguments) run silently and are not counted. </step>
Announce: ── deep-plan [3/{total}] Loading GSD context ──
After reading, detail: CONTEXT.md {✓/✗} | RESEARCH.md {✓/✗} | ROADMAP.md {✓/✗}
INIT=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" init plan-phase "$PHASE" 2>/dev/null)
Parse the JSON output for: phase_dir, padded_phase, phase_slug, has_context, has_research, context_path, research_path.
Required: Read CONTEXT.md from the phase directory. If CONTEXT.md does not exist:
Phase {N} has no CONTEXT.md. Run `/gsd-discuss-phase {N}` first to gather
implementation decisions, then re-run `/deep-plan {N}`.
Stop.
Optional reads (if they exist):
- RESEARCH.md from the phase directory
- The phase section from
.planning/ROADMAP.md(goals, requirements, success criteria) .planning/REQUIREMENTS.mdfor requirement definitions
Check for existing plans: If PLAN.md files already exist for this phase:
If text_mode is active, present as a plain-text numbered list instead of AskUserQuestion:
1. Add another plan (increment plan number)
2. Replan from scratch (overwrite)
3. Cancel
Type a number to choose:
Parse the user's response (number or free text describing their choice). If invalid, re-prompt.
Otherwise, use AskUserQuestion with options:
- "Add another plan" (increment plan number)
- "Replan from scratch" (overwrite)
- "Cancel" </step>
Announce: ── deep-plan [3.5/{total}] Checking if work is already done ──
This step skips planning when the phase's success criteria are already satisfied by existing code (LIFE-02). Saves users from re-planning work that was implemented ad-hoc via /quick or other entry points.
Skip conditions:
- If
has_plans=truefrom Step 2's init data → skip silently (already planned). - If criterion list from
roadmap get-phaseis empty → skip silently (nothing to check against).
Evidence gathering (per criterion):
- Extract searchable tokens from each criterion text (file paths, function names, behavior keywords).
- Run lightweight checks: grep for tokens, Read referenced files, ls expected paths.
- Tag each criterion:
met(all evidence found),partial(some evidence),not met(no evidence).
Routing:
- All criteria
met: present summary table. Ask via AskUserQuestion (or numbered list in text_mode):- "All success criteria for Phase {N} appear satisfied by existing code. Mark phase complete and exit?"
- Options:
Yes, mark complete/No, plan anyway (work may be redundant)/Show evidence first - On
Yes: Edit.planning/ROADMAP.mdto flip[ ]→[x]for the phase line. Log:[step 3.5] Phase {N} marked complete based on existing code evidence. Exit deep-plan with handoff message. - On
No: continue to Step 4. - On
Show evidence first: print the evidence table, then re-prompt with first two options only.
- Any
partialornot met: log[step 3.5] {met}/{total} criteria show evidence — continuing to planand proceed to Step 4. Do not prompt.
Caveman override: the AskUserQuestion in this step is a v2 signal — output the summary table and prompt in full prose regardless of caveman level. (Step 1 override map.) </step>
<step name="gather_intel"> ## Step 4: Gather GSD IntelligenceAnnounce: ── deep-plan [4/{total}] Gathering codebase intelligence ──
After gathering, detail: Intel: {found_list} ({N}/5 files, {fresh/stale/none}) | Research: {found_list} ({warm/cold} start)
Read references/intel-sources.md for the full source list (intel/ files, research/ files, scope boundaries), staleness rules, the no-analysis fallback prompt, and the gsd_knowledge composition spec.
Check each source in order. Read what exists, skip what doesn't. If neither .planning/intel/ nor .planning/research/ exist, prompt the user per the reference (Continue anyway / Run /gsd-scan first).
After gathering, build the gsd_knowledge block per the schema in the reference. This block feeds Step 6 (CE warm-start prompt).
</step>
Announce: ── deep-plan [5/{total}] Building planning brief ──
After composing, detail: Locked decisions: {N} | Open questions: {N} | Seed files: {N}
Transform GSD artifacts into a structured planning brief. This brief serves two purposes: (1) it becomes context for CE research, and (2) it frames the implementation units.
From CONTEXT.md:
<domain>section → Problem frame and scope boundaries<decisions>locked items → Resolved questions (carry forward as-is)<decisions>"Claude's Discretion" items → Open questions for research to inform<code_context>→ Seed files for repo-research-analyst<specifics>→ Constraints and examples<deferred>→ Explicit non-goals (scope boundaries)
From phase RESEARCH.md (if exists):
- Technical approach → Pre-existing research (do not re-research these topics)
- Risk analysis → Known risks to carry into the plan
From ROADMAP.md phase section:
- Phase goal → Objective
- Success criteria → Requirements trace
- Dependencies → Prerequisites
Compose a 2-3 paragraph planning brief summarizing:
- What the phase delivers and why
- User decisions that are locked
- Areas where code research is needed (subtract what GSD intel already covers)
- Files and patterns already identified </step>
Announce: ── deep-plan [6/{total}] CE deep research ({warm/cold} start) ──
- Warm:
Pre-fed: {summary} → CE focusing on: integration points, gaps, risks - Cold:
No GSD intel to pre-feed → CE exploring from scratch
If --skip-research was passed, skip to Step 7.
Decide warm vs cold: if gsd_knowledge block from Step 4 is non-empty → warm-start. Otherwise → cold-start.
Read references/ce-prompts.md for the full Task spawn body (warm + cold variants), confidence-tag rules, merge rules, and post-CE announce templates. Substitute {N}, {phase_name}, {planning_brief}, {gsd_knowledge block from Step 3}, and {files from code_context} from in-memory state, then spawn the agent.
After CE returns: apply the confidence tags + merge rules from the reference. Display the post-CE announce line per warm/cold variant. </step>
<step name="resolve_questions"> ## Step 7: Resolve Planning QuestionsAnnounce: ── deep-plan [7/{total}] Resolving planning questions ──
After resolving, detail: Auto-resolved: {N} | Asking user: {N} | Deferred: {N}
Build a question list from:
- "Claude's Discretion" items from CONTEXT.md that research can now inform
- Gaps discovered by repo-research-analyst
- Technical decisions needed for implementation units
For each question:
- If research provides a clear answer → resolve silently
- If the answer materially affects scope, architecture, or risk → ask the user (see below)
- If the answer depends on runtime behavior → defer to implementation
If text_mode is active, present each question as a plain-text numbered list instead of AskUserQuestion. For fixed-option questions:
{Question text}
1. {option 1}
2. {option 2}
...
Type a number to choose:
For open-ended questions, prompt for free text input. Parse the user's response (number or free text describing their choice). If invalid, re-prompt.
Otherwise, use AskUserQuestion for each question with the appropriate options.
Keep user questions focused and minimal (1-2 max). Don't ask about things the user already decided in CONTEXT.md.
Caveman override: If override map active (Step 1), output question blocks in full prose. </step>
<step name="structure_units"> ## Step 8: Structure Implementation UnitsAnnounce: ── deep-plan [8/{total}] Structuring implementation units ──
After structuring, detail: Units: {N} | Test scenarios: {N} | Must-haves: {truths}/{artifacts}/{links}
Break the phase work into implementation units. Each unit represents one meaningful atomic change.
For each unit, define:
- Name — clear, action-oriented
- Goal — what this unit accomplishes
- Requirements — which ROADMAP requirements or success criteria it advances
- Dependencies — what must exist first (other units or external)
- Files — repo-relative paths to create, modify, or test
- Approach — key decisions, data flow, integration notes (not code). Carry forward CE confidence tags on any findings referenced (e.g., "integration via event bus [HIGH]" or "likely uses shared state [MEDIUM]")
- Patterns to follow — existing code to mirror (from research findings, with confidence tag)
- Test scenarios — specific test cases by category:
- Happy path: core functionality with expected inputs/outputs
- Edge cases: boundary values, empty inputs, concurrent access
- Error paths: invalid input, failures, timeouts
- Integration: cross-layer scenarios mocks won't prove
- Verification — how to know the unit is complete (outcomes, not commands)
Generate GSD must_haves from test scenarios:
truths→ one truth per critical test scenario ("User can X", "Y produces Z")artifacts→ one artifact per created/modified file with a searchablecontainstokenkey_links→ traceability from source to destination with search patterns
Ordering: Dependencies first. Pure functions/constants before stateful units (hooks, classes, services) before composition layers (components, handlers, modules). Infrastructure before features.
Caveman override: If scope pivot detected (Step 1 override map), output pivot reasoning in full prose. </step>
<step name="write_plan"> ## Step 9: Write GSD PLAN.mdRead references/plan-template.md for: numeric plan-numbering rules, the file-path convention, substitution-variable table, and the full PLAN.md template body (frontmatter + objective + execution_context + context + tasks + threat_model + verification + success_criteria + output).
Substitute live values per the variable table in the reference. Write to .planning/phases/{padded_phase}-{slug}/{padded_phase}-{MM}-PLAN.md.
For executor_model and model_recommendation placeholders, write the frontmatter block in the template shape but leave routing values for Step 9.5 to fill. Step 9.5 owns the routing decision and must replace those placeholders before Step 10 validation.
Path-portability rule: tilde paths only (@~/...); never @$HOME/... (PORT-01).
Announce: ── deep-plan [9/{total}] Plan written ──
Detail: .planning/phases/{phase_dir}/{padded_phase}-{MM}-PLAN.md — Units: {N} | Test scenarios: {N} | Must-haves: {truths} truths, {artifacts} artifacts, {links} links
</step>
Read references/scoring.md for: signal extraction heuristics, the three perspective formulas, the byte-ratio table, the threshold map, the half-up rounding helper with Number.EPSILON correction, the banner format with machine-readable trailer, and the worked-examples / golden-fixture index.
This step always runs, including when --skip-research was passed. Missing CE-derived signals fall back to 0 per D-08 — novel and unknown_deps default to 0 when feasibility/research output is absent, and the banner appends (reduced confidence: --skip-research used).
Extract the 8 signals from in-memory pipeline state:
- Read the
unitsarray produced by Step 8 and themust_havesobject produced by Step 8. - For each signal in
references/scoring.md## Signal Extraction:- Compute the auto value per the heuristic listed for that signal.
- For
files_modified, deduplicate paths across allunit.filesarrays (D-15) — same file referenced by 3 units counts once. - For
checkpoints, parse CONTEXT.md for the<questions>block. If absent, default to 0 and remember to annotate the banner.
- Read CONTEXT.md once for the optional
<signals>override block (D-13). Parse the block by locating<signals>and</signals>markers in the body (NOT in frontmatter) and iterating flatkey: valuelines between them — DO NOT reuse the frontmatter-onlyyaml_getfromtests/eval-caveman-rule.shortests/eval-scoring.sh. That helper is gated on---fences (it tracks a frontmatter state machine and exits at the second---), so it returns empty for any key inside a body-level<signals>block. Use a dedicated extractor: capture lines between the<signals>and</signals>markers, then split each line on the first:and trim. Validate each value matches^[0-9]+$(non-negative integer); reject non-numeric or negative values per signal and remember to annotate the banner with(<signals> override rejected for {key}: not a non-negative integer). - Merge: each key in
<signals>REPLACES the auto value; missing keys leave the auto value in place.
Compute the three perspectives and the combined score per the formulas in references/scoring.md. Do NOT inline the formulas here — call them out by name (volume, structure, risk, combined) and trust the reference doc for the math.
Apply the threshold map from references/scoring.md to the rounded combined score to determine the recommended model. Use >= comparison (locked rule from Pitfall 4 mitigation). Read resolved_config.bias from the resolved-config object set in Step 1 (per references/config.md ## Resolved-Config Object Shape) — defaults to balanced when config is absent (_source: "defaults").
Estimate input tokens by walking the deduplicated files_modified set, calling wc -c (or equivalent) on each path, and dividing by the per-extension byte ratio from the table in references/scoring.md. Sum the per-file estimates. The default ratio for unknown extensions is 3.0 — conservative high, biases toward triggering the advisory rather than missing it.
Determine the phase-split advisory: strict AND gate (D-01) — input_tokens > 180000 AND combined >= opus_thresholds[bias]. Determine the borderline hint (D-12): if combined is within ±10% of either threshold, prepare the appropriate bias-bump suggestion.
Build the in-memory routing-decision object (D-06) with snake_case field names per the convention in references/scoring.md:
volume_score,structure_score,risk_score,combined_score— rounded to 1 decimal via the half-up helperrecommended_model— one ofopus,sonnet,haikubias— the active bias used for threshold mappingthreshold— the threshold value compared against (e.g., 12 for opus/balanced)input_tokens_estimate— integeradvisory— booleanborderline_hint— string or nullreduced_confidence— boolean (true when--skip-researchwas passed)executor_model— the model downstream executors should use after pins and auto-mode budget caps are appliedselection_reason— one ofrecommended,pinned,gsd_profile_cap, orno_gsd_profilesignals— the merged signal values (post-override)signal_overrides_rejected— array of any<signals>keys whose values were rejected for invalidity
Determine executor_model from the same routing decision object:
- If
resolved_config.pinis non-null, setexecutor_model = resolved_config.pinandselection_reason = "pinned". - Else if
resolved_config.mode = "auto", read the current GSD profile:
Apply the budget-cap map exactly:GSD_PROFILE=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config-get workflow.profile 2>/dev/null)quality -> opus,balanced -> sonnet,budget -> sonnet.- If
GSD_PROFILEis empty, keepexecutor_model = recommended_modeland setselection_reason = "no_gsd_profile". - If
recommended_modelis above the mapped cap, setexecutor_modelto the cap andselection_reason = "gsd_profile_cap". - If
recommended_modelis at or below the cap, keepexecutor_model = recommended_modelandselection_reason = "recommended".
- If
- Else set
executor_model = recommended_modelandselection_reason = "recommended".
Use model ordering haiku < sonnet < opus for cap comparison. A cap never upgrades a lower recommendation.
Hold this object in scope. Do NOT write it to a sidecar file (D-06). Immediately update the PLAN.md written in Step 9 so its frontmatter contains:
executor_model: "{executor_model}"
model_recommendation:
recommended_model: "{recommended_model}"
executor_model: "{executor_model}"
selection_reason: "{selection_reason}"
bias: "{bias}"
threshold: {threshold}
scores:
volume: {volume_score}
structure: {structure_score}
risk: {risk_score}
combined: {combined_score}
input_tokens_estimate: {input_tokens_estimate}
advisory: {advisory}
reduced_confidence: {reduced_confidence}
The frontmatter block and the routing banner must be sourced from the same in-memory routing decision. Step 10 validation runs only after this frontmatter update.
Append routing telemetry after the frontmatter update succeeds. Use structured JSON parse/update/write on .planning/config.json; never overwrite unrelated keys. Append one object to _telemetry.decisions[] and preserve all existing entries:
{
"timestamp": "{ISO-8601 timestamp}",
"phase_id": "{padded_phase}-{slug}",
"plan_path": "{path to written PLAN.md}",
"recommended_model": "{recommended_model}",
"executor_model": "{executor_model}",
"selection_reason": "{selection_reason}",
"override_flag": {override_flag},
"scores": {
"volume": {volume_score},
"structure": {structure_score},
"risk": {risk_score},
"combined": {combined_score}
},
"bias": "{bias}",
"threshold": {threshold},
"input_tokens_estimate": {input_tokens_estimate},
"advisory": {advisory},
"reduced_confidence": {reduced_confidence}
}
If .planning/config.json is malformed, do not rewrite it. Emit a warning that telemetry was skipped and point the user at /deep-plan-doctor. Missing _telemetry or _telemetry.decisions should be created; existing decision entries must remain intact.
Emit the banner using the format specified in references/scoring.md ## Banner Format. The banner has 3 required lines plus optional advisory / borderline / reduced-confidence lines plus the machine-readable trailer comment. The full banner is reproduced below for verification (per success criterion #5 — three perspective scores visible):
── deep-plan [9.5/{total}] Routing decision ──
Volume: {V.V} | Structure: {S.S} | Risk: {R.R} → Combined: {C.C}
Recommendation: {model} ({bias} bias, threshold {T})
{advisory line if D-01 triggered}
{borderline hint if D-12 triggered}
{(reduced confidence: --skip-research used) if applicable}
<!-- DEEP_PLAN_ROUTING: model={model} combined={C.C} volume={V.V} structure={S.S} risk={R.R} bias={bias} threshold={T} advisory={true|false} -->
Phase-split advisory mode-aware behavior (D-02):
When advisory: true, behavior depends on the active mode (locked for Phase 9 to provide; default confirm until then):
If mode: auto, render the advisory line as part of the banner and continue planning uninterrupted.
If text_mode is active (per Step 2 detection), present as a plain-text numbered list:
{advisory message — D-03 diagnostic detail with input tokens, combined score, top 2 contributing signals by weighted contribution}
1. Continue with current scope
2. Stop and split this phase manually
Type a number to choose. Parse response (number or free text). If invalid, re-prompt.
Otherwise (mode: confirm, no --text flag), use AskUserQuestion with two options mirroring the list above.
If the user chooses "Stop and split this phase manually", print Run: /gsd-add-phase to split this phase and stop the deep-plan run cleanly. The PLAN.md from Step 9 has already been written — the user can revisit it after splitting.
Announce: ── deep-plan [9.5/{total}] Routing decision ──
Detail: Volume: {V} | Structure: {S} | Risk: {R} → Combined: {C} | Model: {model} ({bias} bias){advisory_suffix}
Caveman override: routing-decision banner is always full prose regardless of caveman mode (Step 1 override map). New v2 signal — see references/caveman-rule.md Signal: Routing Decision Banner.
</step>
Announce: ── deep-plan [10/{total}] Validating plan structure ──
Detail: Spawning plan-validator against PLAN.md...
This step always runs. It catches format errors that would break gsd-executor — frontmatter schema, task XML structure, must_haves validity, and @-reference resolution.
Validation lifecycle: deep-plan validates plans twice when --review is active:
- Step 10 (this step) — initial validation after first write
- Step 11 — post-revision validation if feasibility findings trigger a plan update Both stages use the same plan-validator agent and follow the same retry-on-error pattern (1 retry, then surface to user).
Read references/validation-flow.md for the full Task spawn body, FAIL/WARN/PASS result routing, and retry semantics. Substitute {path to written PLAN.md} and {project root path} from in-memory state.
Announce (after validation): ── deep-plan [10/{total}] Validation complete ──
Detail: Result: {PASS/WARN/FAIL} | Errors: {N} | Warnings: {N}
</step>
Announce (before review): ── deep-plan [11/{total}] Feasibility review ──
Detail: Launching feasibility-reviewer against PLAN.md...
If --review flag was passed:
Select the feasibility reviewer model explicitly:
feasibility_model="opus"
Explicit --review always forces the feasibility reviewer model to opus regardless of inherited model, executor_model, or GSD profile cap. Quality wins because the user opted into a feasibility pass. If a future non-explicit feasibility path is added, it may set feasibility_model="$executor_model" from the Step 9.5 routing decision instead.
Spawn the CE feasibility reviewer with the Agent tool's model parameter:
Agent(
subagent_type="compound-engineering:ce-feasibility-reviewer",
model: "$feasibility_model",
description="Feasibility review for {path to written PLAN.md}",
prompt="Review this plan for feasibility — will the proposed technical approach survive contact with reality?
Plan: {path to written PLAN.md}
Codebase: {project root}
Focus on:
1. Will the implementation order work given actual code dependencies?
2. Are there build/deployment issues the plan misses?
3. Are file paths and code references accurate?
4. Are the risks realistic and mitigations sufficient?
Be critical. Flag anything that would fail during implementation."
)
Route findings by severity:
HIGH severity findings — auto-revise:
The user already opted into --review; HIGH findings exist precisely to trigger revision. Skip the permission prompt and revise automatically.
- Log:
[step 11] HIGH finding(s) detected — auto-revising plan: {brief summary} - Update the PLAN.md with fixes. Save the original content in a
pre_revision_planvariable before the Edit call so revert is possible if validation persistently fails. - Re-run plan-validator on the revised plan to catch any cross-reference drift introduced by the revision (renamed task files, stale artifact paths, broken key_links).
- On validation FAIL: auto-fix what can be fixed, then attempt one more revision pass. If errors persist, present three options via AskUserQuestion: "Revert to pre-revision plan", "Accept errors and proceed", "Stop and fix manually".
- On validation PASS or WARN: log
[step 11] Auto-revision validated — proceeding.
MODERATE severity findings — surface as warnings, do not block:
- Display each MODERATE finding with its location and recommendation.
- Log:
[step 11] {N} MODERATE finding(s) — surfaced as warnings, not blocking. - These should be addressed during execution but do not gate plan output.
LOW severity findings — summarize briefly:
- One-line per finding for the audit trail.
- No execution-time gating.
Announce (after review): ── deep-plan [11/{total}] Feasibility review complete ──
Detail: {N} findings: {high} HIGH | {moderate} MODERATE | {low} LOW
Caveman override: If any HIGH finding present (Step 1 override map), output entire feasibility review in full prose. </step>
<step name="handoff"> ## Step 12: HandoffAnnounce: ── deep-plan [12/{total}] Complete ──
Mark all remaining Deep Plan tasks as completed.
Display summary:
## Deep Plan Complete
**Phase:** {N} — {phase_name}
**Plan:** {padded_phase}-{MM}-PLAN.md
**Units:** {count} implementation units
**Test scenarios:** {count} across all units
**Must-haves:** {truths_count} truths, {artifacts_count} artifacts, {links_count} key links
**Validation:** {PASS/WARN/FAIL} ({error_count} errors, {warning_count} warnings)
{if review: **Feasibility:** {findings_summary}}
**Next:** Run `/gsd-execute-phase {N}` to execute this plan.
</step>
</process>
<success_criteria>
- GSD CONTEXT.md was read and transformed into planning context
- GSD intel/research artifacts were gathered and composed into warm-start context (if available)
- CE repo-research-analyst received targeted prompt (warm-start or cold-start) and findings merged
- CE was NOT asked to re-discover information GSD already provided
- User was asked only material scoping questions (0-2 max)
- Implementation units have file paths, test scenarios, and verification
- Output PLAN.md has valid GSD frontmatter with must_haves
- PLAN.md is parseable by gsd-executor
- Plan-validator agent ran and returned PASS or WARN (errors were fixed before proceeding)
- Feasibility review ran (if --review) and findings presented
- Every step displayed a branded
── deep-plan [N/M]header with detail lines - TaskCreate tasks were created upfront and marked completed per step </success_criteria>