diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3fff4ff4..681804b8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -314,6 +314,15 @@ bin/install.js — Installer (multi-runtime) get-shit-done/ bin/lib/ — Core library modules (.cjs) workflows/ — Workflow definitions (.md) + Large workflows split per progressive-disclosure + pattern: workflows//modes/*.md + + workflows//templates/*. Parent dispatches + to mode files. See workflows/discuss-phase/ as + the canonical example (#2551). New modes for + discuss-phase land in + workflows/discuss-phase/modes/.md. + Per-file budgets enforced by + tests/workflow-size-budget.test.cjs. references/ — Reference documentation (.md) templates/ — File templates agents/ — Agent definitions (.md) — CANONICAL SOURCE diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 91ae0ebc..3b663afb 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -131,6 +131,33 @@ Orchestration logic that commands reference. Contains the step-by-step process i **Total workflows:** see [`docs/INVENTORY.md`](INVENTORY.md#workflows) for the authoritative count and full roster. +#### Progressive disclosure for workflows + +Workflow files are loaded verbatim into Claude's context every time the +corresponding `/gsd:*` command is invoked. To keep that cost bounded, the +workflow size budget enforced by `tests/workflow-size-budget.test.cjs` +mirrors the agent budget from #2361: + +| Tier | Per-file line limit | +|-----------|--------------------| +| `XL` | 1700 — top-level orchestrators (`execute-phase`, `plan-phase`, `new-project`) | +| `LARGE` | 1500 — multi-step planners and large feature workflows | +| `DEFAULT` | 1000 — focused single-purpose workflows (the target tier) | + +`workflows/discuss-phase.md` is held to a stricter <500-line ceiling per +issue #2551. When a workflow grows beyond its tier, extract per-mode bodies +into `workflows//modes/.md`, templates into +`workflows//templates/`, and shared knowledge into +`get-shit-done/references/`. The parent file becomes a thin dispatcher that +Reads only the mode and template files needed for the current invocation. + +`workflows/discuss-phase/` is the canonical example of this pattern — +parent dispatches, modes/ holds per-flag behavior (`power.md`, `all.md`, +`auto.md`, `chain.md`, `text.md`, `batch.md`, `analyze.md`, `default.md`, +`advisor.md`), and templates/ holds CONTEXT.md, DISCUSSION-LOG.md, and +checkpoint.json schemas that are read only when the corresponding output +file is being written. + ### Agents (`agents/*.md`) Specialized agent definitions with frontmatter specifying: diff --git a/docs/INVENTORY-MANIFEST.json b/docs/INVENTORY-MANIFEST.json index 43f5c282..b7920d0a 100644 --- a/docs/INVENTORY-MANIFEST.json +++ b/docs/INVENTORY-MANIFEST.json @@ -242,6 +242,7 @@ "project-skills-discovery.md", "questioning.md", "revision-loop.md", + "scout-codebase.md", "sketch-interactivity.md", "sketch-theme-system.md", "sketch-tooling.md", diff --git a/docs/INVENTORY.md b/docs/INVENTORY.md index c835f9f3..3bcc6fcc 100644 --- a/docs/INVENTORY.md +++ b/docs/INVENTORY.md @@ -269,7 +269,7 @@ Full roster at `get-shit-done/workflows/*.md`. Workflows are thin orchestrators --- -## References (50 shipped) +## References (51 shipped) Full roster at `get-shit-done/references/*.md`. References are shared knowledge documents that workflows and agents `@-reference`. The groupings below match [`docs/ARCHITECTURE.md`](ARCHITECTURE.md#references-get-shit-donereferencesmd) — core, workflow, thinking-model clusters, and the modular planner decomposition. @@ -303,6 +303,7 @@ Full roster at `get-shit-done/references/*.md`. References are shared knowledge | `continuation-format.md` | Session continuation/resume format. | | `domain-probes.md` | Domain-specific probing questions for discuss-phase. | | `gate-prompts.md` | Gate/checkpoint prompt templates. | +| `scout-codebase.md` | Phase-type→codebase-map selection table for discuss-phase scout step (extracted via #2551). | | `revision-loop.md` | Plan revision iteration patterns. | | `universal-anti-patterns.md` | Universal anti-patterns to detect and avoid. | | `artifact-types.md` | Planning artifact type definitions. | @@ -354,7 +355,7 @@ The `gsd-planner` agent is decomposed into a core agent plus reference modules t | `planner-revision.md` | Plan revision patterns for iterative refinement. | | `planner-source-audit.md` | Planner source-audit and authority-limit rules. | -> **Subdirectory:** `get-shit-done/references/few-shot-examples/` contains additional few-shot examples (`plan-checker.md`, `verifier.md`) that are referenced from specific agents. These are not counted in the 50 top-level references. +> **Subdirectory:** `get-shit-done/references/few-shot-examples/` contains additional few-shot examples (`plan-checker.md`, `verifier.md`) that are referenced from specific agents. These are not counted in the 51 top-level references. --- diff --git a/get-shit-done/references/scout-codebase.md b/get-shit-done/references/scout-codebase.md new file mode 100644 index 00000000..d01a386a --- /dev/null +++ b/get-shit-done/references/scout-codebase.md @@ -0,0 +1,51 @@ +# Codebase scout — map selection table + +> Lazy-loaded reference for the `scout_codebase` step in +> `workflows/discuss-phase.md` (extracted via #2551 progressive-disclosure +> refactor). Read this only when prior `.planning/codebase/*.md` maps exist +> and the workflow needs to pick which 2–3 to load. + +## Phase-type → recommended maps + +Read 2–3 maps based on inferred phase type. Do NOT read all seven — +that inflates context without improving discussion quality. + +| Phase type (infer from title + ROADMAP entry) | Read these maps | +|---|---| +| UI / frontend / styling / design | CONVENTIONS.md, STRUCTURE.md, STACK.md | +| Backend / API / service / data model | STACK.md, ARCHITECTURE.md, INTEGRATIONS.md | +| Integration / third-party / provider | STACK.md, INTEGRATIONS.md, ARCHITECTURE.md | +| Infrastructure / DevOps / CI / deploy | STACK.md, ARCHITECTURE.md, INTEGRATIONS.md | +| Testing / QA / coverage | TESTING.md, CONVENTIONS.md, STRUCTURE.md | +| Documentation / content | CONVENTIONS.md, STRUCTURE.md | +| Mixed / unclear | STACK.md, ARCHITECTURE.md, CONVENTIONS.md | + +Read CONCERNS.md only if the phase explicitly addresses known concerns or +security issues. + +## Single-read rule + +Read each map file in a **single** Read call. Do not read the same file at +two different offsets — split reads break prompt-cache reuse and cost more +than a single full read. + +## No-maps fallback + +If `.planning/codebase/*.md` does not exist: +1. Extract key terms from the phase goal (e.g., "feed" → "post", "card", + "list"; "auth" → "login", "session", "token") +2. `grep -rlE "{term1}|{term2}" src/ app/ --include="*.ts" ...` (use `-E` + for extended regex so the `|` alternation works on both GNU grep and BSD + grep / macOS), and `ls` the conventional component/hook/util dirs +3. Read the 3–5 most relevant files + +## Output (internal ``) + +From the scan, identify: +- **Reusable assets** — components, hooks, utilities usable in this phase +- **Established patterns** — state management, styling, data fetching +- **Integration points** — routes, nav, providers where new code connects +- **Creative options** — approaches the architecture enables or constrains + +Used in `analyze_phase` and `present_gray_areas`. NOT written to a file — +session-only. diff --git a/get-shit-done/workflows/discuss-phase.md b/get-shit-done/workflows/discuss-phase.md index 170932a3..3304c799 100644 --- a/get-shit-done/workflows/discuss-phase.md +++ b/get-shit-done/workflows/discuss-phase.md @@ -10,56 +10,57 @@ You are a thinking partner, not an interviewer. The user is the visionary — yo @~/.claude/get-shit-done/references/universal-anti-patterns.md + +**Per-mode bodies, templates, and the advisor flow are lazy-loaded** to keep +this file under the 500-line workflow budget (#2551, mirrors #2361's agent +budget). Read only the files needed for the current invocation: + +| When | Read | +|---|---| +| `--power` in $ARGUMENTS | `workflows/discuss-phase/modes/power.md` (then exit standard flow) | +| `--all` in $ARGUMENTS | `workflows/discuss-phase/modes/all.md` overlay | +| `--auto` in $ARGUMENTS | `workflows/discuss-phase/modes/auto.md` + `workflows/discuss-phase/modes/chain.md` (auto-advance) | +| `--chain` in $ARGUMENTS | `workflows/discuss-phase/modes/default.md` + `workflows/discuss-phase/modes/chain.md` | +| `--text` in $ARGUMENTS or `workflow.text_mode: true` | `workflows/discuss-phase/modes/text.md` overlay | +| `--batch` in $ARGUMENTS | `workflows/discuss-phase/modes/batch.md` overlay | +| `--analyze` in $ARGUMENTS | `workflows/discuss-phase/modes/analyze.md` overlay | +| ADVISOR_MODE = true (USER-PROFILE.md exists) | `workflows/discuss-phase/modes/advisor.md` | +| no flags above | `workflows/discuss-phase/modes/default.md` | +| in `write_context` step | `workflows/discuss-phase/templates/context.md` | +| in `git_commit` step | `workflows/discuss-phase/templates/discussion-log.md` | +| writing checkpoints | `workflows/discuss-phase/templates/checkpoint.json` | + +Do not Read mode files unless the corresponding flag/condition is set. + + **CONTEXT.md feeds into:** 1. **gsd-phase-researcher** — Reads CONTEXT.md to know WHAT to research - - "User wants card-based layout" → researcher investigates card component patterns - - "Infinite scroll decided" → researcher looks into virtualization libraries - 2. **gsd-planner** — Reads CONTEXT.md to know WHAT decisions are locked - - "Pull-to-refresh on mobile" → planner includes that in task specs - - "Claude's Discretion: loading skeleton" → planner can decide approach **Your job:** Capture decisions clearly enough that downstream agents can act on them without asking the user again. - **Not your job:** Figure out HOW to implement. That's what research and planning do with the decisions you capture. **User = founder/visionary. Claude = builder.** -The user knows: -- How they imagine it working -- What it should look/feel like -- What's essential vs nice-to-have -- Specific behaviors or references they have in mind +The user knows: how they imagine it working, what it should look/feel like, what's essential vs nice-to-have, specific behaviors or references they have in mind. -The user doesn't know (and shouldn't be asked): -- Codebase patterns (researcher reads the code) -- Technical risks (researcher identifies these) -- Implementation approach (planner figures this out) -- Success metrics (inferred from the work) +The user doesn't know (and shouldn't be asked): codebase patterns (researcher reads the code), technical risks (researcher identifies these), implementation approach (planner figures this out), success metrics (inferred from the work). Ask about vision and implementation choices. Capture decisions for downstream agents. -**CRITICAL: No scope creep.** +**CRITICAL: No scope creep.** The phase boundary comes from ROADMAP.md and is FIXED. Discussion clarifies HOW to implement what's scoped, never WHETHER to add new capabilities. -The phase boundary comes from ROADMAP.md and is FIXED. Discussion clarifies HOW to implement what's scoped, never WHETHER to add new capabilities. +**Allowed (clarifying ambiguity):** "How should posts be displayed?" (layout), "What happens on empty state?" (within the feature), "Pull to refresh or manual?" (behavior choice). -**Allowed (clarifying ambiguity):** -- "How should posts be displayed?" (layout, density, info shown) -- "What happens on empty state?" (within the feature) -- "Pull to refresh or manual?" (behavior choice) +**Not allowed (scope creep):** "Should we also add comments?" / "What about search/filtering?" / "Maybe include bookmarking?" — those are new capabilities and belong in their own phase. -**Not allowed (scope creep):** -- "Should we also add comments?" (new capability) -- "What about search/filtering?" (new capability) -- "Maybe include bookmarking?" (new capability) - -**The heuristic:** Does this clarify how we implement what's already in the phase, or does it add a new capability that could be its own phase? +**Heuristic:** Does this clarify how we implement what's already in the phase, or does it add a new capability that could be its own phase? **When user suggests scope creep:** ``` @@ -75,68 +76,29 @@ Capture the idea in a "Deferred Ideas" section. Don't lose it, don't act on it. Gray areas are **implementation decisions the user cares about** — things that could go multiple ways and would change the result. -**How to identify gray areas:** +1. Read the phase goal from ROADMAP.md +2. Understand the domain — something users SEE / CALL / RUN / READ / something being ORGANIZED — and let that drive what kinds of decisions matter +3. Generate phase-specific gray areas (not generic categories) -1. **Read the phase goal** from ROADMAP.md -2. **Understand the domain** — What kind of thing is being built? - - Something users SEE → visual presentation, interactions, states matter - - Something users CALL → interface contracts, responses, errors matter - - Something users RUN → invocation, output, behavior modes matter - - Something users READ → structure, tone, depth, flow matter - - Something being ORGANIZED → criteria, grouping, handling exceptions matter -3. **Generate phase-specific gray areas** — Not generic categories, but concrete decisions for THIS phase - -**Don't use generic category labels** (UI, UX, Behavior). Generate specific gray areas: +**Don't use generic category labels** (UI, UX, Behavior). Generate specific gray areas. Examples: ``` -Phase: "User authentication" -→ Session handling, Error responses, Multi-device policy, Recovery flow - -Phase: "Organize photo library" -→ Grouping criteria, Duplicate handling, Naming convention, Folder structure - -Phase: "CLI for database backups" -→ Output format, Flag design, Progress reporting, Error recovery - -Phase: "API documentation" -→ Structure/navigation, Code examples depth, Versioning approach, Interactive elements +Phase: "User authentication" → Session handling, Error responses, Multi-device policy, Recovery flow +Phase: "Organize photo library" → Grouping criteria, Duplicate handling, Naming convention, Folder structure +Phase: "CLI for database backups"→ Output format, Flag design, Progress reporting, Error recovery +Phase: "API documentation" → Structure/navigation, Code examples depth, Versioning approach, Interactive elements ``` -**The key question:** What decisions would change the outcome that the user should weigh in on? - -**Claude handles these (don't ask):** -- Technical implementation details -- Architecture patterns -- Performance optimization -- Scope (roadmap defines this) +**Claude handles these (don't ask):** technical implementation details, architecture patterns, performance optimization, scope (roadmap defines this). -**IMPORTANT: Answer validation** — After every AskUserQuestion call, check if the response is empty or whitespace-only. If so: +**IMPORTANT: Answer validation** — After every AskUserQuestion call, if the response is empty/whitespace-only: -**Exception — "Other" with empty text:** If the user selected "Other" (or "Chat more") and the response body is empty or whitespace-only, this is NOT an empty answer — it is a signal that the user wants to type freeform input. In this case: -1. Output a single plain-text line: "What would you like to discuss?" -2. STOP generating. Do not call any tools. Do not output any further text. -3. Wait for the user's next message. -4. After receiving their message, reflect it back and continue. -Do NOT retry the AskUserQuestion or generate more questions when "Other" is selected with empty text. +- **"Other" with empty text** (the user wants to type freeform): output `"What would you like to discuss?"`, STOP generating, wait for the user's next message, then reflect it back and continue. Do NOT retry AskUserQuestion or call any tools. +- **Any other empty response:** retry once with the same parameters; if still empty, present options as a plain-text numbered list. Never proceed with empty input. -**All other empty responses:** If the response is empty or whitespace-only (and the user did NOT select "Other"): -1. Retry the question once with the same parameters -2. If still empty, present the options as a plain-text numbered list and ask the user to type their choice number -Never proceed with an empty answer. - -**Text mode (`workflow.text_mode: true` in config or `--text` flag):** -When text mode is active, **do not use AskUserQuestion at all**. Instead, present every -question as 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 the Claude App -cannot forward TUI menu selections back to the host. - -Enable text mode: -- Per-session: pass `--text` flag to any command (e.g., `/gsd:discuss-phase --text`) -- Per-project: `gsd-sdk query config-set workflow.text_mode true` - -Text mode applies to ALL workflows in the session, not just discuss-phase. +**Text mode** (`--text` or `workflow.text_mode: true`): follow `workflows/discuss-phase/modes/text.md` — do not use AskUserQuestion at all. @@ -154,41 +116,38 @@ AGENT_SKILLS_ADVISOR=$(gsd-sdk query agent-skills gsd-advisor 2>/dev/null) Parse JSON for: `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `has_research`, `has_context`, `has_plans`, `has_verification`, `plan_count`, `roadmap_exists`, `planning_exists`, `response_language`. -**If `response_language` is set:** All user-facing questions, prompts, and explanations in this workflow MUST be presented in `{response_language}`. This includes AskUserQuestion labels, option text, gray area descriptions, and discussion summaries. Technical terms, code, and file paths remain in English. Subagent prompts stay in English — only user-facing output is translated. +**If `response_language` is set:** All user-facing questions, prompts, and explanations in this workflow MUST be presented in `{response_language}`. Technical terms, code, file paths, and subagent prompts stay in English — only user-facing output is translated. **If `phase_found` is false:** ``` Phase [X] not found in roadmap. - Use /gsd:progress ${GSD_WS} to see available phases. ``` Exit workflow. -**If `phase_found` is true:** Continue to check_existing. +**Mode dispatch — Read mode files lazily based on flags in $ARGUMENTS:** -**Power mode** — If `--power` is present in ARGUMENTS: -- Skip interactive questioning entirely -- Read and execute @~/.claude/get-shit-done/workflows/discuss-phase-power.md end-to-end -- Do not continue with the steps below +```bash +# Detect advisor mode (file-existence guard — no Read until needed) +if [ -f "$HOME/.claude/get-shit-done/USER-PROFILE.md" ]; then + ADVISOR_MODE=true +else + ADVISOR_MODE=false +fi +``` -**All mode** — If `--all` is present in ARGUMENTS: -- In `present_gray_areas`: auto-select ALL gray areas without asking the user (skips the AskUserQuestion selection step) -- Discussion for each area proceeds fully interactively (user drives the conversation for every area) -- Does NOT auto-advance to plan-phase afterward — use `--chain` or `--auto` if you want auto-advance -- Log: `[--all] Auto-selected all gray areas: [list area names].` -- This is the "discuss everything" shortcut: skip the selection friction, keep full interactive control +- If `--power` in $ARGUMENTS: `Read(workflows/discuss-phase/modes/power.md)` and execute it end-to-end. Do NOT continue with the steps below. +- Otherwise, continue. Per-flag overlay reads happen at their relevant steps: + - `--all` → Read `workflows/discuss-phase/modes/all.md` before `present_gray_areas`. + - `--auto` → Read `workflows/discuss-phase/modes/auto.md` before `check_existing` (it overrides several steps). + - `--chain` → Read `workflows/discuss-phase/modes/chain.md` before `auto_advance`. + - `--text` (or `workflow.text_mode: true`) → Read `workflows/discuss-phase/modes/text.md` before any AskUserQuestion call. + - `--batch` → Read `workflows/discuss-phase/modes/batch.md` before `discuss_areas`. + - `--analyze` → Read `workflows/discuss-phase/modes/analyze.md` before `discuss_areas`. + - `ADVISOR_MODE = true` → Read `workflows/discuss-phase/modes/advisor.md` before `analyze_phase` (it changes the discussion flow and adds an `advisor_research` substep). + - No flags → Read `workflows/discuss-phase/modes/default.md` before `discuss_areas`. -**Auto mode** — If `--auto` is present in ARGUMENTS: -- In `check_existing`: auto-select "Skip" (if context exists) or continue without prompting (if no context/plans) -- In `present_gray_areas`: auto-select ALL gray areas without asking the user -- In `discuss_areas`: for each discussion question, choose the recommended option (first option, or the one marked "recommended") without using AskUserQuestion -- Log each auto-selected choice inline so the user can review decisions in the context file -- After discussion completes, auto-advance to plan-phase (existing behavior) - -**Chain mode** — If `--chain` is present in ARGUMENTS: -- Discussion is fully interactive (questions, gray area selection — same as default mode) -- After discussion completes, auto-advance to plan-phase → execute-phase (same as `--auto`) -- This is the middle ground: user controls the discuss decisions, then plan+execute run autonomously +**If `phase_found` is true:** Continue to `check_blocking_antipatterns`. @@ -202,13 +161,10 @@ ls ${phase_dir}/.continue-here.md 2>/dev/null || true If `.continue-here.md` exists, parse its "Critical Anti-Patterns" table for rows with `severity` = `blocking`. -**If one or more `blocking` anti-patterns are found:** - -This step cannot be skipped. Before proceeding to `check_existing` or any other step, the agent must demonstrate understanding of each blocking anti-pattern by answering all three questions for each one: - -1. **What is this anti-pattern?** — Describe it in your own words, not by quoting the handoff. +**If one or more `blocking` anti-patterns are found:** the agent must demonstrate understanding of each by answering all three questions for each one: +1. **What is this anti-pattern?** — Describe it in your own words. 2. **How did it manifest?** — Explain the specific failure that caused it to be recorded. -3. **What structural mechanism (not acknowledgment) prevents it?** — Name the concrete step, checklist item, or enforcement mechanism that stops recurrence. +3. **What structural mechanism (not acknowledgment) prevents it?** — Name the concrete step or enforcement mechanism that stops recurrence. Write these answers inline before continuing. If a blocking anti-pattern cannot be answered from the context in `.continue-here.md`, stop and ask the user for clarification. @@ -216,7 +172,7 @@ Write these answers inline before continuing. If a blocking anti-pattern cannot -Check if a SPEC.md (from `/gsd:spec-phase`) exists for this phase. SPEC.md locks requirements before implementation decisions — if present, this discussion focuses on HOW to implement, not WHAT to build. +Check if a SPEC.md (from `/gsd:spec-phase`) exists for this phase. SPEC.md locks requirements before implementation decisions. ```bash ls ${phase_dir}/*-SPEC.md 2>/dev/null | grep -v AI-SPEC | head -1 || true @@ -224,18 +180,14 @@ ls ${phase_dir}/*-SPEC.md 2>/dev/null | grep -v AI-SPEC | head -1 || true **If SPEC.md is found:** 1. Read the SPEC.md file. -2. Count the number of requirements (numbered items in the `## Requirements` section). -3. Display: - ``` - Found SPEC.md — {N} requirements locked. Focusing on implementation decisions. - ``` -4. Set internal flag `spec_loaded = true`. -5. Store the requirements, boundaries, and acceptance criteria from SPEC.md as `` — these flow directly into CONTEXT.md without re-asking. -6. Continue to `check_existing`. +2. Count requirements (numbered items in `## Requirements`). +3. Display: `Found SPEC.md — {N} requirements locked. Focusing on implementation decisions.` +4. Set `spec_loaded = true`. +5. Store requirements, boundaries, and acceptance criteria as `` — these flow directly into CONTEXT.md without re-asking. -**If no SPEC.md is found:** Continue to `check_existing` with `spec_loaded = false` (default behavior unchanged). +**If no SPEC.md is found:** Continue with `spec_loaded = false`. -**Note:** SPEC.md files named `AI-SPEC.md` (from `/gsd:ai-integration-phase`) are excluded — those serve a different purpose. +**Note:** SPEC.md files named `AI-SPEC.md` (from `/gsd:ai-integration-phase`) are excluded — different purpose. @@ -247,877 +199,201 @@ ls ${phase_dir}/*-CONTEXT.md 2>/dev/null || true **If exists:** -**If `--auto`:** Auto-select "Update it" — load existing context and continue to analyze_phase. Log: `[auto] Context exists — updating with auto-selected decisions.` +**If `--auto`:** Auto-select "Update it" — load existing context and continue to `analyze_phase`. Log: `[auto] Context exists — updating with auto-selected decisions.` -**Otherwise:** Use AskUserQuestion: -- header: "Context" -- question: "Phase [X] already has context. What do you want to do?" -- options: - - "Update it" — Review and revise existing context - - "View it" — Show me what's there - - "Skip" — Use existing context as-is - -If "Update": Load existing, continue to analyze_phase -If "View": Display CONTEXT.md, then offer update/skip -If "Skip": Exit workflow +**Otherwise:** AskUserQuestion (header: "Context"; question: "Phase [X] already has context. What do you want to do?"; options: "Update it" / "View it" / "Skip"). Branch accordingly. **If doesn't exist:** -**Check for interrupted discussion checkpoint:** - +Check for an interrupted discussion checkpoint: ```bash ls ${phase_dir}/*-DISCUSS-CHECKPOINT.json 2>/dev/null || true ``` -If a checkpoint file exists (previous session was interrupted before CONTEXT.md was written): +If a checkpoint file exists: **If `--auto`:** Auto-select "Resume" — load checkpoint and continue from last completed area. -**Otherwise:** Use AskUserQuestion: -- header: "Resume" -- question: "Found interrupted discussion checkpoint ({N} areas completed out of {M}). Resume from where you left off?" -- options: - - "Resume" — Load checkpoint, skip completed areas, continue discussion - - "Start fresh" — Delete checkpoint, start discussion from scratch - -If "Resume": Parse the checkpoint JSON. Load `decisions` into the internal accumulator. Set `areas_completed` to skip those areas. Continue to `present_gray_areas` with only the remaining areas. -If "Start fresh": Delete the checkpoint file. Continue as if no checkpoint existed. +**Otherwise:** AskUserQuestion (header: "Resume"; question: "Found interrupted discussion checkpoint ({N} areas completed out of {M}). Resume from where you left off?"; options: "Resume" / "Start fresh"). On "Resume", parse the checkpoint JSON, load `decisions` into the internal accumulator, set `areas_completed` to skip those areas, continue to `present_gray_areas` with only the remaining areas. On "Start fresh", delete the checkpoint and continue. Check `has_plans` and `plan_count` from init. **If `has_plans` is true:** **If `--auto`:** Auto-select "Continue and replan after". Log: `[auto] Plans exist — continuing with context capture, will replan after.` -**Otherwise:** Use AskUserQuestion: -- header: "Plans exist" -- question: "Phase [X] already has {plan_count} plan(s) created without user context. Your decisions here won't affect existing plans unless you replan." -- options: - - "Continue and replan after" — Capture context, then run /gsd:plan-phase {X} ${GSD_WS} to replan - - "View existing plans" — Show plans before deciding - - "Cancel" — Skip discuss-phase +**Otherwise:** AskUserQuestion (header: "Plans exist"; question: "Phase [X] already has {plan_count} plan(s) created without user context. Your decisions here won't affect existing plans unless you replan."; options: "Continue and replan after" / "View existing plans" / "Cancel"). Branch accordingly. -If "Continue and replan after": Continue to analyze_phase. -If "View existing plans": Display plan files, then offer "Continue" / "Cancel". -If "Cancel": Exit workflow. - -**If `has_plans` is false:** Continue to load_prior_context. +**If `has_plans` is false:** Continue to `load_prior_context`. -Read project-level and prior phase context to avoid re-asking decided questions and maintain consistency. +Read project-level and prior phase context to avoid re-asking decided questions. -**Step 1: Read project-level files** ```bash -# Core project files cat .planning/PROJECT.md 2>/dev/null || true cat .planning/REQUIREMENTS.md 2>/dev/null || true cat .planning/STATE.md 2>/dev/null || true ``` -Extract from these: -- **PROJECT.md** — Vision, principles, non-negotiables, user preferences -- **REQUIREMENTS.md** — Acceptance criteria, constraints, must-haves vs nice-to-haves -- **STATE.md** — Current progress, any flags or session notes - -**Step 2: Read bounded prior CONTEXT.md files** - -Reading every prior CONTEXT.md grows linearly with phase count and inflates context cost. Instead, read a bounded set: +Read at most **3** prior CONTEXT.md files (most recent 3 phases before current). If `.planning/DECISIONS-INDEX.md` exists, read that instead — it is a bounded rolling summary that supersedes per-phase reads. ```bash -# Find CONTEXT.md files from phases before current, sorted newest-first (find .planning/phases -name "*-CONTEXT.md" 2>/dev/null || true) | sort -r ``` -Read at most **3** prior CONTEXT.md files (the most recent 3 phases before the current one). This is sufficient for decision continuity — earlier decisions are already captured in PROJECT.md and REQUIREMENTS.md. +For each CONTEXT.md read: extract `` (locked preferences), `` (particular references), and patterns (e.g., "user prefers minimal UI", "user rejected single-key shortcuts"). -If `.planning/DECISIONS-INDEX.md` exists, read that instead of individual CONTEXT files — it is a bounded rolling summary that supersedes per-phase reads. - -For each CONTEXT.md read: -- Read the `` section — these are locked preferences -- Read `` — particular references or "I want it like X" moments -- Note any patterns (e.g., "user consistently prefers minimal UI", "user rejected single-key shortcuts") - -**Step 3: Build internal `` context** - -Structure the extracted information: -``` - -## Project-Level -- [Key principle or constraint from PROJECT.md] -- [Requirement that affects this phase from REQUIREMENTS.md] - -## From Prior Phases -### Phase N: [Name] -- [Decision that may be relevant to current phase] -- [Preference that establishes a pattern] - -### Phase M: [Name] -- [Another relevant decision] - -``` - -**Step 4: Load spike/sketch findings (if they exist)** +**Spike/sketch findings:** Check for project-local skills: ```bash -# Check for spike/sketch findings skills (project-local) SPIKE_FINDINGS=$(ls ./.claude/skills/spike-findings-*/SKILL.md 2>/dev/null | head -1) SKETCH_FINDINGS=$(ls ./.claude/skills/sketch-findings-*/SKILL.md 2>/dev/null | head -1) - -# Also check for raw spikes/sketches not yet wrapped up RAW_SPIKES=$(ls .planning/spikes/MANIFEST.md 2>/dev/null) RAW_SKETCHES=$(ls .planning/sketches/MANIFEST.md 2>/dev/null) ``` -If spike/sketch findings skills exist, read their SKILL.md and reference files. Extract: -- **Validated patterns** — what was proven to work (use these, don't re-explore) -- **Landmines** — what was proven NOT to work (avoid these) -- **Constraints** — hard limits discovered (rate limits, API gaps, library limitations) -- **Design decisions** — winning visual directions, CSS patterns, layout choices +If findings skills exist, read SKILL.md and reference files; extract validated patterns, landmines, constraints, design decisions. Add them to ``. -Add to ``: -``` -## From Spike Experiments -- [Validated pattern or constraint from spike findings] +If raw spikes/sketches exist but no findings skill, note: `⚠ Unpackaged spikes/sketches detected — run /gsd:spike-wrap-up or /gsd:sketch-wrap-up to make findings available.` -## From Design Sketches -- [Design decision or visual direction from sketch findings] -``` +Build internal `` with sections for Project-Level (from PROJECT.md / REQUIREMENTS.md), From Prior Phases (per-phase decisions), and From Spike/Sketch Findings (validated patterns, landmines, design decisions). -If raw spikes/sketches exist but no findings skill, note in output: -``` -⚠ Unpackaged spikes/sketches detected — run `/gsd:spike-wrap-up` or `/gsd:sketch-wrap-up` to make findings available to planning agents. -``` +**Usage downstream:** `analyze_phase` skips already-decided gray areas; `present_gray_areas` annotates options ("You chose X in Phase 5"); `discuss_areas` pre-fills or flags conflicts. -**Usage in subsequent steps:** -- `analyze_phase`: Skip gray areas already decided in prior phases or validated by spikes/sketches -- `present_gray_areas`: Annotate options with prior decisions ("You chose X in Phase 5") and spike/sketch findings ("Spike 002 validated this approach") -- `discuss_areas`: Pre-fill answers or flag conflicts ("This contradicts Phase 3 — same here or different?") - -**If no prior context exists:** Continue without — this is expected for early phases. +**If no prior context exists:** Continue without — expected for early phases. -Check if any pending todos are relevant to this phase's scope. Surfaces backlog items that might otherwise be missed. +Check pending todos for matches with this phase's scope. -**Load and match todos:** ```bash TODO_MATCHES=$(gsd-sdk query todo.match-phase "${PHASE_NUMBER}") ``` Parse JSON for: `todo_count`, `matches[]` (each with `file`, `title`, `area`, `score`, `reasons`). -**If `todo_count` is 0 or `matches` is empty:** Skip silently — no workflow slowdown. +**If `todo_count` is 0 or `matches` is empty:** Skip silently. -**If matches found:** - -Present matched todos to the user. Show each match with its title, area, and why it matched: - -``` -📋 Found {N} pending todo(s) that may be relevant to Phase {X}: - -{For each match:} -- **{title}** (area: {area}, relevance: {score}) — matched on {reasons} -``` - -Use AskUserQuestion (multiSelect) asking which todos to fold into this phase's scope: - -``` -Which of these todos should be folded into Phase {X} scope? -(Select any that apply, or none to skip) -``` - -**For selected (folded) todos:** -- Store internally as `` for inclusion in CONTEXT.md `` section -- These become additional scope items that downstream agents (researcher, planner) will see - -**For unselected (reviewed but not folded) todos:** -- Store internally as `` for inclusion in CONTEXT.md `` section -- This prevents future phases from re-surfacing the same todos as "missed" +**If matches found:** Present each match (title, area, why it matched). AskUserQuestion (multiSelect) asking which to fold. Folded → `` for CONTEXT.md ``. Reviewed but not folded → `` for CONTEXT.md ``. **Auto mode (`--auto`):** Fold all todos with score >= 0.4 automatically. Log the selection. -Lightweight scan of existing code to inform gray area identification and discussion. Uses ~10% context — acceptable for an interactive session. +Lightweight scan of existing code to inform gray area identification (~10% context). -**Step 1: Check for existing codebase maps** -```bash -ls .planning/codebase/*.md 2>/dev/null || true -``` - -**If codebase maps exist:** Select 2–3 maps based on phase type. Do NOT read all seven — that inflates context without improving discussion quality. Use this selection table: - -| Phase type (infer from title + ROADMAP entry) | Read these maps | -|---|---| -| UI / frontend / styling / design | CONVENTIONS.md, STRUCTURE.md, STACK.md | -| Backend / API / service / data model | STACK.md, ARCHITECTURE.md, INTEGRATIONS.md | -| Integration / third-party / provider | STACK.md, INTEGRATIONS.md, ARCHITECTURE.md | -| Infrastructure / DevOps / CI / deploy | STACK.md, ARCHITECTURE.md, INTEGRATIONS.md | -| Testing / QA / coverage | TESTING.md, CONVENTIONS.md, STRUCTURE.md | -| Documentation / content | CONVENTIONS.md, STRUCTURE.md | -| Mixed / unclear | STACK.md, ARCHITECTURE.md, CONVENTIONS.md | - -Read CONCERNS.md only if the phase explicitly addresses known concerns or security issues. - -**Important — no split reads:** Read each map file in a single Read call. Do not read the same file at two different offsets — split reads break prompt cache reuse and cost more than a single full read. - -Extract: -- Reusable components/hooks/utilities -- Established patterns (state management, styling, data fetching) -- Integration points (where new code would connect) - -Skip to Step 3 below. - -**Step 2: If no codebase maps, do targeted grep** - -Extract key terms from the phase goal (e.g., "feed" → "post", "card", "list"; "auth" → "login", "session", "token"). - -```bash -# Find files related to phase goal terms -grep -rl "{term1}\|{term2}" src/ app/ --include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" 2>/dev/null | head -10 || true - -# Find existing components/hooks -ls src/components/ 2>/dev/null || true -ls src/hooks/ 2>/dev/null || true -ls src/lib/ src/utils/ 2>/dev/null || true -``` - -Read the 3-5 most relevant files to understand existing patterns. - -**Step 3: Build internal codebase_context** - -From the scan, identify: -- **Reusable assets** — existing components, hooks, utilities that could be used in this phase -- **Established patterns** — how the codebase does state management, styling, data fetching -- **Integration points** — where new code would connect (routes, nav, providers) -- **Creative options** — approaches the existing architecture enables or constrains - -Store as internal `` for use in analyze_phase and present_gray_areas. This is NOT written to a file — it's used within this session only. +Read `@~/.claude/get-shit-done/references/scout-codebase.md` — it contains the phase-type→map selection table, single-read rule, no-maps fallback, and `` output schema. Then execute: +1. `ls .planning/codebase/*.md` to find existing maps +2. Select 2–3 maps via the reference's table; or grep fallback if none exist +3. Build internal `` per the reference's output schema -Analyze the phase to identify gray areas worth discussing. **Use both `prior_decisions` and `codebase_context` to ground the analysis.** - -**Read the phase description from ROADMAP.md and determine:** +Analyze the phase to identify gray areas. Use both `prior_decisions` and `codebase_context` to ground the analysis. 1. **Domain boundary** — What capability is this phase delivering? State it clearly. -1b. **Initialize canonical refs accumulator** — Start building the `` list for CONTEXT.md. This accumulates throughout the entire discussion, not just this step. +1b. **Initialize canonical refs accumulator** — Start building `` for CONTEXT.md. Sources: + - **Now:** Copy `Canonical refs:` from ROADMAP.md for this phase. Expand each to a full relative path. Check REQUIREMENTS.md and PROJECT.md for specs/ADRs referenced. + - **`scout_codebase`:** If existing code references docs (e.g., comments citing ADRs), add those. + - **`discuss_areas`:** When the user says "read X", "check Y", or references any doc/spec/ADR — add it immediately. These are often the MOST important refs. - **Source 1 (now):** Copy `Canonical refs:` from ROADMAP.md for this phase. Expand each to a full relative path. - **Source 2 (now):** Check REQUIREMENTS.md and PROJECT.md for any specs/ADRs referenced for this phase. - **Source 3 (scout_codebase):** If existing code references docs (e.g., comments citing ADRs), add those. - **Source 4 (discuss_areas):** When the user says "read X", "check Y", or references any doc/spec/ADR during discussion — add it immediately. These are often the MOST important refs because they represent docs the user specifically wants followed. + This list is MANDATORY in CONTEXT.md. Every ref must have a full relative path. If no external docs exist, note that explicitly. - This list is MANDATORY in CONTEXT.md. Every ref must have a full relative path so downstream agents can read it directly. If no external docs exist, note that explicitly. +2. **Check prior decisions** — Scan `` for already-decided gray areas; mark them pre-answered. -2. **Check prior decisions** — Before generating gray areas, check if any were already decided: - - Scan `` for relevant choices (e.g., "Ctrl+C only, no single-key shortcuts") - - These are **pre-answered** — don't re-ask unless this phase has conflicting needs - - Note applicable prior decisions for use in presentation +2b. **SPEC.md awareness** — If `spec_loaded = true`: `` are pre-answered (Goal, Boundaries, Constraints, Acceptance Criteria). Do NOT generate gray areas about WHAT to build or WHY. Only generate gray areas about HOW to implement. When presenting, include: "Requirements are locked by SPEC.md — discussing implementation decisions only." -2b. **SPEC.md awareness** — If `spec_loaded = true` (SPEC.md was found in `check_spec`): - - The `` from SPEC.md are pre-answered: Goal, Boundaries, Constraints, Acceptance Criteria. - - Do NOT generate gray areas about WHAT to build or WHY — those are locked. - - Only generate gray areas about HOW to implement: technical approach, library choices, UX/UI patterns, interaction details, error handling style. - - When presenting gray areas, include a note: "Requirements are locked by SPEC.md — discussing implementation decisions only." +3. **Gray areas** — For each relevant category, identify 1-2 specific ambiguities that would change implementation. Annotate with code context where relevant. -3. **Gray areas by category** — For each relevant category (UI, UX, Behavior, Empty States, Content), identify 1-2 specific ambiguities that would change implementation. **Annotate with code context where relevant** (e.g., "You already have a Card component" or "No existing pattern for this"). +4. **Skip assessment** — If no meaningful gray areas exist (pure infrastructure, clear-cut implementation, all already decided), the phase may not need discussion. -4. **Skip assessment** — If no meaningful gray areas exist (pure infrastructure, clear-cut implementation, or all already decided in prior phases), the phase may not need discussion. - -**Advisor Mode Detection:** - -Check if advisor mode should activate: - -1. Check for USER-PROFILE.md: - ```bash - PROFILE_PATH="$HOME/.claude/get-shit-done/USER-PROFILE.md" - ``` - ADVISOR_MODE = file exists at PROFILE_PATH → true, otherwise → false - -2. If ADVISOR_MODE is true, resolve vendor_philosophy calibration tier: - - Priority 1: Read config.json > preferences.vendor_philosophy (project-level override) - - Priority 2: Read USER-PROFILE.md Vendor Choices/Philosophy rating (global) - - Priority 3: Default to "standard" if neither has a value or value is UNSCORED - - Map to calibration tier: - - conservative OR thorough-evaluator → full_maturity - - opinionated → minimal_decisive - - pragmatic-fast OR any other value OR empty → standard - -3. Resolve model for advisor agents: - ```bash - ADVISOR_MODEL=$(gsd-sdk query resolve-model gsd-advisor-researcher --raw) - ``` - -If ADVISOR_MODE is false, skip all advisor-specific steps — workflow proceeds with existing conversational flow unchanged. - -**User Profile Language Detection:** - -Check USER-PROFILE.md for communication preferences that indicate a non-technical product owner: - -```bash -PROFILE_CONTENT=$(cat "$HOME/.claude/get-shit-done/USER-PROFILE.md" 2>/dev/null || true) -``` - -Set NON_TECHNICAL_OWNER = true if ANY of the following are present in USER-PROFILE.md: -- `learning_style: guided` -- The word `jargon` appears in a `frustration_triggers` section -- `explanation_depth: practical-detailed` (without a technical modifier) -- `explanation_depth: high-level` - -NON_TECHNICAL_OWNER = false if USER-PROFILE.md does not exist or none of the above signals are present. - -When NON_TECHNICAL_OWNER is true, reframe gray area labels and descriptions in product-outcome language before presenting them to the user. Preserve the same underlying decision — only change the framing: -- Technical implementation term → outcome the user will experience - - "Token architecture" → "Color system: which approach prevents the dark theme from flashing white on open" - - "CSS variable strategy" → "Theme colors: how your brand colors stay consistent in both light and dark mode" - - "Component API surface area" → "How the building blocks connect: how tightly coupled should these parts be" - - "Caching strategy: SWR vs React Query" → "Loading speed: should screens show saved data right away or wait for fresh data" -- All decisions stay the same. Only the question language adapts. - -This reframing applies to: -1. Gray area labels and descriptions in `present_gray_areas` -2. Advisor research rationale rewrites in `advisor_research` synthesis - -**Output your analysis internally, then present to user.** - -Example analysis for "Post Feed" phase (with code and prior context): -``` -Domain: Displaying posts from followed users -Existing: Card component (src/components/ui/Card.tsx), useInfiniteQuery hook, Tailwind CSS -Prior decisions: "Minimal UI preferred" (Phase 2), "No pagination — always infinite scroll" (Phase 4) -Gray areas: -- UI: Layout style (cards vs timeline vs grid) — Card component exists with shadow/rounded variants -- UI: Information density (full posts vs previews) — no existing density patterns -- Behavior: Loading pattern — ALREADY DECIDED: infinite scroll (Phase 4) -- Empty State: What shows when no posts exist — EmptyState component exists in ui/ -- Content: What metadata displays (time, author, reactions count) -``` +**Advisor mode hand-off:** If `ADVISOR_MODE` is true, follow `workflows/discuss-phase/modes/advisor.md` for the rest of analyze/discuss flow (it adds an `advisor_research` substep and replaces the standard `discuss_areas` with table-first selection). The detection block (USER-PROFILE.md existence + non-technical-owner signals + calibration tier resolution) lives in that file — read it once when ADVISOR_MODE is true and follow its rules. -Present the domain boundary, prior decisions, and gray areas to user. +Present the domain boundary, prior decisions, and gray areas to the user. -**First, state the boundary and any prior decisions that apply:** ``` Phase [X]: [Name] Domain: [What this phase delivers — from your analysis] -We'll clarify HOW to implement this. -(New capabilities belong in other phases.) +We'll clarify HOW to implement this. (New capabilities belong in other phases.) [If prior decisions apply:] **Carrying forward from earlier phases:** - [Decision from Phase N that applies here] -- [Decision from Phase M that applies here] ``` -**If `--auto` or `--all`:** Auto-select ALL gray areas. Log: `[--auto/--all] Selected all gray areas: [list area names].` Skip the AskUserQuestion below and continue directly to discuss_areas with all areas selected. +**If `--auto` or `--all`** (per `modes/auto.md` or `modes/all.md`): Auto-select ALL gray areas. Log: `[--auto/--all] Selected all gray areas: [list area names].` Skip the AskUserQuestion below and continue directly to `discuss_areas` with all areas selected. **Otherwise, use AskUserQuestion (multiSelect: true):** - header: "Discuss" - question: "Which areas do you want to discuss for [phase name]?" -- options: Generate 3-4 phase-specific gray areas, each with: - - "[Specific area]" (label) — concrete, not generic - - [1-2 questions this covers + code context annotation] (description) - - **Highlight the recommended choice with brief explanation why** +- options: 3-4 phase-specific gray areas, each with a concrete label (not generic), 1-2 questions in description, and code-context / prior-decision annotations: + ``` + ☐ Layout style — Cards vs list vs timeline? + (You already have a Card component with shadow/rounded variants. Reusing it keeps the app consistent.) -**Prior decision annotations:** When a gray area was already decided in a prior phase, annotate it: -``` -☐ Exit shortcuts — How should users quit? - (You decided "Ctrl+C only, no single-key shortcuts" in Phase 5 — revisit or keep?) -``` + ☐ Loading behavior — Infinite scroll or pagination? + (You chose infinite scroll in Phase 4. useInfiniteQuery hook already set up.) + ``` -**Code context annotations:** When the scout found relevant existing code, annotate the gray area description: -``` -☐ Layout style — Cards vs list vs timeline? - (You already have a Card component with shadow/rounded variants. Reusing it keeps the app consistent.) -``` +**Do NOT include a "skip" or "you decide" option.** User ran this command to discuss — give real choices. -**Combining both:** When both prior decisions and code context apply: -``` -☐ Loading behavior — Infinite scroll or pagination? - (You chose infinite scroll in Phase 4. useInfiniteQuery hook already set up.) -``` - -**Do NOT include a "skip" or "you decide" option.** User ran this command to discuss — give them real choices. - -**Examples by domain (with code context):** - -For "Post Feed" (visual feature): -``` -☐ Layout style — Cards vs list vs timeline? (Card component exists with variants) -☐ Loading behavior — Infinite scroll or pagination? (useInfiniteQuery hook available) -☐ Content ordering — Chronological, algorithmic, or user choice? -☐ Post metadata — What info per post? Timestamps, reactions, author? -``` - -For "Database backup CLI" (command-line tool): -``` -☐ Output format — JSON, table, or plain text? Verbosity levels? -☐ Flag design — Short flags, long flags, or both? Required vs optional? -☐ Progress reporting — Silent, progress bar, or verbose logging? -☐ Error recovery — Fail fast, retry, or prompt for action? -``` - -For "Organize photo library" (organization task): -``` -☐ Grouping criteria — By date, location, faces, or events? -☐ Duplicate handling — Keep best, keep all, or prompt each time? -☐ Naming convention — Original names, dates, or descriptive? -☐ Folder structure — Flat, nested by year, or by category? -``` - -Continue to discuss_areas with selected areas (or advisor_research if ADVISOR_MODE is true). - - - -**Advisor Research** (only when ADVISOR_MODE is true) - -After user selects gray areas in present_gray_areas, spawn parallel research agents. - -1. Display brief status: "Researching {N} areas..." - -2. For EACH user-selected gray area, spawn a Task() in parallel: - - Task( - prompt="First, read @~/.claude/agents/gsd-advisor-researcher.md for your role and instructions. - - {area_name}: {area_description from gray area identification} - {phase_goal and description from ROADMAP.md} - {project name and brief description from PROJECT.md} - {resolved calibration tier: full_maturity | standard | minimal_decisive} - - Research this gray area and return a structured comparison table with rationale. - ${AGENT_SKILLS_ADVISOR}", - subagent_type="general-purpose", - model="{ADVISOR_MODEL}", - description="Research: {area_name}" - ) - - All Task() calls spawn simultaneously — do NOT wait for one before starting the next. - -3. After ALL agents return, SYNTHESIZE results before presenting: - For each agent's return: - a. Parse the markdown comparison table and rationale paragraph - b. Verify all 5 columns present (Option | Pros | Cons | Complexity | Recommendation) — fill any missing columns rather than showing broken table - c. Verify option count matches calibration tier: - - full_maturity: 3-5 options acceptable - - standard: 2-4 options acceptable - - minimal_decisive: 1-2 options acceptable - If agent returned too many, trim least viable. If too few, accept as-is. - d. Rewrite rationale paragraph to weave in project context and ongoing discussion context that the agent did not have access to - e. If agent returned only 1 option, convert from table format to direct recommendation: "Standard approach for {area}: {option}. {rationale}" - f. **If NON_TECHNICAL_OWNER is true:** After completing steps a–e, apply a plain language rewrite to the rationale paragraph. Replace implementation-level terms with outcome descriptions the user can reason about without technical context. The table option names may also be rewritten in plain language if they are implementation terms — the Recommendation column value and the table structure remain intact. Do not remove detail; translate it. Example: "SWR uses stale-while-revalidate to serve cached responses immediately" → "This approach shows you something right away, then quietly updates in the background — users see data instantly." - -4. Store synthesized tables for use in discuss_areas. - -**If ADVISOR_MODE is false:** Skip this step entirely — proceed directly from present_gray_areas to discuss_areas. +Continue to `discuss_areas` with selected areas (or to `advisor_research` per `modes/advisor.md` if `ADVISOR_MODE` is true). -Discuss each selected area with the user. Flow depends on advisor mode. +Discussion behavior is defined by the active mode file(s): -**If ADVISOR_MODE is true:** +- **Advisor mode (ADVISOR_MODE = true):** follow `workflows/discuss-phase/modes/advisor.md` — research-backed comparison tables, table-first selection. +- **--auto:** follow `workflows/discuss-phase/modes/auto.md` — Claude picks recommended option for every question; no AskUserQuestion. Single-pass cap enforced. +- **Default (no flags):** follow `workflows/discuss-phase/modes/default.md` — 4 single-question turns per area, then check whether to continue. -Table-first discussion flow — present research-backed comparison tables, then capture user picks. +Overlays (combine with the active mode): +- `--text` → `workflows/discuss-phase/modes/text.md` (replace AskUserQuestion with plain-text numbered lists) +- `--batch` → `workflows/discuss-phase/modes/batch.md` (group 2–5 questions per turn) +- `--analyze` → `workflows/discuss-phase/modes/analyze.md` (trade-off table before each question) -**For each selected area:** +**Overlay stacking:** overlays combine and apply outer→inner in fixed order `--analyze` → `--batch` → `--text` (e.g., `--batch --analyze` = trade-off table per question group; add `--text` for plain-text rendering). Mode-specific precedence (e.g., `--auto --power`) is documented in each overlay file's "Combination rules" section. -1. **Present the synthesized comparison table + rationale paragraph** (from advisor_research step) +All modes preserve the universal rules below. -2. **Use AskUserQuestion:** - - header: "{area_name}" - - question: "Which approach for {area_name}?" - - options: Extract from the table's Option column (AskUserQuestion adds "Other" automatically) +**Universal rules (apply to every mode):** -3. **Record the user's selection:** - - If user picks from table options → record as locked decision for that area - - If user picks "Other" → receive their input, reflect it back for confirmation, record - - **Thinking partner (conditional):** - If `features.thinking_partner` is enabled in config, check the user's answer for tradeoff signals - (see `references/thinking-partner.md` for signal list). If tradeoff detected: - - ``` - I notice competing priorities here — {option_A} optimizes for {goal_A} while {option_B} optimizes for {goal_B}. - - Want me to think through the tradeoffs before we lock this in? - [Yes, analyze] / [No, decision made] - ``` - - If yes: provide 3-5 bullet analysis (what each optimizes/sacrifices, alignment with PROJECT.md goals, recommendation). Then return to normal flow. - If no or thinking_partner disabled: continue to next area. - -4. **After recording pick, Claude decides whether follow-up questions are needed:** - - If the pick has ambiguity that would affect downstream planning → ask 1-2 targeted follow-up questions using AskUserQuestion - - If the pick is clear and self-contained → move to next area - - Do NOT ask the standard 4 questions — the table already provided the context - -5. **After all areas processed:** - - header: "Done" - - question: "That covers [list areas]. Ready to create context?" - - options: "Create context" / "Revisit an area" - -**Scope creep handling (advisor mode):** -If user mentions something outside the phase domain: -``` -"[Feature] sounds like a new capability — that belongs in its own phase. -I'll note it as a deferred idea. - -Back to [current area]: [return to current question]" -``` - -Track deferred ideas internally. - ---- - -**If ADVISOR_MODE is false:** - -For each selected area, conduct a focused discussion loop. - -**Research-before-questions mode:** Check if `workflow.research_before_questions` is enabled in config (from init context or `.planning/config.json`). When enabled, before presenting questions for each area: -1. Do a brief web search for best practices related to the area topic -2. Summarize the top findings in 2-3 bullet points -3. Present the research alongside the question so the user can make a more informed decision - -Example with research enabled: -``` -Let's talk about [Authentication Strategy]. - -📊 Best practices research: -• OAuth 2.0 + PKCE is the current standard for SPAs (replaces implicit flow) -• Session tokens with httpOnly cookies preferred over localStorage for XSS protection -• Consider passkey/WebAuthn support — adoption is accelerating in 2025-2026 - -With that context: How should users authenticate? -``` - -When disabled (default), skip the research and present questions directly as before. - -**Text mode support:** Parse optional `--text` from `$ARGUMENTS`. -- Accept `--text` flag OR read `workflow.text_mode` from config (from init context) -- When active, replace ALL `AskUserQuestion` calls with plain-text numbered lists -- User types a number to select, or types free text for "Other" -- This is required for Claude Code remote sessions (`/rc` mode) where TUI menus - don't work through the Claude App - -**Batch mode support:** Parse optional `--batch` from `$ARGUMENTS`. -- Accept `--batch`, `--batch=N`, or `--batch N` - -**Analyze mode support:** Parse optional `--analyze` from `$ARGUMENTS`. -When `--analyze` is active, before presenting each question (or question group in batch mode), provide a brief **trade-off analysis** for the decision: -- 2-3 options with pros/cons based on codebase context and common patterns -- A recommended approach with reasoning -- Known pitfalls or constraints from prior phases - -Example with `--analyze`: -``` -**Trade-off analysis: Authentication strategy** - -| Approach | Pros | Cons | -|----------|------|------| -| Session cookies | Simple, httpOnly prevents XSS | Requires CSRF protection, sticky sessions | -| JWT (stateless) | Scalable, no server state | Token size, revocation complexity | -| OAuth 2.0 + PKCE | Industry standard for SPAs | More setup, redirect flow UX | - -💡 Recommended: OAuth 2.0 + PKCE — your app has social login in requirements (REQ-04) and this aligns with the existing NextAuth setup in `src/lib/auth.ts`. - -How should users authenticate? -``` - -This gives the user context to make informed decisions without extra prompting. When `--analyze` is absent, present questions directly as before. -- Accept `--batch`, `--batch=N`, or `--batch N` -- Default to 4 questions per batch when no number is provided -- Clamp explicit sizes to 2-5 so a batch stays answerable -- If `--batch` is absent, keep the existing one-question-at-a-time flow - -**Philosophy:** stay adaptive, but let the user choose the pacing. -- Default mode: 4 single-question turns, then check whether to continue -- `--batch` mode: 1 grouped turn with 2-5 numbered questions, then check whether to continue - -Each answer (or answer set, in batch mode) should reveal the next question or next batch. - -**Auto mode (`--auto`):** For each area, Claude selects the recommended option (first option, or the one explicitly marked "recommended") for every question without using AskUserQuestion. Log each auto-selected choice: -``` -[auto] [Area] — Q: "[question text]" → Selected: "[chosen option]" (recommended default) -``` -After all areas are auto-resolved, skip the "Explore more gray areas" prompt and proceed directly to write_context. - -**CRITICAL — Auto-mode pass cap:** -In `--auto` mode, the discuss step MUST complete in a **single pass**. After writing CONTEXT.md once, you are DONE — proceed immediately to write_context and then auto_advance. Do NOT re-read your own CONTEXT.md to find "gaps", "undefined types", or "missing decisions" and run additional passes. This creates a self-feeding loop where each pass generates references that the next pass treats as gaps, consuming unbounded time and resources. - -Check the pass cap from config: -```bash -MAX_PASSES=$(gsd-sdk query config-get workflow.max_discuss_passes 2>/dev/null || echo "3") -``` - -If you have already written and committed CONTEXT.md, the discuss step is complete. Move on. - -**Interactive mode (no `--auto`):** - -**For each area:** - -1. **Announce the area:** - ``` - Let's talk about [Area]. - ``` - -2. **Ask questions using the selected pacing:** - - **Default (no `--batch`): Ask 4 questions using AskUserQuestion** - - header: "[Area]" (max 12 chars — abbreviate if needed) - - question: Specific decision for this area - - options: 2-3 concrete choices (AskUserQuestion adds "Other" automatically), with the recommended choice highlighted and brief explanation why - - **Annotate options with code context** when relevant: - ``` - "How should posts be displayed?" - - Cards (reuses existing Card component — consistent with Messages) - - List (simpler, would be a new pattern) - - Timeline (needs new Timeline component — none exists yet) - ``` - - Include "You decide" as an option when reasonable — captures Claude discretion - - **Context7 for library choices:** When a gray area involves library selection (e.g., "magic links" → query next-auth docs) or API approach decisions, use `mcp__context7__*` tools to fetch current documentation and inform the options. Don't use Context7 for every question — only when library-specific knowledge improves the options. - - **Batch mode (`--batch`): Ask 2-5 numbered questions in one plain-text turn** - - Group closely related questions for the current area into a single message - - Keep each question concrete and answerable in one reply - - When options are helpful, include short inline choices per question rather than a separate AskUserQuestion for every item - - After the user replies, reflect back the captured decisions, note any unanswered items, and ask only the minimum follow-up needed before moving on - - Preserve adaptiveness between batches: use the full set of answers to decide the next batch or whether the area is sufficiently clear - -3. **After the current set of questions, check:** - - header: "[Area]" (max 12 chars) - - question: "More questions about [area], or move to next? (Remaining: [list other unvisited areas])" - - options: "More questions" / "Next area" - - When building the question text, list the remaining unvisited areas so the user knows what's ahead. For example: "More questions about Layout, or move to next? (Remaining: Loading behavior, Content ordering)" - - If "More questions" → ask another 4 single questions, or another 2-5 question batch when `--batch` is active, then check again - If "Next area" → proceed to next selected area - If "Other" (free text) → interpret intent: continuation phrases ("chat more", "keep going", "yes", "more") map to "More questions"; advancement phrases ("done", "move on", "next", "skip") map to "Next area". If ambiguous, ask: "Continue with more questions about [area], or move to the next area?" - -4. **After all initially-selected areas complete:** - - Summarize what was captured from the discussion so far - - AskUserQuestion: - - header: "Done" - - question: "We've discussed [list areas]. Which gray areas remain unclear?" - - options: "Explore more gray areas" / "I'm ready for context" - - If "Explore more gray areas": - - Identify 2-4 additional gray areas based on what was learned - - Return to present_gray_areas logic with these new areas - - Loop: discuss new areas, then prompt again - - If "I'm ready for context": Proceed to write_context - -**Canonical ref accumulation during discussion:** -When the user references a doc, spec, or ADR during any answer — e.g., "read adr-014", "check the MCP spec", "per browse-spec.md" — immediately: -1. Read the referenced doc (or confirm it exists) -2. Add it to the canonical refs accumulator with full relative path -3. Use what you learned from the doc to inform subsequent questions - -These user-referenced docs are often MORE important than ROADMAP.md refs because they represent docs the user specifically wants downstream agents to follow. Never drop them. - -**Question design:** -- Options should be concrete, not abstract ("Cards" not "Option A") -- Each answer should inform the next question or next batch -- If user picks "Other" to provide freeform input (e.g., "let me describe it", "something else", or an open-ended reply), ask your follow-up as plain text — NOT another AskUserQuestion. Wait for them to type at the normal prompt, then reflect their input back and confirm before resuming AskUserQuestion or the next numbered batch. - -**Scope creep handling:** -If user mentions something outside the phase domain: -``` -"[Feature] sounds like a new capability — that belongs in its own phase. -I'll note it as a deferred idea. - -Back to [current area]: [return to current question]" -``` - -Track deferred ideas internally. - -**Incremental checkpoint — save after each area completes:** - -After each area is resolved (user says "Next area" or area auto-resolves in `--auto` mode), immediately write a checkpoint file with all decisions captured so far. This prevents data loss if the session is interrupted mid-discussion. - -**Checkpoint file:** `${phase_dir}/${padded_phase}-DISCUSS-CHECKPOINT.json` - -Write after each area: -```json -{ - "phase": "{PHASE_NUM}", - "phase_name": "{phase_name}", - "timestamp": "{ISO timestamp}", - "areas_completed": ["Area 1", "Area 2"], - "areas_remaining": ["Area 3", "Area 4"], - "decisions": { - "Area 1": [ - {"question": "...", "answer": "...", "options_presented": ["..."]}, - {"question": "...", "answer": "...", "options_presented": ["..."]} - ], - "Area 2": [ - {"question": "...", "answer": "...", "options_presented": ["..."]} - ] - }, - "deferred_ideas": ["..."], - "canonical_refs": ["..."] -} -``` - -This is a structured checkpoint, not the final CONTEXT.md — the `write_context` step still produces the canonical output. But if the session dies, the next `/gsd:discuss-phase` invocation can detect this checkpoint and offer to resume from it instead of starting from scratch. - -**On session resume:** In the `check_existing` step, also check for `*-DISCUSS-CHECKPOINT.json`. If found and no CONTEXT.md exists: -- Display: "Found interrupted discussion checkpoint ({N} areas completed). Resume from checkpoint?" -- Options: "Resume" / "Start fresh" -- On "Resume": Load the checkpoint, skip completed areas, continue from where it left off -- On "Start fresh": Delete the checkpoint, proceed as normal - -**After write_context completes successfully:** Delete the checkpoint file — the canonical CONTEXT.md now has all decisions. - -**Track discussion log data internally:** -For each question asked, accumulate: -- Area name -- All options presented (label + description) -- Which option the user selected (or their free-text response) -- Any follow-up notes or clarifications the user provided -This data is used to generate DISCUSSION-LOG.md in the `write_context` step. +- **Canonical ref accumulation** — when the user references a doc/spec/ADR during any answer, immediately Read it (or confirm it exists) and add it to the canonical refs accumulator with full relative path. Use what you learned to inform subsequent questions. These docs are often MORE important than ROADMAP.md refs because the user specifically wants downstream agents to follow them. +- **Scope creep** — if user mentions something outside the phase domain, capture as deferred idea and redirect. +- **Incremental checkpoint** — after each area completes, write `${phase_dir}/${padded_phase}-DISCUSS-CHECKPOINT.json`. Read `workflows/discuss-phase/templates/checkpoint.json` for the schema. The checkpoint is structured state, not the canonical CONTEXT.md (`write_context` produces the canonical output). On session resume, the parent's `check_existing` step detects the checkpoint and offers to resume. +- **Discussion log accumulation** — for each question asked, accumulate area name, options presented, user's selection, follow-up notes. Used by `git_commit` to write DISCUSSION-LOG.md. -Create CONTEXT.md capturing decisions made. +Create CONTEXT.md and DISCUSSION-LOG.md. -**Also generate DISCUSSION-LOG.md** — a full audit trail of the discuss-phase Q&A. -This file is for human reference only (software audits, compliance reviews). It is NOT -consumed by downstream agents (researcher, planner, executor). +DISCUSSION-LOG.md is for human reference only (audits, retrospectives) and is NOT consumed by downstream agents (researcher, planner, executor). **Find or create phase directory:** -Use values from init: `phase_dir`, `phase_slug`, `padded_phase`. - -If `phase_dir` is null (phase exists in roadmap but no directory): +Use values from init: `phase_dir`, `phase_slug`, `padded_phase`. If `phase_dir` is null: ```bash mkdir -p ".planning/phases/${padded_phase}-${phase_slug}" ``` **File location:** `${phase_dir}/${padded_phase}-CONTEXT.md` +**Read the CONTEXT.md template now (lazy-loaded):** +``` +Read(workflows/discuss-phase/templates/context.md) +``` + +The template documents variable substitutions and conditional sections. Substitute live values for `[X]`, `[Name]`, `[date]`, `${padded_phase}`, `{N}`. Include `` only when `spec_loaded = true`. Include "Folded Todos" / "Reviewed Todos" subsections only when the `cross_reference_todos` step folded or reviewed todos. + **SPEC.md integration** — If `spec_loaded = true`: -- Add a `` section immediately after `` (see template below). +- Add the `` section immediately after ``. - Add the SPEC.md file to `` with note "Locked requirements — MUST read before planning". - Do NOT duplicate requirements text from SPEC.md into `` — agents read SPEC.md directly. - The `` section contains only implementation decisions from this discussion. -**Structure the content by what was discussed:** - -```markdown -# Phase [X]: [Name] - Context - -**Gathered:** [date] -**Status:** Ready for planning - - -## Phase Boundary - -[Clear statement of what this phase delivers — the scope anchor] - - - -[If spec_loaded = true, insert this section:] - -## Requirements (locked via SPEC.md) - -**{N} requirements are locked.** See `{padded_phase}-SPEC.md` for full requirements, boundaries, and acceptance criteria. - -Downstream agents MUST read `{padded_phase}-SPEC.md` before planning or implementing. Requirements are not duplicated here. - -**In scope (from SPEC.md):** [copy the "In scope" bullet list from SPEC.md Boundaries] -**Out of scope (from SPEC.md):** [copy the "Out of scope" bullet list from SPEC.md Boundaries] - - - - -## Implementation Decisions - -### [Category 1 that was discussed] -- **D-01:** [Decision or preference captured] -- **D-02:** [Another decision if applicable] - -### [Category 2 that was discussed] -- **D-03:** [Decision or preference captured] - -### Claude's Discretion -[Areas where user said "you decide" — note that Claude has flexibility here] - -### Folded Todos -[If any todos were folded into scope from the cross_reference_todos step, list them here. -Each entry should include the todo title, original problem, and how it fits this phase's scope. -If no todos were folded: omit this subsection entirely.] - - - - -## Canonical References - -**Downstream agents MUST read these before planning or implementing.** - -[MANDATORY section. Write the FULL accumulated canonical refs list here. -Sources: ROADMAP.md refs + REQUIREMENTS.md refs + user-referenced docs during -discussion + any docs discovered during codebase scout. Group by topic area. -Every entry needs a full relative path — not just a name.] - -### [Topic area 1] -- `path/to/adr-or-spec.md` — [What it decides/defines that's relevant] -- `path/to/doc.md` §N — [Specific section reference] - -### [Topic area 2] -- `path/to/feature-doc.md` — [What this doc defines] - -[If no external specs: "No external specs — requirements fully captured in decisions above"] - - - - -## Existing Code Insights - -### Reusable Assets -- [Component/hook/utility]: [How it could be used in this phase] - -### Established Patterns -- [Pattern]: [How it constrains/enables this phase] - -### Integration Points -- [Where new code connects to existing system] - - - - -## Specific Ideas - -[Any particular references, examples, or "I want it like X" moments from discussion] - -[If none: "No specific requirements — open to standard approaches"] - - - - -## Deferred Ideas - -[Ideas that came up but belong in other phases. Don't lose them.] - -### Reviewed Todos (not folded) -[If any todos were reviewed in cross_reference_todos but not folded into scope, -list them here so future phases know they were considered. -Each entry: todo title + reason it was deferred (out of scope, belongs in Phase Y, etc.) -If no reviewed-but-deferred todos: omit this subsection entirely.] - -[If none: "None — discussion stayed within phase scope"] - - - ---- - -*Phase: XX-name* -*Context gathered: [date]* -``` - -Write file. +Write the file. @@ -1127,10 +403,6 @@ Present summary and next steps: Created: .planning/phases/${PADDED_PHASE}-${SLUG}/${PADDED_PHASE}-CONTEXT.md ## Decisions Captured - -### [Category] -- [Key decision] - ### [Category] - [Key decision] @@ -1150,69 +422,28 @@ Created: .planning/phases/${PADDED_PHASE}-${SLUG}/${PADDED_PHASE}-CONTEXT.md --- -**Also available:** -- `/gsd:discuss-phase ${PHASE} --chain ${GSD_WS}` — re-run with auto plan+execute after -- `/gsd:plan-phase ${PHASE} --skip-research ${GSD_WS}` — plan without research -- `/gsd:ui-phase ${PHASE} ${GSD_WS}` — generate UI design contract before planning (if phase has frontend work) -- Review/edit CONTEXT.md before continuing - ---- +**Also available:** `--chain` for auto plan+execute after; `/gsd:plan-phase ${PHASE} --skip-research ${GSD_WS}` to plan without research; `/gsd:ui-phase ${PHASE} ${GSD_WS}` for UI design contracts; review/edit CONTEXT.md before continuing. ``` -**Write DISCUSSION-LOG.md before committing:** +**Write DISCUSSION-LOG.md before committing.** **File location:** `${phase_dir}/${padded_phase}-DISCUSSION-LOG.md` -```markdown -# Phase [X]: [Name] - Discussion Log - -> **Audit trail only.** Do not use as input to planning, research, or execution agents. -> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered. - -**Date:** [ISO date] -**Phase:** [phase number]-[phase name] -**Areas discussed:** [comma-separated list] - ---- - -[For each gray area discussed:] - -## [Area Name] - -| Option | Description | Selected | -|--------|-------------|----------| -| [Option 1] | [Description from AskUserQuestion] | | -| [Option 2] | [Description] | ✓ | -| [Option 3] | [Description] | | - -**User's choice:** [Selected option or free-text response] -**Notes:** [Any clarifications, follow-up context, or rationale the user provided] - ---- - -[Repeat for each area] - -## Claude's Discretion - -[List areas where user said "you decide" or deferred to Claude] - -## Deferred Ideas - -[Ideas mentioned during discussion that were noted for future phases] +**Read the DISCUSSION-LOG.md template now (lazy-loaded):** +``` +Read(workflows/discuss-phase/templates/discussion-log.md) ``` -Write file. +Substitute live values from the discussion log accumulator (area names, options presented, user selections, notes, deferred ideas, Claude's discretion items). Write the file. **Clean up checkpoint file** — CONTEXT.md is now the canonical record: - ```bash rm -f "${phase_dir}/${padded_phase}-DISCUSS-CHECKPOINT.json" ``` Commit phase context and discussion log: - ```bash gsd-sdk query commit "docs(${padded_phase}): capture phase context" "${phase_dir}/${padded_phase}-CONTEXT.md" "${phase_dir}/${padded_phase}-DISCUSSION-LOG.md" ``` @@ -1227,121 +458,40 @@ Update STATE.md with session info: gsd-sdk query state.record-session \ --stopped-at "Phase ${PHASE} context gathered" \ --resume-file "${phase_dir}/${padded_phase}-CONTEXT.md" -``` -Commit STATE.md: - -```bash gsd-sdk query commit "docs(state): record phase ${PHASE} context session" .planning/STATE.md ``` -Check for auto-advance trigger: +Auto-advance behavior is defined in `workflows/discuss-phase/modes/chain.md`. -1. Parse `--auto` and `--chain` flags from $ARGUMENTS. Note: --all is NOT an auto-advance trigger — it only affects area selection. A session with `--all` but without `--auto` or `--chain` returns to manual next-steps after discussion completes. -2. **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): - ```bash - if [[ ! "$ARGUMENTS" =~ --auto ]] && [[ ! "$ARGUMENTS" =~ --chain ]]; then - gsd-sdk query config-set workflow._auto_chain_active false 2>/dev/null - fi - ``` -3. Read consolidated auto-mode (`active` = chain flag OR user preference): - ```bash - AUTO_MODE=$(gsd-sdk query check auto-mode --pick active 2>/dev/null || echo "false") - ``` +If `--auto`, `--chain`, or `workflow.auto_advance` is enabled, Read that file now and execute its `auto_advance` step (which handles flag-syncing, banner display, plan-phase Skill dispatch, and return-status branching). -**If `--auto` or `--chain` flag present AND `AUTO_MODE` is not true:** Persist chain flag to config (handles direct usage without new-project): -```bash -gsd-sdk query config-set workflow._auto_chain_active true -``` - -**If `--auto` flag present OR `--chain` flag present OR `AUTO_MODE` is true:** - -Display banner: -``` -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - GSD ► AUTO-ADVANCING TO PLAN -━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - -Context captured. Launching plan-phase... -``` - -Launch plan-phase using the Skill tool to avoid nested Task sessions (which cause runtime freezes due to deep agent nesting — see #686): -``` -Skill(skill="gsd-plan-phase", args="${PHASE} --auto ${GSD_WS}") -``` - -This keeps the auto-advance chain flat — discuss, plan, and execute all run at the same nesting level rather than spawning increasingly deep Task agents. - -**Handle plan-phase return:** -- **PHASE COMPLETE** → Full chain succeeded. Display: - ``` - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - GSD ► PHASE ${PHASE} COMPLETE - ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - - Auto-advance pipeline finished: discuss → plan → execute - - /clear then: - - Next: /gsd:discuss-phase ${NEXT_PHASE} ${WAS_CHAIN ? "--chain" : "--auto"} ${GSD_WS} - ``` -- **PLANNING COMPLETE** → Planning done, execution didn't complete: - ``` - Auto-advance partial: Planning complete, execution did not finish. - Continue: /gsd:execute-phase ${PHASE} ${GSD_WS} - ``` -- **PLANNING INCONCLUSIVE / CHECKPOINT** → Stop chain: - ``` - Auto-advance stopped: Planning needs input. - Continue: /gsd:plan-phase ${PHASE} ${GSD_WS} - ``` -- **GAPS FOUND** → Stop chain: - ``` - Auto-advance stopped: Gaps found during execution. - Continue: /gsd:plan-phase ${PHASE} --gaps ${GSD_WS} - ``` - -**If none of `--auto`, `--chain`, nor config enabled:** -Route to `confirm_creation` step (existing behavior — show manual next steps). +Otherwise, route to `confirm_creation` (manual next steps). - -When `--power` flag is present in ARGUMENTS, skip interactive questioning and execute the power user workflow. - -The power user mode generates ALL questions upfront into machine-readable and human-friendly files, then waits for the user to answer at their own pace before processing all answers in a single pass. - -**Full step-by-step instructions:** @~/.claude/get-shit-done/workflows/discuss-phase-power.md - -**Summary of flow:** -1. Run the same phase analysis (gray area identification) as standard mode -2. Write all questions to `{phase_dir}/{padded_phase}-QUESTIONS.json` and `{phase_dir}/{padded_phase}-QUESTIONS.html` -3. Notify user with file paths and wait for a "refresh" or "finalize" command -4. On "refresh": read the JSON, process answered questions, update stats and HTML -5. On "finalize": read all answers from JSON, generate CONTEXT.md in the standard format - - - Phase validated against roadmap - Prior context loaded (PROJECT.md, REQUIREMENTS.md, STATE.md, prior CONTEXT.md files) - Already-decided questions not re-asked (carried forward from prior phases) - Codebase scouted for reusable assets, patterns, and integration points -- Gray areas identified through intelligent analysis with code and prior decision annotations -- User selected which areas to discuss -- Each selected area explored until user satisfied (with code-informed and prior-decision-informed options) +- Gray areas identified with code and prior-decision annotations +- User selected which areas to discuss (or `--all`/`--auto` auto-selected) +- Each selected area explored under the active mode's rules until satisfied - Scope creep redirected to deferred ideas - CONTEXT.md captures actual decisions, not vague vision -- CONTEXT.md includes canonical_refs section with full file paths to every spec/ADR/doc downstream agents need (MANDATORY — never omit) +- CONTEXT.md includes canonical_refs section with full file paths to every spec/ADR/doc downstream agents need (MANDATORY) - CONTEXT.md includes code_context section with reusable assets and patterns - Deferred ideas preserved for future phases - STATE.md updated with session info - User knows next steps - Checkpoint file written after each area completes (incremental save) -- Interrupted sessions can be resumed from checkpoint (no re-answering completed areas) +- Interrupted sessions can be resumed from checkpoint - Checkpoint file cleaned up after successful CONTEXT.md write - `--chain` triggers interactive discuss followed by auto plan+execute (no auto-answering) - `--chain` and `--auto` both persist chain flag and auto-advance to plan-phase +- Per-mode bodies, templates, and advisor flow are lazy-loaded — parent stays under the workflow size budget enforced by `tests/workflow-size-budget.test.cjs` diff --git a/get-shit-done/workflows/discuss-phase/modes/advisor.md b/get-shit-done/workflows/discuss-phase/modes/advisor.md new file mode 100644 index 00000000..0dd12393 --- /dev/null +++ b/get-shit-done/workflows/discuss-phase/modes/advisor.md @@ -0,0 +1,173 @@ +# Advisor mode — research-backed comparison tables + +> **Lazy-loaded and gated.** The parent `workflows/discuss-phase.md` Reads +> this file ONLY when `ADVISOR_MODE` is true (i.e., when +> `$HOME/.claude/get-shit-done/USER-PROFILE.md` exists). Skip the Read +> entirely when no profile is present — that's the inverse of the +> `--advisor` flag from #2174 (don't pay the cost when unused). + +## Activation + +```bash +PROFILE_PATH="$HOME/.claude/get-shit-done/USER-PROFILE.md" +if [ -f "$PROFILE_PATH" ]; then + ADVISOR_MODE=true +else + ADVISOR_MODE=false +fi +``` + +If `ADVISOR_MODE` is false, do **not** Read this file — proceed with the +standard `default.md` discussion flow. + +## Calibration tier + +Resolve `vendor_philosophy` calibration tier: +1. **Priority 1:** Read `config.json` > `preferences.vendor_philosophy` + (project-level override) +2. **Priority 2:** Read USER-PROFILE.md `Vendor Choices/Philosophy` rating + (global) +3. **Priority 3:** Default to `"standard"` if neither has a value or value + is `UNSCORED` + +Map to calibration tier: +- `conservative` OR `thorough-evaluator` → `full_maturity` +- `opinionated` → `minimal_decisive` +- `pragmatic-fast` OR any other value OR empty → `standard` + +Resolve advisor model: +```bash +ADVISOR_MODEL=$(gsd-sdk query resolve-model gsd-advisor-researcher --raw) +``` + +## Non-technical owner detection + +Read USER-PROFILE.md and check for product-owner signals: + +```bash +PROFILE_CONTENT=$(cat "$HOME/.claude/get-shit-done/USER-PROFILE.md" 2>/dev/null || true) +``` + +Set `NON_TECHNICAL_OWNER = true` if ANY of the following are present: +- `learning_style: guided` +- The word `jargon` appears in a `frustration_triggers` section +- `explanation_depth: practical-detailed` (without a technical modifier) +- `explanation_depth: high-level` + +**Tie-breaker / precedence (when signals conflict):** +1. An explicit `technical_background: true` (or any `explanation_depth` value + tagged with a technical modifier such as `practical-detailed:technical`) + **overrides** all inferred non-technical signals — set + `NON_TECHNICAL_OWNER = false`. +2. Otherwise, ANY single matching signal is sufficient to set + `NON_TECHNICAL_OWNER = true` (signals are OR-aggregated, not weighted). +3. Contradictory `explanation_depth` values: the most recent entry wins. + +Log the resolved value and the matched/overriding signal so the user can +audit why a given framing was used. + +When `NON_TECHNICAL_OWNER` is true, reframe gray area labels and +descriptions in product-outcome language before presenting them. Preserve +the same underlying decision — only change the framing: + +- Technical implementation term → outcome the user will experience + - "Token architecture" → "Color system: which approach prevents the dark theme from flashing white on open" + - "CSS variable strategy" → "Theme colors: how your brand colors stay consistent in both light and dark mode" + - "Component API surface area" → "How the building blocks connect: how tightly coupled should these parts be" + - "Caching strategy: SWR vs React Query" → "Loading speed: should screens show saved data right away or wait for fresh data" + +This reframing applies to: +1. Gray area labels and descriptions in `present_gray_areas` +2. Advisor research rationale rewrites in the synthesis step below + +## advisor_research step + +After the user selects gray areas in `present_gray_areas`, spawn parallel +research agents. + +1. Display brief status: `Researching {N} areas...` + +2. For EACH user-selected gray area, spawn a `Task()` in parallel: + + ``` + Task( + prompt="First, read @~/.claude/agents/gsd-advisor-researcher.md for your role and instructions. + + {area_name}: {area_description from gray area identification} + {phase_goal and description from ROADMAP.md} + {project name and brief description from PROJECT.md} + {resolved calibration tier: full_maturity | standard | minimal_decisive} + + Research this gray area and return a structured comparison table with rationale. + ${AGENT_SKILLS_ADVISOR}", + subagent_type="general-purpose", + model="{ADVISOR_MODEL}", + description="Research: {area_name}" + ) + ``` + + All `Task()` calls spawn simultaneously — do NOT wait for one before + starting the next. + +3. After ALL agents return, **synthesize results** before presenting: + + For each agent's return: + a. Parse the markdown comparison table and rationale paragraph + b. Verify all 5 columns present (Option | Pros | Cons | Complexity | Recommendation) — fill any missing columns rather than showing broken table + c. Verify option count matches calibration tier: + - `full_maturity`: 3-5 options acceptable + - `standard`: 2-4 options acceptable + - `minimal_decisive`: 1-2 options acceptable + If agent returned too many, trim least viable. If too few, accept as-is. + d. Rewrite rationale paragraph to weave in project context and ongoing discussion context that the agent did not have access to + e. If agent returned only 1 option, convert from table format to direct recommendation: "Standard approach for {area}: {option}. {rationale}" + f. **If `NON_TECHNICAL_OWNER` is true:** apply a plain language rewrite to the rationale paragraph. Replace implementation-level terms with outcome descriptions the user can reason about without technical context. The Recommendation column value and the table structure remain intact. Do not remove detail; translate it. Example: "SWR uses stale-while-revalidate to serve cached responses immediately" → "This approach shows you something right away, then quietly updates in the background — users see data instantly." + +4. Store synthesized tables for use in `discuss_areas` (table-first flow). + +## discuss_areas (advisor table-first flow) + +For each selected area: + +1. **Present the synthesized comparison table + rationale paragraph** (from + `advisor_research`) + +2. **Use AskUserQuestion** (or text-mode equivalent if `--text` overlay): + - header: `{area_name}` + - question: `Which approach for {area_name}?` + - options: extract from the table's Option column (AskUserQuestion adds + "Other" automatically) + +3. **Record the user's selection:** + - If user picks from table options → record as locked decision for that + area + - If user picks "Other" → receive their input, reflect it back for + confirmation, record + +4. **Thinking partner (conditional):** same rule as default mode — if + `features.thinking_partner` is enabled and tradeoff signals are + detected, offer a 3-5 bullet analysis before locking in. + +5. **After recording pick, decide whether follow-up questions are needed:** + - If the pick has ambiguity that would affect downstream planning → + ask 1-2 targeted follow-up questions using AskUserQuestion + - If the pick is clear and self-contained → move to next area + - Do NOT ask the standard 4 questions — the table already provided the + context + +6. **After all areas processed:** + - header: "Done" + - question: "That covers [list areas]. Ready to create context?" + - options: "Create context" / "Revisit an area" + +## Scope creep handling (advisor mode) + +If user mentions something outside the phase domain: +``` +"[Feature] sounds like a new capability — that belongs in its own phase. +I'll note it as a deferred idea. + +Back to [current area]: [return to current question]" +``` + +Track deferred ideas internally. diff --git a/get-shit-done/workflows/discuss-phase/modes/all.md b/get-shit-done/workflows/discuss-phase/modes/all.md new file mode 100644 index 00000000..50fa9d06 --- /dev/null +++ b/get-shit-done/workflows/discuss-phase/modes/all.md @@ -0,0 +1,28 @@ +# --all mode — auto-select ALL gray areas, discuss interactively + +> **Lazy-loaded.** Read this file from `workflows/discuss-phase.md` when +> `--all` is present in `$ARGUMENTS`. Behavior overlays the default mode. + +## Effect + +- In `present_gray_areas`: auto-select ALL gray areas without asking the user + (skips the AskUserQuestion area-selection step). +- Discussion for each area proceeds **fully interactively** — the user drives + every question for every area (use the default-mode `discuss_areas` flow). +- Does NOT auto-advance to plan-phase afterward — use `--chain` or `--auto` + if you want auto-advance. +- Log: `[--all] Auto-selected all gray areas: [list area names].` + +## Why this mode exists + +This is the "discuss everything" shortcut: skip the selection friction, keep +full interactive control over each individual question. + +## Combination rules + +- `--all --auto`: `--auto` wins for the discussion phase too (Claude picks + recommended answers); `--all`'s contribution is just area auto-selection. +- `--all --chain`: areas auto-selected, discussion interactive, then + auto-advance to plan/execute (chain semantics). +- `--all --batch` / `--all --text` / `--all --analyze`: layered overlays + apply during discussion as documented in their respective files. diff --git a/get-shit-done/workflows/discuss-phase/modes/analyze.md b/get-shit-done/workflows/discuss-phase/modes/analyze.md new file mode 100644 index 00000000..b373da11 --- /dev/null +++ b/get-shit-done/workflows/discuss-phase/modes/analyze.md @@ -0,0 +1,44 @@ +# --analyze mode — trade-off tables before each question + +> **Lazy-loaded overlay.** Read this file from `workflows/discuss-phase.md` +> when `--analyze` is present in `$ARGUMENTS`. Combinable with default, +> `--all`, `--chain`, `--text`, `--batch`. + +## Effect + +Before presenting each question (or question group, in batch mode), provide +a brief **trade-off analysis** for the decision: +- 2-3 options with pros/cons based on codebase context and common patterns +- A recommended approach with reasoning +- Known pitfalls or constraints from prior phases + +## Example + +```markdown +**Trade-off analysis: Authentication strategy** + +| Approach | Pros | Cons | +|----------|------|------| +| Session cookies | Simple, httpOnly prevents XSS | Requires CSRF protection, sticky sessions | +| JWT (stateless) | Scalable, no server state | Token size, revocation complexity | +| OAuth 2.0 + PKCE | Industry standard for SPAs | More setup, redirect flow UX | + +💡 Recommended: OAuth 2.0 + PKCE — your app has social login in requirements (REQ-04) and this aligns with the existing NextAuth setup in `src/lib/auth.ts`. + +How should users authenticate? +``` + +This gives the user context to make informed decisions without extra +prompting. + +When `--analyze` is absent, present questions directly as before (no +trade-off table). + +## Sourcing the analysis + +- Pros/cons should reflect the codebase context loaded in `scout_codebase` + and any prior decisions surfaced in `load_prior_context`. +- The recommendation must explicitly tie to project context (e.g., + existing libraries, prior phase decisions, documented requirements). +- If a related ADR or spec is referenced in CONTEXT.md ``, + cite it in the recommendation. diff --git a/get-shit-done/workflows/discuss-phase/modes/auto.md b/get-shit-done/workflows/discuss-phase/modes/auto.md new file mode 100644 index 00000000..977ae49c --- /dev/null +++ b/get-shit-done/workflows/discuss-phase/modes/auto.md @@ -0,0 +1,56 @@ +# --auto mode — fully autonomous discuss-phase + +> **Lazy-loaded.** Read this file from `workflows/discuss-phase.md` when +> `--auto` is present in `$ARGUMENTS`. After the discussion completes, the +> parent's `auto_advance` step also reads `modes/chain.md` to drive the +> auto-advance to plan-phase. + +## Effect across steps + +- **`check_existing`**: if CONTEXT.md exists, auto-select "Update it" — load + existing context and continue to `analyze_phase` (matches the parent step's + documented `--auto` branch). If no context exists, continue without + prompting. For interrupted checkpoints, auto-select "Resume". For existing + plans, auto-select "Continue and replan after". Log every decision so the + user can audit. +- **`cross_reference_todos`**: fold all todos with relevance score >= 0.4 + automatically. Log the selection. +- **`present_gray_areas`**: auto-select ALL gray areas. Log: + `[--auto] Selected all gray areas: [list area names].` +- **`discuss_areas`**: for each discussion question, choose the recommended + option (first option, or the one explicitly marked "recommended") **without + using AskUserQuestion**. Skip interactive prompts entirely. Log each + auto-selected choice inline so the user can review decisions in the + context file: + ``` + [auto] [Area] — Q: "[question text]" → Selected: "[chosen option]" (recommended default) + ``` +- After all areas are auto-resolved, skip the "Explore more gray areas" + prompt and proceed directly to `write_context`. +- After `write_context`, **auto-advance** to plan-phase via `modes/chain.md`. + +## CRITICAL — Auto-mode pass cap + +In `--auto` mode, the discuss step MUST complete in a **single pass**. After +writing CONTEXT.md once, you are DONE — proceed immediately to +`write_context` and then auto_advance. Do NOT re-read your own CONTEXT.md to +find "gaps", "undefined types", or "missing decisions" and run additional +passes. This creates a self-feeding loop where each pass generates references +that the next pass treats as gaps, consuming unbounded time and resources. + +Check the pass cap from config: +```bash +MAX_PASSES=$(gsd-sdk query config-get workflow.max_discuss_passes 2>/dev/null || echo "3") +``` + +If you have already written and committed CONTEXT.md, the discuss step is +complete. Move on. + +## Combination rules + +- `--auto --text` / `--auto --batch`: text/batch overlays are no-ops in + auto mode (no user prompts to render). +- `--auto --analyze`: trade-off tables can still be logged for the audit + trail; selection still uses the recommended option. +- `--auto --power`: `--power` wins (power mode generates files for offline + answering — incompatible with autonomous selection). diff --git a/get-shit-done/workflows/discuss-phase/modes/batch.md b/get-shit-done/workflows/discuss-phase/modes/batch.md new file mode 100644 index 00000000..c62b25d5 --- /dev/null +++ b/get-shit-done/workflows/discuss-phase/modes/batch.md @@ -0,0 +1,52 @@ +# --batch mode — grouped question batches + +> **Lazy-loaded overlay.** Read this file from `workflows/discuss-phase.md` +> when `--batch` is present in `$ARGUMENTS`. Combinable with default, +> `--all`, `--chain`, `--text`, `--analyze`. + +## Argument parsing + +Parse optional `--batch` from `$ARGUMENTS`: +- Accept `--batch`, `--batch=N`, or `--batch N` +- Default to **4 questions per batch** when no number is provided +- Clamp explicit sizes to **2–5** so a batch stays answerable +- If `--batch` is absent, keep the existing one-question-at-a-time flow + (default mode). + +## Effect on discuss_areas + +`--batch` mode: ask **2–5 numbered questions in one plain-text turn** per +area, instead of the default 4 single-question AskUserQuestion turns. + +- Group closely related questions for the current area into a single + message +- Keep each question concrete and answerable in one reply +- When options are helpful, include short inline choices per question + rather than a separate AskUserQuestion for every item +- After the user replies, reflect back the captured decisions, note any + unanswered items, and ask only the minimum follow-up needed before + moving on +- Preserve adaptiveness between batches: use the full set of answers to + decide the next batch or whether the area is sufficiently clear + +## Philosophy + +Stay adaptive, but let the user choose the pacing. +- Default mode: 4 single-question turns, then check whether to continue +- `--batch` mode: 1 grouped turn with 2–5 numbered questions, then check + whether to continue + +Each answer set should reveal the next question or next batch. + +## Example batch + +``` +Authentication — please answer 1–4: + +1. Which auth strategy? (a) Session cookies (b) JWT (c) OAuth 2.0 + PKCE +2. Where do tokens live? (a) httpOnly cookie (b) localStorage (c) memory only +3. Session lifetime? (a) 1h (b) 24h (c) 30d (d) configurable +4. Account recovery? (a) email reset (b) magic link (c) both + +Reply with your choices (e.g. "1c, 2a, 3b, 4c") or describe in your own words. +``` diff --git a/get-shit-done/workflows/discuss-phase/modes/chain.md b/get-shit-done/workflows/discuss-phase/modes/chain.md new file mode 100644 index 00000000..0806b174 --- /dev/null +++ b/get-shit-done/workflows/discuss-phase/modes/chain.md @@ -0,0 +1,97 @@ +# --chain mode — interactive discuss, then auto-advance + +> **Lazy-loaded.** Read this file from `workflows/discuss-phase.md` when +> `--chain` is present in `$ARGUMENTS`, or when the parent's `auto_advance` +> step needs to dispatch to plan-phase under `--auto`. + +## Effect + +- Discussion is **fully interactive** — questions, gray-area selection, and + follow-ups behave exactly the same as default mode. +- After discussion completes, **auto-advance to plan-phase → execute-phase** + (same downstream behavior as `--auto`). +- This is the middle ground: the user controls the discuss decisions, then + plan and execute run autonomously. + +## auto_advance step (executed by the parent file) + +1. Parse `--auto` and `--chain` flags from `$ARGUMENTS`. **Note:** `--all` + is NOT an auto-advance trigger — it only affects area selection. A + session with `--all` but without `--auto` or `--chain` returns to manual + next-steps after discussion completes. + +2. **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): + ```bash + if [[ ! "$ARGUMENTS" =~ --auto ]] && [[ ! "$ARGUMENTS" =~ --chain ]]; then + gsd-sdk query config-set workflow._auto_chain_active false 2>/dev/null + fi + ``` + +3. Read consolidated auto-mode (`active` = chain flag OR user preference): + ```bash + AUTO_MODE=$(gsd-sdk query check auto-mode --pick active 2>/dev/null || echo "false") + ``` + +4. **If `--auto` or `--chain` flag present AND `AUTO_MODE` is not true:** + Persist chain flag to config (handles direct usage without new-project): + ```bash + gsd-sdk query config-set workflow._auto_chain_active true + ``` + +5. **If `--auto` flag present OR `--chain` flag present OR `AUTO_MODE` is + true:** display banner and launch plan-phase. + + Banner: + ``` + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + GSD ► AUTO-ADVANCING TO PLAN + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + Context captured. Launching plan-phase... + ``` + + Launch plan-phase using the Skill tool to avoid nested Task sessions + (which cause runtime freezes due to deep agent nesting — see #686): + ``` + Skill(skill="gsd-plan-phase", args="${PHASE} --auto ${GSD_WS}") + ``` + + This keeps the auto-advance chain flat — discuss, plan, and execute all + run at the same nesting level rather than spawning increasingly deep + Task agents. + +6. **Handle plan-phase return:** + + - **PHASE COMPLETE** → Full chain succeeded. Display: + ``` + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + GSD ► PHASE ${PHASE} COMPLETE + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + Auto-advance pipeline finished: discuss → plan → execute + + /clear then: + + Next: /gsd:discuss-phase ${NEXT_PHASE} ${WAS_CHAIN ? "--chain" : "--auto"} ${GSD_WS} + ``` + - **PLANNING COMPLETE** → Planning done, execution didn't complete: + ``` + Auto-advance partial: Planning complete, execution did not finish. + Continue: /gsd:execute-phase ${PHASE} ${GSD_WS} + ``` + - **PLANNING INCONCLUSIVE / CHECKPOINT** → Stop chain: + ``` + Auto-advance stopped: Planning needs input. + Continue: /gsd:plan-phase ${PHASE} ${GSD_WS} + ``` + - **GAPS FOUND** → Stop chain: + ``` + Auto-advance stopped: Gaps found during execution. + Continue: /gsd:plan-phase ${PHASE} --gaps ${GSD_WS} + ``` + +7. **If none of `--auto`, `--chain`, nor config enabled:** route to + `confirm_creation` step (existing behavior — show manual next steps). diff --git a/get-shit-done/workflows/discuss-phase/modes/default.md b/get-shit-done/workflows/discuss-phase/modes/default.md new file mode 100644 index 00000000..fb54e71e --- /dev/null +++ b/get-shit-done/workflows/discuss-phase/modes/default.md @@ -0,0 +1,141 @@ +# Default mode — interactive discuss-phase + +> **Lazy-loaded.** Read this file from `workflows/discuss-phase.md` when no +> mode flag is present (the baseline interactive flow). When `--text`, +> `--batch`, or `--analyze` is also present, layer the corresponding overlay +> file from this directory on top of the rules below. + +This document defines `discuss_areas` for the default flow. The shared steps +that come before (`initialize`, `check_blocking_antipatterns`, `check_spec`, +`check_existing`, `load_prior_context`, `cross_reference_todos`, +`scout_codebase`, `analyze_phase`, `present_gray_areas`) live in the parent +file and run for every mode. + +## discuss_areas (default, interactive) + +For each selected area, conduct a focused discussion loop. + +**Research-before-questions mode:** Check if `workflow.research_before_questions` is enabled in config (from init context or `.planning/config.json`). When enabled, before presenting questions for each area: +1. Do a brief web search for best practices related to the area topic +2. Summarize the top findings in 2-3 bullet points +3. Present the research alongside the question so the user can make a more informed decision + +Example with research enabled: +```text +Let's talk about [Authentication Strategy]. + +📊 Best practices research: +• OAuth 2.0 + PKCE is the current standard for SPAs (replaces implicit flow) +• Session tokens with httpOnly cookies preferred over localStorage for XSS protection +• Consider passkey/WebAuthn support — adoption is accelerating in 2025-2026 + +With that context: How should users authenticate? +``` + +When disabled (default), skip the research and present questions directly as before. + +**Philosophy:** stay adaptive. Default flow is 4 single-question turns, then +check whether to continue. Each answer should reveal the next question. + +**For each area:** + +1. **Announce the area:** + ```text + Let's talk about [Area]. + ``` + +2. **Ask 4 questions using AskUserQuestion:** + - header: "[Area]" (max 12 chars — abbreviate if needed) + - question: Specific decision for this area + - options: 2-3 concrete choices (AskUserQuestion adds "Other" automatically), with the recommended choice highlighted and brief explanation why + - **Annotate options with code context** when relevant: + ```text + "How should posts be displayed?" + - Cards (reuses existing Card component — consistent with Messages) + - List (simpler, would be a new pattern) + - Timeline (needs new Timeline component — none exists yet) + ``` + - Include "You decide" as an option when reasonable — captures Claude discretion + - **Context7 for library choices:** When a gray area involves library selection (e.g., "magic links" → query next-auth docs) or API approach decisions, use `mcp__context7__*` tools to fetch current documentation and inform the options. Don't use Context7 for every question — only when library-specific knowledge improves the options. + +3. **After the current set of questions, check:** + - header: "[Area]" (max 12 chars) + - question: "More questions about [area], or move to next? (Remaining: [list other unvisited areas])" + - options: "More questions" / "Next area" + + When building the question text, list the remaining unvisited areas so the user knows what's ahead. For example: "More questions about Layout, or move to next? (Remaining: Loading behavior, Content ordering)" + + If "More questions" → ask another 4 single questions, then check again + If "Next area" → proceed to next selected area + If "Other" (free text) → interpret intent: continuation phrases ("chat more", "keep going", "yes", "more") map to "More questions"; advancement phrases ("done", "move on", "next", "skip") map to "Next area". If ambiguous, ask: "Continue with more questions about [area], or move to the next area?" + +4. **After all initially-selected areas complete:** + - Summarize what was captured from the discussion so far + - AskUserQuestion: + - header: "Done" + - question: "We've discussed [list areas]. Which gray areas remain unclear?" + - options: "Explore more gray areas" / "I'm ready for context" + - If "Explore more gray areas": + - Identify 2-4 additional gray areas based on what was learned + - Return to present_gray_areas logic with these new areas + - Loop: discuss new areas, then prompt again + - If "I'm ready for context": Proceed to write_context + +**Canonical ref accumulation during discussion:** +When the user references a doc, spec, or ADR during any answer — e.g., "read adr-014", "check the MCP spec", "per browse-spec.md" — immediately: +1. Read the referenced doc (or confirm it exists) +2. Add it to the canonical refs accumulator with full relative path +3. Use what you learned from the doc to inform subsequent questions + +These user-referenced docs are often MORE important than ROADMAP.md refs because they represent docs the user specifically wants downstream agents to follow. Never drop them. + +**Question design:** +- Options should be concrete, not abstract ("Cards" not "Option A") +- Each answer should inform the next question or next batch +- If user picks "Other" to provide freeform input (e.g., "let me describe it", "something else", or an open-ended reply), ask your follow-up as plain text — NOT another AskUserQuestion. Wait for them to type at the normal prompt, then reflect their input back and confirm before resuming AskUserQuestion or the next numbered batch. + +**Thinking partner (conditional):** +If `features.thinking_partner` is enabled in config, check the user's answer for tradeoff signals +(see `references/thinking-partner.md` for signal list). If tradeoff detected: + +```text +I notice competing priorities here — {option_A} optimizes for {goal_A} while {option_B} optimizes for {goal_B}. + +Want me to think through the tradeoffs before we lock this in? +[Yes, analyze] / [No, decision made] +``` + +If yes: provide 3-5 bullet analysis (what each optimizes/sacrifices, alignment with PROJECT.md goals, recommendation). Then return to normal flow. + +**Scope creep handling:** +If user mentions something outside the phase domain: +```text +"[Feature] sounds like a new capability — that belongs in its own phase. +I'll note it as a deferred idea. + +Back to [current area]: [return to current question]" +``` + +Track deferred ideas internally. + +**Incremental checkpoint — save after each area completes:** + +After each area is resolved (user says "Next area"), immediately write a checkpoint file with all decisions captured so far. This prevents data loss if the session is interrupted mid-discussion. + +**Checkpoint file:** `${phase_dir}/${padded_phase}-DISCUSS-CHECKPOINT.json` + +Schema: read `workflows/discuss-phase/templates/checkpoint.json` for the +canonical structure — copy it and substitute the live values. + +**On session resume:** Handled in the parent's `check_existing` step. After +`write_context` completes successfully, the parent's `git_commit` step +deletes the checkpoint. + +**Track discussion log data internally:** +For each question asked, accumulate: +- Area name +- All options presented (label + description) +- Which option the user selected (or their free-text response) +- Any follow-up notes or clarifications the user provided + +This data is used to generate DISCUSSION-LOG.md in the parent's `git_commit` step. diff --git a/get-shit-done/workflows/discuss-phase/modes/power.md b/get-shit-done/workflows/discuss-phase/modes/power.md new file mode 100644 index 00000000..9277ad6d --- /dev/null +++ b/get-shit-done/workflows/discuss-phase/modes/power.md @@ -0,0 +1,44 @@ +# --power mode — bulk question generation, async answering + +> **Lazy-loaded.** Read this file from `workflows/discuss-phase.md` when +> `--power` is present in `$ARGUMENTS`. The full step-by-step instructions +> live in the existing `discuss-phase-power.md` workflow file (kept stable +> at its original path so installed `@`-references continue to resolve). + +## Dispatch + +``` +Read @~/.claude/get-shit-done/workflows/discuss-phase-power.md +``` + +Execute it end-to-end. Do not continue with the standard interactive steps. + +## Summary of flow + +The power user mode generates ALL questions upfront into machine-readable +and human-friendly files, then waits for the user to answer at their own +pace before processing all answers in a single pass. + +1. Run the same phase analysis (gray area identification) as standard mode +2. Write all questions to + `{phase_dir}/{padded_phase}-QUESTIONS.json` and + `{phase_dir}/{padded_phase}-QUESTIONS.html` +3. Notify user with file paths and wait for a "refresh" or "finalize" + command +4. On "refresh": read the JSON, process answered questions, update stats + and HTML +5. On "finalize": read all answers from JSON, generate CONTEXT.md in the + standard format + +## When to use + +Large phases with many gray areas, or when users prefer to answer +questions offline / asynchronously rather than interactively in the chat +session. + +## Combination rules + +- `--power --auto`: power wins. Power mode is incompatible with + autonomous selection — its purpose is offline answering. +- `--power --chain`: after the power-mode finalize step writes + CONTEXT.md, the chain auto-advance still applies (Read `chain.md`). diff --git a/get-shit-done/workflows/discuss-phase/modes/text.md b/get-shit-done/workflows/discuss-phase/modes/text.md new file mode 100644 index 00000000..cfc7ea19 --- /dev/null +++ b/get-shit-done/workflows/discuss-phase/modes/text.md @@ -0,0 +1,55 @@ +# --text mode — plain-text overlay (no AskUserQuestion) + +> **Lazy-loaded overlay.** Read this file from `workflows/discuss-phase.md` +> when `--text` is present in `$ARGUMENTS`, OR when +> `workflow.text_mode: true` is set in config (e.g., per-project default). + +## Effect + +When text mode is active, **do not use AskUserQuestion at all**. Instead, +present every question as a plain-text numbered list and ask the user to +type their choice number. Free-text input maps to the "Other" branch of +the equivalent AskUserQuestion call. + +This is required for Claude Code remote sessions (`/rc` mode) where the +Claude App cannot forward TUI menu selections back to the host. + +## Activation + +- Per-session: pass `--text` flag to any command (e.g., + `/gsd:discuss-phase --text`) +- Per-project: `gsd-sdk query config-set workflow.text_mode true` + +Text mode applies to ALL workflows in the session, not just discuss-phase. + +## Question rendering + +Replace this: +```text +AskUserQuestion( + header="Layout", + question="How should posts be displayed?", + options=["Cards", "List", "Timeline"] +) +``` + +With this: +```text +Layout — How should posts be displayed? + 1. Cards + 2. List + 3. Timeline + 4. Other (type freeform) + +Reply with a number, or describe your preference. +``` + +Wait for the user's reply at the normal prompt. Parse: +- Numeric reply → mapped to that option +- Free text → treated as "Other" — reflect it back, confirm, then proceed + +## Empty-answer handling + +The same answer-validation rules from the parent file apply: empty +responses trigger one retry, then a clarifying question. Do not proceed +with empty input. diff --git a/get-shit-done/workflows/discuss-phase/templates/checkpoint.json b/get-shit-done/workflows/discuss-phase/templates/checkpoint.json new file mode 100644 index 00000000..ac28aa34 --- /dev/null +++ b/get-shit-done/workflows/discuss-phase/templates/checkpoint.json @@ -0,0 +1,18 @@ +{ + "phase": "{PHASE_NUM}", + "phase_name": "{phase_name}", + "timestamp": "{ISO timestamp}", + "areas_completed": ["Area 1", "Area 2"], + "areas_remaining": ["Area 3", "Area 4"], + "decisions": { + "Area 1": [ + {"question": "...", "answer": "...", "options_presented": ["..."]}, + {"question": "...", "answer": "...", "options_presented": ["..."]} + ], + "Area 2": [ + {"question": "...", "answer": "...", "options_presented": ["..."]} + ] + }, + "deferred_ideas": ["..."], + "canonical_refs": ["..."] +} diff --git a/get-shit-done/workflows/discuss-phase/templates/context.md b/get-shit-done/workflows/discuss-phase/templates/context.md new file mode 100644 index 00000000..019b05aa --- /dev/null +++ b/get-shit-done/workflows/discuss-phase/templates/context.md @@ -0,0 +1,136 @@ +# CONTEXT.md template — for discuss-phase write_context step + +> **Lazy-loaded.** Read this file only inside the `write_context` step of +> `workflows/discuss-phase.md`, immediately before writing +> `${phase_dir}/${padded_phase}-CONTEXT.md`. Do not put a reference to this +> file in `` — that defeats the progressive-disclosure +> savings introduced by issue #2551. + +## Variable substitutions + +The caller substitutes: +- `[X]` → phase number +- `[Name]` → phase name +- `[date]` → ISO date when context was gathered +- `${padded_phase}` → zero-padded phase number (e.g., `07`, `15`) +- `{N}` → counts (requirements, etc.) + +## Conditional sections + +- **``** — include only when `spec_loaded = true` (a `*-SPEC.md` + was found by `check_spec`). Otherwise omit the entire `` block. +- **Folded Todos / Reviewed Todos** — include subsections only when the + `cross_reference_todos` step folded or reviewed at least one todo. + +## Template body + +```markdown +# Phase [X]: [Name] - Context + +**Gathered:** [date] +**Status:** Ready for planning + + +## Phase Boundary + +[Clear statement of what this phase delivers — the scope anchor] + + + +[If spec_loaded = true, insert this section:] + +## Requirements (locked via SPEC.md) + +**{N} requirements are locked.** See `{padded_phase}-SPEC.md` for full requirements, boundaries, and acceptance criteria. + +Downstream agents MUST read `{padded_phase}-SPEC.md` before planning or implementing. Requirements are not duplicated here. + +**In scope (from SPEC.md):** [copy the "In scope" bullet list from SPEC.md Boundaries] +**Out of scope (from SPEC.md):** [copy the "Out of scope" bullet list from SPEC.md Boundaries] + + + + +## Implementation Decisions + +### [Category 1 that was discussed] +- **D-01:** [Decision or preference captured] +- **D-02:** [Another decision if applicable] + +### [Category 2 that was discussed] +- **D-03:** [Decision or preference captured] + +### Claude's Discretion +[Areas where user said "you decide" — note that Claude has flexibility here] + +### Folded Todos +[If any todos were folded into scope from the cross_reference_todos step, list them here. +Each entry should include the todo title, original problem, and how it fits this phase's scope. +If no todos were folded: omit this subsection entirely.] + + + + +## Canonical References + +**Downstream agents MUST read these before planning or implementing.** + +[MANDATORY section. Write the FULL accumulated canonical refs list here. +Sources: ROADMAP.md refs + REQUIREMENTS.md refs + user-referenced docs during +discussion + any docs discovered during codebase scout. Group by topic area. +Every entry needs a full relative path — not just a name.] + +### [Topic area 1] +- `path/to/adr-or-spec.md` — [What it decides/defines that's relevant] +- `path/to/doc.md` §N — [Specific section reference] + +### [Topic area 2] +- `path/to/feature-doc.md` — [What this doc defines] + +[If no external specs: "No external specs — requirements fully captured in decisions above"] + + + + +## Existing Code Insights + +### Reusable Assets +- [Component/hook/utility]: [How it could be used in this phase] + +### Established Patterns +- [Pattern]: [How it constrains/enables this phase] + +### Integration Points +- [Where new code connects to existing system] + + + + +## Specific Ideas + +[Any particular references, examples, or "I want it like X" moments from discussion] + +[If none: "No specific requirements — open to standard approaches"] + + + + +## Deferred Ideas + +[Ideas that came up but belong in other phases. Don't lose them.] + +### Reviewed Todos (not folded) +[If any todos were reviewed in cross_reference_todos but not folded into scope, +list them here so future phases know they were considered. +Each entry: todo title + reason it was deferred (out of scope, belongs in Phase Y, etc.) +If no reviewed-but-deferred todos: omit this subsection entirely.] + +[If none: "None — discussion stayed within phase scope"] + + + +--- + +*Phase: [X]-[Name]* +*Context gathered: [date]* +``` diff --git a/get-shit-done/workflows/discuss-phase/templates/discussion-log.md b/get-shit-done/workflows/discuss-phase/templates/discussion-log.md new file mode 100644 index 00000000..62a68684 --- /dev/null +++ b/get-shit-done/workflows/discuss-phase/templates/discussion-log.md @@ -0,0 +1,50 @@ +# DISCUSSION-LOG.md template — for discuss-phase git_commit step + +> **Lazy-loaded.** Read this file only inside the `git_commit` step of +> `workflows/discuss-phase.md`, immediately before writing +> `${phase_dir}/${padded_phase}-DISCUSSION-LOG.md`. + +## Purpose + +Audit trail for human review (compliance, learning, retrospectives). NOT +consumed by downstream agents — those read CONTEXT.md only. + +## Template body + +```markdown +# Phase [X]: [Name] - Discussion Log + +> **Audit trail only.** Do not use as input to planning, research, or execution agents. +> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered. + +**Date:** [ISO date] +**Phase:** [phase number]-[phase name] +**Areas discussed:** [comma-separated list] + +--- + +[For each gray area discussed:] + +## [Area Name] + +| Option | Description | Selected | +|--------|-------------|----------| +| [Option 1] | [Description from AskUserQuestion] | | +| [Option 2] | [Description] | ✓ | +| [Option 3] | [Description] | | + +**User's choice:** [Selected option or free-text response] +**Notes:** [Any clarifications, follow-up context, or rationale the user provided] + +--- + +[Repeat for each area] + +## Claude's Discretion + +[List areas where user said "you decide" or deferred to Claude] + +## Deferred Ideas + +[Ideas mentioned during discussion that were noted for future phases] +``` diff --git a/get-shit-done/workflows/execute-phase.md b/get-shit-done/workflows/execute-phase.md index 36bd8ef6..025f7488 100644 --- a/get-shit-done/workflows/execute-phase.md +++ b/get-shit-done/workflows/execute-phase.md @@ -1271,83 +1271,14 @@ If `TEXT_MODE` is true, present as a plain-text numbered list. Otherwise use Ask -Post-execution structural drift detection (#2003). Runs after the last wave -commits, before verification. **Non-blocking by contract:** any internal -error here MUST fall through and continue to `verify_phase_goal`. The phase +Post-execution structural drift detection (#2003). Non-blocking by contract: +any internal error here MUST fall through to `verify_phase_goal`. The phase is never failed by this gate. -```bash -DRIFT=$(gsd-sdk query verify.codebase-drift 2>/dev/null || echo '{"skipped":true,"reason":"sdk-failed"}') -``` - -Parse JSON for: `skipped`, `reason`, `action_required`, `directive`, -`spawn_mapper`, `affected_paths`, `elements`, `threshold`, `action`, -`last_mapped_commit`, `message`. - -**If `skipped` is true (no STRUCTURE.md, missing git, or any internal error):** -Log one line — `Codebase drift check skipped: {reason}` — and continue to -`verify_phase_goal`. Do NOT prompt the user. Do NOT block. - -**If `action_required` is false:** Continue silently to `verify_phase_goal`. - -**If `action_required` is true AND `directive` is `warn`:** -Print the `message` field verbatim. The format is: - -```text -Codebase drift detected: {N} structural element(s) since last mapping. - -New directories: - - {path} -New barrel exports: - - {path} -New migrations: - - {path} -New route modules: - - {path} - -Run /gsd:map-codebase --paths {affected_paths} to refresh planning context. -``` - -Then continue to `verify_phase_goal`. Do NOT block. Do NOT spawn anything. - -**If `action_required` is true AND `directive` is `auto-remap`:** - -First load the mapper agent's skill bundle (the executor's `AGENT_SKILLS` -from step `init_context` is for `gsd-executor`, not the mapper): - -```bash -AGENT_SKILLS_MAPPER=$(gsd-sdk query agent-skills gsd-codebase-mapper 2>/dev/null || true) -``` - -Then spawn `gsd-codebase-mapper` agents with the `--paths` hint: - -```text -Task( - subagent_type="gsd-codebase-mapper", - description="Incremental codebase remap (drift)", - prompt="Focus: arch -Today's date: {date} ---paths {affected_paths joined by comma} - -Refresh STRUCTURE.md and ARCHITECTURE.md scoped to the listed paths only. -Stamp last_mapped_commit in each document's frontmatter. -${AGENT_SKILLS_MAPPER}" -) -``` - -If the spawn fails or the agent reports an error: log `Codebase drift -auto-remap failed: {reason}` and continue to `verify_phase_goal`. The phase -is NOT failed by a remap failure. - -If the remap succeeds: log `Codebase drift auto-remap completed for paths: -{affected_paths}` and continue to `verify_phase_goal`. - -The two relevant config keys (continue on error / failure if either is invalid): -- `workflow.drift_threshold` (integer, default 3) — minimum drift elements before action -- `workflow.drift_action` — `warn` (default) or `auto-remap` - -This step is fully non-blocking — it never fails the phase, and any -exception path returns control to `verify_phase_goal`. +Load and follow the full step spec from +`get-shit-done/workflows/execute-phase/steps/codebase-drift-gate.md` — +covers the SDK call, JSON contract, `warn` vs `auto-remap` branches, mapper +spawn template, and the two `workflow.drift_*` config keys. diff --git a/get-shit-done/workflows/execute-phase/steps/codebase-drift-gate.md b/get-shit-done/workflows/execute-phase/steps/codebase-drift-gate.md new file mode 100644 index 00000000..5f442109 --- /dev/null +++ b/get-shit-done/workflows/execute-phase/steps/codebase-drift-gate.md @@ -0,0 +1,79 @@ +# Step: codebase_drift_gate + +Post-execution structural drift detection (#2003). Runs after the last wave +commits, before verification. **Non-blocking by contract:** any internal +error here MUST fall through and continue to `verify_phase_goal`. The phase +is never failed by this gate. + +```bash +DRIFT=$(gsd-sdk query verify.codebase-drift 2>/dev/null || echo '{"skipped":true,"reason":"sdk-failed"}') +``` + +Parse JSON for: `skipped`, `reason`, `action_required`, `directive`, +`spawn_mapper`, `affected_paths`, `elements`, `threshold`, `action`, +`last_mapped_commit`, `message`. + +**If `skipped` is true (no STRUCTURE.md, missing git, or any internal error):** +Log one line — `Codebase drift check skipped: {reason}` — and continue to +`verify_phase_goal`. Do NOT prompt the user. Do NOT block. + +**If `action_required` is false:** Continue silently to `verify_phase_goal`. + +**If `action_required` is true AND `directive` is `warn`:** +Print the `message` field verbatim. The format is: + +```text +Codebase drift detected: {N} structural element(s) since last mapping. + +New directories: + - {path} +New barrel exports: + - {path} +New migrations: + - {path} +New route modules: + - {path} + +Run /gsd:map-codebase --paths {affected_paths} to refresh planning context. +``` + +Then continue to `verify_phase_goal`. Do NOT block. Do NOT spawn anything. + +**If `action_required` is true AND `directive` is `auto-remap`:** + +First load the mapper agent's skill bundle (the executor's `AGENT_SKILLS` +from step `init_context` is for `gsd-executor`, not the mapper): + +```bash +AGENT_SKILLS_MAPPER=$(gsd-sdk query agent-skills gsd-codebase-mapper 2>/dev/null || true) +``` + +Then spawn `gsd-codebase-mapper` agents with the `--paths` hint: + +```text +Task( + subagent_type="gsd-codebase-mapper", + description="Incremental codebase remap (drift)", + prompt="Focus: arch +Today's date: {date} +--paths {affected_paths joined by comma} + +Refresh STRUCTURE.md and ARCHITECTURE.md scoped to the listed paths only. +Stamp last_mapped_commit in each document's frontmatter. +${AGENT_SKILLS_MAPPER}" +) +``` + +If the spawn fails or the agent reports an error: log `Codebase drift +auto-remap failed: {reason}` and continue to `verify_phase_goal`. The phase +is NOT failed by a remap failure. + +If the remap succeeds: log `Codebase drift auto-remap completed for paths: +{affected_paths}` and continue to `verify_phase_goal`. + +The two relevant config keys (continue on error / failure if either is invalid): +- `workflow.drift_threshold` (integer, default 3) — minimum drift elements before action +- `workflow.drift_action` — `warn` (default) or `auto-remap` + +This step is fully non-blocking — it never fails the phase, and any +exception path returns control to `verify_phase_goal`. diff --git a/tests/agent-frontmatter.test.cjs b/tests/agent-frontmatter.test.cjs index 2de01f6f..c8f4ec25 100644 --- a/tests/agent-frontmatter.test.cjs +++ b/tests/agent-frontmatter.test.cjs @@ -359,16 +359,23 @@ describe('VERIFY: data-flow trace, environment audit, and behavioral spot-checks describe('DISCUSS: discussion log generation', () => { test('discuss-phase workflow references DISCUSSION-LOG.md generation', () => { - const content = fs.readFileSync( + // After #2551 progressive-disclosure refactor, the DISCUSSION-LOG.md template + // body lives in workflows/discuss-phase/templates/discussion-log.md and is + // read at the git_commit step. Both files together must satisfy the + // documentation contract. + const parent = fs.readFileSync( path.join(WORKFLOWS_DIR, 'discuss-phase.md'), 'utf-8' ); + const tplPath = path.join(WORKFLOWS_DIR, 'discuss-phase', 'templates', 'discussion-log.md'); + const tpl = fs.existsSync(tplPath) ? fs.readFileSync(tplPath, 'utf-8') : ''; + const content = parent + '\n' + tpl; assert.ok( content.includes('DISCUSSION-LOG.md'), 'discuss-phase must reference DISCUSSION-LOG.md generation' ); assert.ok( content.includes('Audit trail only'), - 'discuss-phase must mark discussion log as audit-only' + 'discuss-phase (or its discussion-log template after #2551) must mark discussion log as audit-only' ); }); diff --git a/tests/bug-2549-2550-2552-discuss-phase-context.test.cjs b/tests/bug-2549-2550-2552-discuss-phase-context.test.cjs index b10152a7..1bce0730 100644 --- a/tests/bug-2549-2550-2552-discuss-phase-context.test.cjs +++ b/tests/bug-2549-2550-2552-discuss-phase-context.test.cjs @@ -16,17 +16,35 @@ const path = require('node:path'); const DISCUSS_PHASE = path.join( __dirname, '..', 'get-shit-done', 'workflows', 'discuss-phase.md', ); +// After #2551 progressive-disclosure refactor, the scout_codebase phase-type +// table and split-reads warning live in references/scout-codebase.md. +const SCOUT_REF = path.join( + __dirname, '..', 'get-shit-done', 'references', 'scout-codebase.md', +); + +function readDiscussContext() { + // Both files are required after #2551 — fail loudly if either is missing + // rather than silently weakening the regression coverage. + for (const p of [DISCUSS_PHASE, SCOUT_REF]) { + assert.ok(fs.existsSync(p), `Required discuss-phase context source missing: ${p}`); + } + return [DISCUSS_PHASE, SCOUT_REF].map(p => fs.readFileSync(p, 'utf-8')).join('\n'); +} describe('discuss-phase context fixes (#2549, #2550, #2552)', () => { let src; test('discuss-phase.md source exists', () => { assert.ok(fs.existsSync(DISCUSS_PHASE), 'discuss-phase.md must exist'); - src = fs.readFileSync(DISCUSS_PHASE, 'utf-8'); + assert.ok( + fs.existsSync(SCOUT_REF), + 'references/scout-codebase.md must exist after #2551 extraction', + ); + src = readDiscussContext(); }); // ─── #2549: load_prior_context cap ────────────────────────────────────── test('#2549: load_prior_context must NOT instruct reading ALL prior CONTEXT.md files', () => { - if (!src) src = fs.readFileSync(DISCUSS_PHASE, 'utf-8'); + if (!src) src = readDiscussContext(); assert.ok( !src.includes('For each CONTEXT.md where phase number < current phase'), 'load_prior_context must not unboundedly read all prior CONTEXT.md files', @@ -34,9 +52,13 @@ describe('discuss-phase context fixes (#2549, #2550, #2552)', () => { }); test('#2549: load_prior_context must reference a bounded read (3 phases or DECISIONS-INDEX)', () => { - if (!src) src = fs.readFileSync(DISCUSS_PHASE, 'utf-8'); - const hasBound = src.includes('3') && src.includes('prior CONTEXT.md'); - const hasIndex = src.includes('DECISIONS-INDEX.md'); + // Read ONLY the parent file — `src.includes('3')` against the + // concatenated source can be satisfied by unrelated occurrences of "3" + // in scout-codebase.md (e.g., "3-5 most relevant files"), masking a + // regression where the parent drops the bounded-read instruction. + const parent = fs.readFileSync(DISCUSS_PHASE, 'utf-8'); + const hasBound = /\b(?:most recent|latest|last|up to)\s+3\b[\s\S]{0,160}\bprior CONTEXT\.md\b/i.test(parent); + const hasIndex = parent.includes('DECISIONS-INDEX.md'); assert.ok( hasBound || hasIndex, 'load_prior_context must reference a bounded read (e.g., most recent 3 phases) or DECISIONS-INDEX.md', @@ -45,7 +67,7 @@ describe('discuss-phase context fixes (#2549, #2550, #2552)', () => { // ─── #2550: scout_codebase phase-type selection ────────────────────────── test('#2550: scout_codebase must not instruct reading all 7 codebase maps', () => { - if (!src) src = fs.readFileSync(DISCUSS_PHASE, 'utf-8'); + if (!src) src = readDiscussContext(); assert.ok( !src.includes('Read the most relevant ones (CONVENTIONS.md, STRUCTURE.md, STACK.md based on phase type)'), 'scout_codebase must not use the old vague "most relevant" instruction without a selection table', @@ -53,7 +75,7 @@ describe('discuss-phase context fixes (#2549, #2550, #2552)', () => { }); test('#2550: scout_codebase must include a phase-type-to-maps selection table', () => { - if (!src) src = fs.readFileSync(DISCUSS_PHASE, 'utf-8'); + if (!src) src = readDiscussContext(); // The table maps phase types to specific map selections assert.ok( src.includes('Phase type') && src.includes('Read these maps'), @@ -68,7 +90,7 @@ describe('discuss-phase context fixes (#2549, #2550, #2552)', () => { // ─── #2552: no split reads ─────────────────────────────────────────────── test('#2552: scout_codebase must explicitly prohibit split reads of the same file', () => { - if (!src) src = fs.readFileSync(DISCUSS_PHASE, 'utf-8'); + if (!src) src = readDiscussContext(); const prohibitsSplit = src.includes('split reads') || src.includes('split read'); assert.ok( prohibitsSplit, diff --git a/tests/chain-flag-plan-phase.test.cjs b/tests/chain-flag-plan-phase.test.cjs index eef17f6b..d35507be 100644 --- a/tests/chain-flag-plan-phase.test.cjs +++ b/tests/chain-flag-plan-phase.test.cjs @@ -16,6 +16,15 @@ const path = require('path'); describe('plan-phase chain flag preservation (#1620)', () => { const planPath = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'plan-phase.md'); const discussPath = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'discuss-phase.md'); + // After #2551, discuss-phase chain logic moved to modes/chain.md. + const discussChainPath = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'discuss-phase', 'modes', 'chain.md'); + const readDiscuss = () => { + // Fail loudly if either source is missing — silent filtering would let a + // regression that deletes modes/chain.md pass this whole suite. + assert.ok(fs.existsSync(discussPath), `discuss-phase.md missing: ${discussPath}`); + assert.ok(fs.existsSync(discussChainPath), `discuss-phase/modes/chain.md missing after #2551 split: ${discussChainPath}`); + return [discussPath, discussChainPath].map(p => fs.readFileSync(p, 'utf8')).join('\n'); + }; test('plan-phase sync-flag guard checks both --auto AND --chain', () => { const content = fs.readFileSync(planPath, 'utf8'); @@ -37,7 +46,7 @@ describe('plan-phase chain flag preservation (#1620)', () => { test('plan-phase and discuss-phase use the same guard pattern for clearing _auto_chain_active', () => { const planContent = fs.readFileSync(planPath, 'utf8'); - const discussContent = fs.readFileSync(discussPath, 'utf8'); + const discussContent = readDiscuss(); const guardPattern = 'if [[ ! "$ARGUMENTS" =~ --auto ]] && [[ ! "$ARGUMENTS" =~ --chain ]]; then'; @@ -47,7 +56,7 @@ describe('plan-phase chain flag preservation (#1620)', () => { ); assert.ok( discussContent.includes(guardPattern), - 'discuss-phase should use the dual-flag guard pattern' + 'discuss-phase (or discuss-phase/modes/chain.md after #2551 split) should use the dual-flag guard pattern' ); }); diff --git a/tests/discuss-checkpoint.test.cjs b/tests/discuss-checkpoint.test.cjs index c41a2881..bebd9d12 100644 --- a/tests/discuss-checkpoint.test.cjs +++ b/tests/discuss-checkpoint.test.cjs @@ -14,9 +14,25 @@ const path = require('path'); describe('discuss-phase incremental checkpoint saves (#1485)', () => { const workflowPath = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'discuss-phase.md'); + // After #2551 progressive-disclosure refactor, checkpoint logic lives in the + // default mode file and the JSON schema lives in the templates directory. + const defaultModePath = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'discuss-phase', 'modes', 'default.md'); + const checkpointTplPath = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'discuss-phase', 'templates', 'checkpoint.json'); + + function readAll() { + // Fail loudly if any required source is missing — silent filtering would + // let a regression that deletes the extracted default-mode or checkpoint + // template pass the suite. + for (const p of [workflowPath, defaultModePath, checkpointTplPath]) { + assert.ok(fs.existsSync(p), `Required discuss-phase checkpoint source missing: ${p}`); + } + return [workflowPath, defaultModePath, checkpointTplPath] + .map(p => fs.readFileSync(p, 'utf8')) + .join('\n'); + } test('workflow writes checkpoint file after each area completes', () => { - const content = fs.readFileSync(workflowPath, 'utf8'); + const content = readAll(); assert.ok( content.includes('DISCUSS-CHECKPOINT.json'), 'workflow should reference checkpoint JSON file' @@ -28,14 +44,14 @@ describe('discuss-phase incremental checkpoint saves (#1485)', () => { }); test('checkpoint includes decisions, areas completed, and areas remaining', () => { - const content = fs.readFileSync(workflowPath, 'utf8'); + const content = readAll(); assert.ok(content.includes('areas_completed'), 'checkpoint should track completed areas'); assert.ok(content.includes('areas_remaining'), 'checkpoint should track remaining areas'); assert.ok(content.includes('"decisions"'), 'checkpoint should include decisions object'); }); test('check_existing step detects checkpoint for session resume', () => { - const content = fs.readFileSync(workflowPath, 'utf8'); + const content = readAll(); // The check_existing step should look for checkpoint files assert.ok( content.includes('DISCUSS-CHECKPOINT.json') && content.includes('Resume'), diff --git a/tests/discuss-phase-power.test.cjs b/tests/discuss-phase-power.test.cjs index 0d708eff..06d82cab 100644 --- a/tests/discuss-phase-power.test.cjs +++ b/tests/discuss-phase-power.test.cjs @@ -37,12 +37,17 @@ describe('discuss-phase power user mode (#1513)', () => { describe('main workflow file (discuss-phase.md)', () => { test('has power_user_mode section or references discuss-phase-power.md', () => { - const content = fs.readFileSync(workflowPath, 'utf8'); - const hasPowerSection = content.includes('power_user_mode') || content.includes('power user mode'); + // After #2551, the power dispatch lives in discuss-phase/modes/power.md and + // the parent references it via the dispatch table. + const parentContent = fs.readFileSync(workflowPath, 'utf8'); + const powerModePath = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'discuss-phase', 'modes', 'power.md'); + const powerMode = fs.existsSync(powerModePath) ? fs.readFileSync(powerModePath, 'utf8') : ''; + const content = parentContent + '\n' + powerMode; + const hasPowerSection = content.includes('power_user_mode') || content.includes('power user mode') || content.includes('modes/power.md'); const hasReference = content.includes('discuss-phase-power'); assert.ok( hasPowerSection || hasReference, - 'discuss-phase.md should have power_user_mode section or reference discuss-phase-power.md' + 'discuss-phase.md (or modes/power.md after #2551) should have power_user_mode section or reference discuss-phase-power.md' ); }); diff --git a/tests/thinking-partner.test.cjs b/tests/thinking-partner.test.cjs index 4d3ef47d..81758c32 100644 --- a/tests/thinking-partner.test.cjs +++ b/tests/thinking-partner.test.cjs @@ -91,48 +91,51 @@ describe('Thinking Partner Integration (#1726)', () => { }); // Workflow integration tests + // After #2551 progressive-disclosure refactor, the thinking-partner block + // moved into the per-mode files (default.md, advisor.md) since the prompt + // is mode-specific (only fires inside discuss_areas, after a user answer). describe('Discuss-phase integration', () => { - test('discuss-phase.md contains thinking partner conditional block', () => { - const content = fs.readFileSync( + function readDiscussFamily() { + const candidates = [ path.join(GSD_ROOT, 'workflows', 'discuss-phase.md'), - 'utf-8' - ); + path.join(GSD_ROOT, 'workflows', 'discuss-phase', 'modes', 'default.md'), + path.join(GSD_ROOT, 'workflows', 'discuss-phase', 'modes', 'advisor.md'), + ]; + return candidates + .filter(p => fs.existsSync(p)) + .map(p => fs.readFileSync(p, 'utf-8')) + .join('\n'); + } + + test('discuss-phase.md contains thinking partner conditional block', () => { + const content = readDiscussFamily(); assert.ok( content.includes('Thinking partner (conditional)'), - 'discuss-phase.md should contain thinking partner conditional block' + 'discuss-phase workflow family should contain thinking partner conditional block' ); }); test('discuss-phase references features.thinking_partner config', () => { - const content = fs.readFileSync( - path.join(GSD_ROOT, 'workflows', 'discuss-phase.md'), - 'utf-8' - ); + const content = readDiscussFamily(); assert.ok( content.includes('features.thinking_partner'), - 'discuss-phase.md should reference the config key' + 'discuss-phase workflow family should reference the config key' ); }); test('discuss-phase references thinking-partner.md for signal list', () => { - const content = fs.readFileSync( - path.join(GSD_ROOT, 'workflows', 'discuss-phase.md'), - 'utf-8' - ); + const content = readDiscussFamily(); assert.ok( content.includes('references/thinking-partner.md'), - 'discuss-phase.md should reference the signal list doc' + 'discuss-phase workflow family should reference the signal list doc' ); }); test('discuss-phase offers skip option', () => { - const content = fs.readFileSync( - path.join(GSD_ROOT, 'workflows', 'discuss-phase.md'), - 'utf-8' - ); + const content = readDiscussFamily(); assert.ok( content.includes('No, decision made'), - 'discuss-phase.md should offer a skip/decline option' + 'discuss-phase workflow family should offer a skip/decline option' ); }); }); diff --git a/tests/workflow-size-budget.test.cjs b/tests/workflow-size-budget.test.cjs new file mode 100644 index 00000000..dbe994d2 --- /dev/null +++ b/tests/workflow-size-budget.test.cjs @@ -0,0 +1,317 @@ +/** + * Workflow size budget. + * + * Workflow definitions in `get-shit-done/workflows/*.md` are loaded verbatim + * into Claude's context every time the corresponding `/gsd:*` command is + * invoked. Unbounded growth is paid on every invocation across every session. + * + * Tiered the same way as agent budgets (#2361): + * - XL : top-level orchestrators (e.g., execute-phase, autonomous) + * - LARGE : multi-step planners + * - DEFAULT : focused single-purpose workflows (target tier) + * + * Raising a budget is a deliberate choice — adjust the constant, write a + * rationale in the PR, and confirm the bloat is not duplicated content + * that belongs in `get-shit-done/references/` or a per-mode subdirectory + * (see `workflows/discuss-phase/modes/` for the progressive-disclosure + * pattern introduced by #2551). + * + * See: + * - https://github.com/gsd-build/get-shit-done/issues/2551 (this test) + * - https://github.com/gsd-build/get-shit-done/issues/2361 (agent budget) + */ + +const { test, describe } = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('fs'); +const path = require('path'); + +const WORKFLOWS_DIR = path.join(__dirname, '..', 'get-shit-done', 'workflows'); + +const XL_BUDGET = 1700; +const LARGE_BUDGET = 1500; +const DEFAULT_BUDGET = 1000; + +// Top-level orchestrators that own end-to-end multi-phase rubrics. +// Grandfathered at current sizes — see PR #2551 for #2551 progressive-disclosure +// pattern that future shrinks should follow. +const XL_WORKFLOWS = new Set([ + 'execute-phase', // 1622 + 'plan-phase', // 1493 + 'new-project', // 1391 +]); + +// Multi-step planners and bigger feature workflows. Grandfathered. +const LARGE_WORKFLOWS = new Set([ + 'docs-update', // 1155 + 'autonomous', // 789 + 'complete-milestone', // 847 + 'verify-work', // 740 + 'transition', // 693 + 'help', // 667 + 'discuss-phase-assumptions', // 670 + 'progress', // 619 + 'new-milestone', // 611 + 'update', // 587 + 'quick', // 971 + 'code-review', // 515 +]); + +const ALL_WORKFLOWS = fs.readdirSync(WORKFLOWS_DIR) + .filter(f => f.endsWith('.md')) + .map(f => f.replace('.md', '')); + +function budgetFor(workflow) { + if (XL_WORKFLOWS.has(workflow)) return { tier: 'XL', limit: XL_BUDGET }; + if (LARGE_WORKFLOWS.has(workflow)) return { tier: 'LARGE', limit: LARGE_BUDGET }; + return { tier: 'DEFAULT', limit: DEFAULT_BUDGET }; +} + +function lineCount(filePath) { + const content = fs.readFileSync(filePath, 'utf-8'); + if (content.length === 0) return 0; + const trailingNewline = content.endsWith('\n') ? 1 : 0; + return content.split('\n').length - trailingNewline; +} + +describe('SIZE: workflow line-count budget', () => { + for (const workflow of ALL_WORKFLOWS) { + const { tier, limit } = budgetFor(workflow); + test(`${workflow} (${tier}) stays under ${limit} lines`, () => { + const filePath = path.join(WORKFLOWS_DIR, workflow + '.md'); + const lines = lineCount(filePath); + assert.ok( + lines <= limit, + `${workflow}.md has ${lines} lines — exceeds ${tier} budget of ${limit}. ` + + `Extract per-mode bodies to a workflows/${workflow}/modes/ subdirectory, ` + + `templates to workflows/${workflow}/templates/, or shared references ` + + `to get-shit-done/references/. See workflows/discuss-phase/ for the pattern.` + ); + }); + } +}); + +describe('SIZE: discuss-phase progressive disclosure (issue #2551)', () => { + // Issue #2551 explicitly targets discuss-phase.md at <500 lines, separate from + // the per-tier grandfathered budgets above. This is the headline metric of the + // refactor — every other workflow above 500 is grandfathered at its current + // size and may shrink later by following the same pattern. + const DISCUSS_PHASE_TARGET = 500; + test(`discuss-phase.md is under ${DISCUSS_PHASE_TARGET} lines (issue #2551 target)`, () => { + const filePath = path.join(WORKFLOWS_DIR, 'discuss-phase.md'); + const lines = lineCount(filePath); + assert.ok( + lines < DISCUSS_PHASE_TARGET, + `discuss-phase.md has ${lines} lines — must be under ${DISCUSS_PHASE_TARGET} per #2551. ` + + `Per-mode logic belongs in workflows/discuss-phase/modes/.md, ` + + `templates in workflows/discuss-phase/templates/.` + ); + }); + + const SUBDIR = path.join(WORKFLOWS_DIR, 'discuss-phase'); + + test('mode files exist for every documented mode', () => { + const expected = ['power', 'all', 'auto', 'chain', 'text', 'batch', 'analyze', 'default', 'advisor']; + for (const mode of expected) { + const p = path.join(SUBDIR, 'modes', `${mode}.md`); + assert.ok( + fs.existsSync(p), + `Expected mode file ${path.relative(WORKFLOWS_DIR, p)} — missing. ` + + `Each --flag in commands/gsd/discuss-phase.md must have a matching mode file.` + ); + } + }); + + test('every mode file is a real, non-empty workflow doc', () => { + const modesDir = path.join(SUBDIR, 'modes'); + if (!fs.existsSync(modesDir)) { + assert.fail(`workflows/discuss-phase/modes/ directory does not exist`); + } + for (const file of fs.readdirSync(modesDir)) { + if (!file.endsWith('.md')) continue; + const p = path.join(modesDir, file); + const content = fs.readFileSync(p, 'utf-8'); + assert.ok(content.trim().length > 100, + `${file} is empty or near-empty (${content.length} chars) — extraction must preserve behavior, not stub it out`); + } + }); + + test('templates extracted to discuss-phase/templates/', () => { + const expected = ['context.md', 'discussion-log.md', 'checkpoint.json']; + for (const t of expected) { + const p = path.join(SUBDIR, 'templates', t); + assert.ok(fs.existsSync(p), + `Expected template ${path.relative(WORKFLOWS_DIR, p)} — missing.`); + } + }); + + test('parent discuss-phase.md dispatches to mode files (power)', () => { + const parent = fs.readFileSync(path.join(WORKFLOWS_DIR, 'discuss-phase.md'), 'utf-8'); + assert.ok( + /discuss-phase\/modes\/power\.md/.test(parent) || + /discuss-phase-power\.md/.test(parent), + `Parent discuss-phase.md must reference workflows/discuss-phase/modes/power.md ` + + `(or the legacy discuss-phase-power.md alias) somewhere in its dispatch logic.` + ); + }); + + test('parent dispatches to all extracted modes (auto, chain, all, advisor)', () => { + const parent = fs.readFileSync(path.join(WORKFLOWS_DIR, 'discuss-phase.md'), 'utf-8'); + for (const mode of ['auto', 'chain', 'all', 'advisor']) { + assert.ok( + new RegExp(`discuss-phase/modes/${mode}\\.md`).test(parent), + `Parent discuss-phase.md must reference workflows/discuss-phase/modes/${mode}.md` + ); + } + }); + + test('parent reads CONTEXT.md template at the write step (not at top)', () => { + const parent = fs.readFileSync(path.join(WORKFLOWS_DIR, 'discuss-phase.md'), 'utf-8'); + // The template reference must appear inside or near the write_context step, + // not in the top-level block (which would defeat lazy load). + const requiredReadingMatch = parent.match(/([\s\S]*?)<\/required_reading>/); + if (requiredReadingMatch) { + assert.ok( + !/discuss-phase\/templates\/context\.md/.test(requiredReadingMatch[1]), + `CONTEXT.md template must NOT be in — that defeats lazy loading. ` + + `Read it inside the write_context step, just before writing the file.` + ); + } + assert.ok( + /discuss-phase\/templates\/context\.md/.test(parent), + `Parent must reference workflows/discuss-phase/templates/context.md somewhere ` + + `(inside write_context step) so the template loads only when CONTEXT.md is being written.` + ); + }); + + test('advisor block is gated behind USER-PROFILE.md existence check', () => { + const parent = fs.readFileSync(path.join(WORKFLOWS_DIR, 'discuss-phase.md'), 'utf-8'); + // The guard MUST be a file-existence check (test -f or equivalent), not an + // unconditional Read of the advisor mode file. + assert.ok( + /USER-PROFILE\.md/.test(parent), + 'Parent must reference USER-PROFILE.md to detect advisor mode' + ); + assert.ok( + /test\s+-[ef]\s+["'$].*USER-PROFILE/.test(parent) || + /\[\[\s+-[ef]\s+["'$].*USER-PROFILE/.test(parent) || + /\[\s+-[ef]\s+["'$].*USER-PROFILE/.test(parent), + 'Advisor mode detection must use a file-existence guard (test -f / [ -f ]) ' + + 'so the advisor mode file is only Read when USER-PROFILE.md exists.' + ); + // Confirm advisor.md Read is conditional on ADVISOR_MODE + const advisorReadGuarded = + /ADVISOR_MODE[\s\S]{0,200}?modes\/advisor\.md/.test(parent) || + /modes\/advisor\.md[\s\S]{0,200}?ADVISOR_MODE/.test(parent) || + /if[\s\S]{0,200}?ADVISOR_MODE[\s\S]{0,400}?advisor\.md/.test(parent); + assert.ok( + advisorReadGuarded, + 'Read of modes/advisor.md must be guarded by ADVISOR_MODE (which derives from USER-PROFILE.md existence). ' + + 'Skip the Read entirely when no profile is present.' + ); + }); + + test('auto mode file documents skipping interactive questions (regression)', () => { + const auto = fs.readFileSync(path.join(SUBDIR, 'modes', 'auto.md'), 'utf-8'); + assert.ok( + /skip[\s\S]{0,80}interactive|without\s+(?:using\s+)?AskUserQuestion|recommended\s+(?:option|default)/i.test(auto), + `auto.md must preserve the documented behavior: skip interactive questions ` + + `and pick the recommended option without using AskUserQuestion.` + ); + }); + + test('auto mode preserves the single-pass cap (regression for inline rule)', () => { + const auto = fs.readFileSync(path.join(SUBDIR, 'modes', 'auto.md'), 'utf-8'); + assert.ok( + /single\s+pass|max_discuss_passes|MAX_PASSES|pass\s+cap/i.test(auto), + `auto.md must preserve the auto-mode pass cap rule from the original workflow. ` + + `Without it, the workflow can self-feed and consume unbounded resources.` + ); + }); + + test('all mode file documents auto-selecting all gray areas (regression)', () => { + const allMode = fs.readFileSync(path.join(SUBDIR, 'modes', 'all.md'), 'utf-8'); + assert.ok( + /auto-select(?:ed)?\s+ALL|select\s+ALL|all\s+gray\s+areas/i.test(allMode), + `all.md must preserve the documented behavior: auto-select ALL gray areas ` + + `without asking the user.` + ); + }); + + test('chain mode documents auto-advance to plan-phase (regression)', () => { + const chain = fs.readFileSync(path.join(SUBDIR, 'modes', 'chain.md'), 'utf-8'); + assert.ok( + /plan-phase/.test(chain) && /(auto-advance|auto\s+plan)/i.test(chain), + `chain.md must preserve the documented auto-advance to plan-phase behavior.` + ); + }); + + test('text mode documents replacing AskUserQuestion (regression)', () => { + const textMode = fs.readFileSync(path.join(SUBDIR, 'modes', 'text.md'), 'utf-8'); + assert.ok( + /AskUserQuestion/.test(textMode) && /(numbered\s+list|plain[-\s]text)/i.test(textMode), + `text.md must preserve the rule: replace AskUserQuestion with plain-text numbered lists.` + ); + }); + + test('batch mode documents 2-5 question grouping (regression)', () => { + const batch = fs.readFileSync(path.join(SUBDIR, 'modes', 'batch.md'), 'utf-8'); + assert.ok( + /2[-\s–]5|2\s+to\s+5|--batch=N|--batch\s+N/.test(batch), + `batch.md must preserve the 2-5 questions-per-batch rule.` + ); + }); + + test('analyze mode documents trade-off table presentation (regression)', () => { + const analyze = fs.readFileSync(path.join(SUBDIR, 'modes', 'analyze.md'), 'utf-8'); + assert.ok( + /trade[-\s]off|tradeoff|pros[\s\S]{0,30}cons/i.test(analyze), + `analyze.md must preserve the trade-off analysis presentation rule.` + ); + }); + + test('CONTEXT.md template preserves all required sections', () => { + const tpl = fs.readFileSync(path.join(SUBDIR, 'templates', 'context.md'), 'utf-8'); + for (const section of ['', '', '', '', '', '']) { + assert.ok(tpl.includes(section), + `CONTEXT.md template missing required section ${section} — extraction dropped content.`); + } + // spec_lock is conditional but the template still has to include it as a documented option + assert.ok(/spec_lock/i.test(tpl), + `CONTEXT.md template must document the conditional section for SPEC.md integration.`); + }); + + test('checkpoint template is valid JSON', () => { + const raw = fs.readFileSync(path.join(SUBDIR, 'templates', 'checkpoint.json'), 'utf-8'); + assert.doesNotThrow(() => JSON.parse(raw), + `checkpoint.json template must parse as valid JSON — downstream code reads it.`); + const parsed = JSON.parse(raw); + for (const key of ['phase', 'phase_name', 'timestamp', 'areas_completed', 'areas_remaining', 'decisions']) { + assert.ok(key in parsed, + `checkpoint.json template missing required field "${key}" — schema regression vs original workflow.`); + } + }); + + test('parent does not leak per-mode bodies inline (would defeat extraction)', () => { + const parent = fs.readFileSync(path.join(WORKFLOWS_DIR, 'discuss-phase.md'), 'utf-8'); + // Heuristic: the parent should not contain the full DISCUSSION-LOG.md template body + // (extracted to templates/discussion-log.md) — that's the heaviest single block. + // Look for unique strings that ONLY appear in the original inline template. + const inlineDiscussionLogSignal = /\| Option \| Description \| Selected \|/g; + const occurrences = (parent.match(inlineDiscussionLogSignal) || []).length; + assert.ok(occurrences === 0, + `Parent discuss-phase.md still contains the inline DISCUSSION-LOG.md table — ` + + `that block must move to workflows/discuss-phase/templates/discussion-log.md.`); + }); + + test('negative: invalid mode flag combinations document a clear error path', () => { + // Sanity check: the parent file should explicitly handle the mode dispatch + // rather than silently doing nothing on an unknown flag pattern. + const parent = fs.readFileSync(path.join(WORKFLOWS_DIR, 'discuss-phase.md'), 'utf-8'); + assert.ok( + /ARGUMENTS|--auto|--chain|--all|--power/.test(parent), + 'Parent must dispatch on $ARGUMENTS — losing the flag-parsing block would silently ' + + 'fall back to default mode and obscure user errors.' + ); + }); +});