From 42b74100f1fcc17f4e64578b523e6b14c782ea4c Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Sat, 6 Jun 2026 11:09:54 -0400 Subject: [PATCH] feat(#159): auto-use existing RESEARCH.md in /gsd:plan-phase --research-phase (#718) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(#159): auto-use existing RESEARCH.md in /gsd:plan-phase --research-phase When RESEARCH.md already exists in research-only mode and neither --research nor --view is passed, emit a one-line notice and exit cleanly instead of prompting update/view/skip. This matches the promptless auto-use of standard /gsd:plan-phase (§5.1) and removes the §5.0/§5.1 inconsistency, making AI-agent and CLI invocations non-interactive in the common case. The two explicit-flag escape hatches (--research to refresh, --view to print) cover any deviation. Closes #159 Co-Authored-By: Claude Opus 4.8 * chore(#159): point changeset fragment at PR #718 Co-Authored-By: Claude Opus 4.8 * docs(#159): tighten research-phase reference register (Diataxis) Make the 'no modifier' research-phase entries descriptive rather than imperative and drop the trailing 'pass --research/--view' clauses, which duplicated the adjacent --research/--view documentation. Reference docs describe; the recovery flags are documented in their own entries. The emitted runtime notice in the workflow keeps naming the flags (in-band recovery), unchanged. Co-Authored-By: Claude Opus 4.8 --------- Co-authored-by: Claude Opus 4.8 --- .../159-research-phase-auto-use-existing.md | 5 +++ commands/gsd/plan-phase.md | 4 +- docs/COMMANDS.md | 4 +- gsd-core/workflows/help/modes/full.md | 4 +- gsd-core/workflows/plan-phase.md | 6 +-- tests/skill-frontmatter-contract.test.cjs | 45 ++++++++++++------- 6 files changed, 43 insertions(+), 25 deletions(-) create mode 100644 .changeset/159-research-phase-auto-use-existing.md diff --git a/.changeset/159-research-phase-auto-use-existing.md b/.changeset/159-research-phase-auto-use-existing.md new file mode 100644 index 000000000..0d0d6dbee --- /dev/null +++ b/.changeset/159-research-phase-auto-use-existing.md @@ -0,0 +1,5 @@ +--- +type: Changed +pr: 718 +--- +`/gsd:plan-phase --research-phase ` now auto-uses an existing `RESEARCH.md` instead of prompting update/view/skip. When research already exists and neither `--research` nor `--view` is passed, it emits a one-line notice and exits cleanly, matching the promptless behavior of standard `/gsd:plan-phase `. Pass `--research` to force-refresh or `--view` to print the existing research. (#159) diff --git a/commands/gsd/plan-phase.md b/commands/gsd/plan-phase.md index 3e4314f8b..4051a361f 100644 --- a/commands/gsd/plan-phase.md +++ b/commands/gsd/plan-phase.md @@ -22,8 +22,8 @@ Create executable phase prompts (PLAN.md files) for a roadmap phase with integra **Research-only mode (`--research-phase `):** Spawn `gsd-phase-researcher` for phase `N`, write `RESEARCH.md`, then exit before the planner runs. Useful for cross-phase research, doc review before committing to a planning approach, and correction-without-replanning loops where iterating on research alone is dramatically cheaper than re-spawning the planner. Replaces the deleted research-phase command (#3042). **Research-only modifiers:** -- **No flag** — when `RESEARCH.md` already exists, prompt the user to choose `update / view / skip`. -- **`--research`** — force-refresh: re-spawn the researcher unconditionally, no prompt. Skips the existing-RESEARCH.md menu. +- **No flag** — when `RESEARCH.md` already exists, auto-uses it: emits a one-line notice and exits cleanly, no prompt. +- **`--research`** — force-refresh: re-spawn the researcher unconditionally, no prompt. Bypasses the existing-RESEARCH.md auto-use path. - **`--view`** — view-only: print existing `RESEARCH.md` to stdout. Does not spawn the researcher. Cheapest mode for the correction-without-replanning loop. If no `RESEARCH.md` exists yet, errors with a hint to drop `--view`. **Orchestrator role:** Parse arguments, validate phase, research domain (unless skipped), spawn gsd-planner, verify with gsd-plan-checker, iterate until pass or max iterations, present results. diff --git a/docs/COMMANDS.md b/docs/COMMANDS.md index 0d71fa243..6bd444ac6 100644 --- a/docs/COMMANDS.md +++ b/docs/COMMANDS.md @@ -162,7 +162,7 @@ Research, plan, and verify a phase. **Produces:** `{phase}-RESEARCH.md`, `{phase}-{N}-PLAN.md`, `{phase}-VALIDATION.md`; `{phase}/SKELETON.md` when Walking Skeleton mode fires **Research-only mode (`--research-phase `):** -- No modifier: prompts `update / view / skip` if RESEARCH.md already exists. +- No modifier: when RESEARCH.md already exists, auto-uses it — emits a one-line notice and exits, no prompt. - With `--research`: force-refresh — re-spawn researcher unconditionally, no prompt. - With `--view`: print existing RESEARCH.md to stdout, no spawn. Errors if RESEARCH.md missing. @@ -185,7 +185,7 @@ See [Package Legitimacy Gate in the User Guide](USER-GUIDE.md#package-legitimacy /gsd-plan-phase 1 --bounce # Plan + external bounce validation /gsd-plan-phase 2 --ingest docs/adr/0010.md # ADR express path for context synthesis /gsd-plan-phase 2 --ingest 'docs/adr/00*.md' --ingest-format auto -/gsd-plan-phase --research-phase 4 # Research only on phase 4 (prompts if RESEARCH.md exists) +/gsd-plan-phase --research-phase 4 # Research only on phase 4 (auto-uses existing RESEARCH.md, no prompt) /gsd-plan-phase --research-phase 4 --view # Print existing RESEARCH.md, no spawn /gsd-plan-phase --research-phase 4 --research # Force-refresh research, no prompt /gsd-plan-phase 1 --mvp # Vertical-slice plan for phase 1 diff --git a/gsd-core/workflows/help/modes/full.md b/gsd-core/workflows/help/modes/full.md index f6f130ab9..1efe4f401 100644 --- a/gsd-core/workflows/help/modes/full.md +++ b/gsd-core/workflows/help/modes/full.md @@ -86,7 +86,7 @@ Create detailed execution plan for a specific phase. - `--skip-research` — bypass the research subagent - `--research-phase ` — research-only mode. Spawns the research agent for phase ``, writes `RESEARCH.md`, then exits before the planner runs. Useful for cross-phase research, doc review before committing to a planning approach, and correction-without-replanning loops. Replaces the deleted `gsd-research-phase` standalone command (#3042). - - Modifiers: `--research` forces refresh (re-spawn researcher, no prompt). `--view` prints existing `RESEARCH.md` to stdout without spawning. With neither, prompts `update / view / skip` if `RESEARCH.md` already exists. + - Modifiers: `--research` forces refresh (re-spawn researcher). `--view` prints existing `RESEARCH.md` to stdout without spawning. With neither, auto-uses an existing `RESEARCH.md` (one-line notice, then clean exit). - `--gaps` — focus only on closing gaps from a prior plan-check - `--skip-verify` — skip the post-plan verifier loop - `--ingest ` — pre-ingest external ADRs/PRDs/SPECs before planning (see *PRD Express Path* below) @@ -100,7 +100,7 @@ Create detailed execution plan for a specific phase. - Multiple plans per phase supported (XX-01, XX-02, etc.) Usage: `/gsd:plan-phase 1` -Usage: `/gsd:plan-phase --research-phase 2` — research only on phase 2 (prompts if `RESEARCH.md` exists) +Usage: `/gsd:plan-phase --research-phase 2` — research only on phase 2 (auto-uses existing `RESEARCH.md`, no prompt) Usage: `/gsd:plan-phase --research-phase 2 --view` — print existing `RESEARCH.md`, no spawn Usage: `/gsd:plan-phase --research-phase 2 --research` — force-refresh, no prompt Result: Creates `.planning/phases/01-foundation/01-01-PLAN.md` diff --git a/gsd-core/workflows/plan-phase.md b/gsd-core/workflows/plan-phase.md index 63b08be16..cc93aa5ce 100644 --- a/gsd-core/workflows/plan-phase.md +++ b/gsd-core/workflows/plan-phase.md @@ -417,15 +417,15 @@ Pass `ai_spec_path` and `framework_line` to planner in step 7 so it can referenc **Skip if:** `--gaps` flag or `--skip-research` flag or `--reviews` flag. -### 5.0. Research-Only Modifiers (`--view`, `--research`, prompt) +### 5.0. Research-Only Modifiers (`--view`, `--research`) **Skip if:** `RESEARCH_ONLY` is `false`. Three branches in research-only mode (`--research-phase `): -1. **`--view`** (or user picks "View" in the prompt below): print `RESEARCH.md` to stdout, no spawn, exit. If `RESEARCH.md` is missing, error with: `--view requires an existing RESEARCH.md; drop --view to spawn the researcher.` +1. **`--view`**: print `RESEARCH.md` to stdout, no spawn, exit. If `RESEARCH.md` is missing, error with: `--view requires an existing RESEARCH.md; drop --view to spawn the researcher.` 2. **`--research`** (force-refresh): re-spawn researcher unconditionally — fall through to "Spawn gsd-phase-researcher" below. -3. **Neither flag AND `has_research=true`:** emit `RESEARCH.md already exists for Phase ${PHASE}.` and prompt the user with three choices: `1. Update — re-spawn researcher and refresh RESEARCH.md`, `2. View — print existing RESEARCH.md and exit (no spawn)`, `3. Skip — exit without spawning or printing`. Map "Update" → fall through to spawn, "View" → set `VIEW_ONLY=true` and emit RESEARCH.md as in (1), "Skip" → exit cleanly. Mirrors the deleted `/gsd-research-phase` standalone's existing-artifact menu (#3042 parity). +3. **Neither flag AND `has_research=true`:** auto-use the existing research and exit cleanly — do not prompt, do not re-spawn. Emit `RESEARCH.md already exists for Phase ${PHASE}, using it. To force-refresh, re-invoke with --research; to print, re-invoke with --view. Path: ${research_path}` then exit. The explicit-flag escape hatches cover any deviation; this matches §5.1's promptless auto-use of existing research, removing the §5.0/§5.1 inconsistency (#159). ```bash if [[ "$VIEW_ONLY" == "true" ]]; then diff --git a/tests/skill-frontmatter-contract.test.cjs b/tests/skill-frontmatter-contract.test.cjs index ecd6f39b1..c05fee768 100644 --- a/tests/skill-frontmatter-contract.test.cjs +++ b/tests/skill-frontmatter-contract.test.cjs @@ -172,29 +172,42 @@ describe('skill frontmatter: /gsd-plan-phase --research-phase flag absorbs the s ); }); - test('workflow has an existing-RESEARCH.md prompt path (update/view/skip) within proximity', () => { + test('research-only mode auto-uses existing RESEARCH.md (no update/view/skip prompt)', () => { const content = read('gsd-core/workflows/plan-phase.md'); - // CR #3045 finding: the previous version of this test asserted - // `update`, `view`, `skip` appeared anywhere in the file, which was - // tautological — those words occur all over the workflow for - // unrelated reasons (--skip-research, --view flag declarations, - // etc.). Tighten to a proximity check: all three choice tokens - // must occur in a window of ~400 chars surrounding "RESEARCH.md - // already exists" / "Update — re-spawn" / equivalent prompt prose, - // proving the prompt section is genuinely present. + // #159: the §5.0 existing-RESEARCH.md path no longer prompts + // update/view/skip. When RESEARCH.md exists and neither --research nor + // --view is set, the workflow emits a brief "using it" notice naming + // the two escape-hatch flags and exits cleanly — matching the + // promptless auto-use behavior of §5.1 standard mode. const idx = content.indexOf('RESEARCH.md already exists'); assert.ok( idx >= 0, - 'plan-phase workflow must contain the literal "RESEARCH.md already exists" prompt header in the research-only existing-artifact section' + 'plan-phase workflow must contain the literal "RESEARCH.md already exists" notice in the research-only existing-artifact section' ); const window = content.slice(idx, idx + 600); - const hasUpdate = /\b(?:update|refresh|re-spawn)\b/i.test(window); - const hasView = /\bview\b/i.test(window); - const hasSkip = /\bskip\b/i.test(window); + // Positive contract: an auto-use notice that names both recovery flags. assert.ok( - hasUpdate && hasView && hasSkip, - 'prompt section near "RESEARCH.md already exists" must mention all three choices (update/refresh/re-spawn, view, skip); ' + - 'got update=' + hasUpdate + ' view=' + hasView + ' skip=' + hasSkip + /using it/i.test(window), + 'existing-RESEARCH.md notice must state the existing research is being used (e.g. "using it")' + ); + assert.ok( + /--research\b/.test(window), + 'notice must name --research as the force-refresh escape hatch' + ); + assert.ok( + /--view\b/.test(window), + 'notice must name --view as the print-existing escape hatch' + ); + // Negative contract: the interactive three-choice prompt must be gone. + // Guard against reintroduction via prose, an AskUserQuestion call, or a + // lingering "skip" choice token. (The §5.1 "skip to step 6" text is ~805 + // chars past the anchor, outside this 600-char window.) + assert.ok( + !/prompt the user/i.test(window) && + !/three choices/i.test(window) && + !/AskUserQuestion/i.test(window) && + !/\bskip\b/i.test(window), + 'existing-RESEARCH.md path must no longer present an interactive update/view/skip prompt' ); }); });