From 2981983baefd9d105be1d3285e61a90a16ad48b9 Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Wed, 10 Jun 2026 10:44:20 -0400 Subject: [PATCH] fix(#973): add Edit to gsd-planner tools and forbid whole-file Write of ROADMAP.md (#989) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(#973): add Edit to gsd-planner tools and forbid whole-file Write of ROADMAP.md gsd-planner shipped Write but not Edit — the same writer-agent gap fixed for six agents in #571/#581. Without Edit, an in-place ROADMAP update fell back to a whole-file Write that truncated committed milestone history (292→16 lines in a real incident). Changes: - agents/gsd-planner.md: add Edit to tools: frontmatter (adjacent to Write) - agents/gsd-planner.md: update_roadmap step now directs Edit (scoped), with an explicit blocking prohibition on whole-file Write of ROADMAP.md or any existing curated .planning/ file - agents/gsd-planner.md: Write contract section clarifies Write is authorized only for net-new PLAN.md creation; existing files must use Edit - tests/agent-frontmatter.test.cjs: extend SECTION_WRITER_AGENTS list (#581 test) to cover gsd-planner — fails before fix, passes after - .changeset/973-gsd-planner-edit-tool.md: Fixed changeset, pr:0 Closes #973 Co-Authored-By: Claude Sonnet 4.6 * chore(#973): backfill changeset pr number (989) * fix(#973): trim gsd-planner.md prose under agent size cap (keep Edit + scoped-Edit-for-ROADMAP rule) Co-Authored-By: Claude Sonnet 4.6 --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 --- .changeset/973-gsd-planner-edit-tool.md | 5 +++++ agents/gsd-planner.md | 10 +++++++--- docs/AGENTS.md | 2 +- tests/agent-frontmatter.test.cjs | 1 + 4 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 .changeset/973-gsd-planner-edit-tool.md diff --git a/.changeset/973-gsd-planner-edit-tool.md b/.changeset/973-gsd-planner-edit-tool.md new file mode 100644 index 000000000..852c99c59 --- /dev/null +++ b/.changeset/973-gsd-planner-edit-tool.md @@ -0,0 +1,5 @@ +--- +type: Fixed +pr: 989 +--- +**`gsd-planner` now ships the `Edit` tool, so it can no longer destroy `ROADMAP.md` via a whole-file `Write`** — the planner had `Write` but not `Edit` (the #571/#581 writer-agent gap), so an in-place ROADMAP edit fell back to a full overwrite that truncated committed milestone history. The `update_roadmap` step now directs scoped `Edit` calls and explicitly forbids passing the full file to `Write`. (#973) diff --git a/agents/gsd-planner.md b/agents/gsd-planner.md index af3d0daf8..f456260a7 100644 --- a/agents/gsd-planner.md +++ b/agents/gsd-planner.md @@ -1,7 +1,7 @@ --- name: gsd-planner description: Creates executable phase plans with task breakdown, dependency analysis, and goal-backward verification. Spawned by /gsd:plan-phase orchestrator. -tools: Read, Write, Bash, Glob, Grep, WebFetch, mcp__context7__* +tools: Read, Write, Edit, Bash, Glob, Grep, WebFetch, mcp__context7__* color: green # hooks: # PostToolUse: @@ -998,6 +998,8 @@ Use template structure for each PLAN.md. These PLAN.md files are the canonical output of this agent. The orchestrator reads each `.planning/phases/{padded_phase}-{slug}/{padded_phase}-{NN}-PLAN.md` from disk after you return; it does NOT read your return message for the file content. +**Write is for net-new PLAN.md only.** For any existing file (`ROADMAP.md`, `.planning/` files) use `Edit` (scoped replacement), never `Write`. See `update_roadmap`. + 1. **Default: write each PLAN.md in a single `Write` call.** On most runtimes this is correct and reliable — do this unless rule 4 applies. 2. **Do NOT return the PLAN.md content in your response.** Your return message is a brief confirmation (see ``); the content lives on disk. 3. **Do NOT use `Bash(cat << 'EOF')` or heredoc** for file creation. Use the `Write` tool. @@ -1062,9 +1064,11 @@ Returns JSON: `{ valid, errors, warnings, task_count, tasks }` Update ROADMAP.md to finalize phase placeholders: +**CRITICAL — use `Edit` (scoped), NOT `Write`, for ROADMAP.md.** A whole-file `Write` destroys all phase entries outside your diff window. Use `Edit` to replace only the target section; use multiple `Edit` calls if needed. NEVER pass the entire ROADMAP.md content to `Write`. + 1. Read `.planning/ROADMAP.md` 2. Find phase entry (`### Phase {N}:`) -3. Update placeholders: +3. Update placeholders using `Edit` (scoped replacement only): **Goal** (only if placeholder): - `[To be planned]` → derive from CONTEXT.md > RESEARCH.md > phase description @@ -1080,7 +1084,7 @@ Plans: - [ ] {phase}-02-PLAN.md — {brief objective} ``` -4. Write updated ROADMAP.md +4. Apply changes with `Edit` (scoped) — use the `gsd roadmap` subcommands (run by the orchestrator) for structural ROADMAP mutations; reserve direct `Edit` for placeholder fills only. diff --git a/docs/AGENTS.md b/docs/AGENTS.md index 85d0fae12..beb3a0f55 100644 --- a/docs/AGENTS.md +++ b/docs/AGENTS.md @@ -161,7 +161,7 @@ GSD uses a multi-agent architecture where thin orchestrators (workflow files) sp |----------|-------| | **Spawned by** | `/gsd-plan-phase`, `/gsd-quick` | | **Parallelism** | Single instance | -| **Tools** | Read, Write, Bash, Glob, Grep, WebFetch, mcp (context7) | +| **Tools** | Read, Write, Edit, Bash, Glob, Grep, WebFetch, mcp (context7) | | **Model (balanced)** | Opus | | **Color** | Green | | **Produces** | `{phase}-{N}-PLAN.md` files | diff --git a/tests/agent-frontmatter.test.cjs b/tests/agent-frontmatter.test.cjs index f39ab5155..9cfe0918f 100644 --- a/tests/agent-frontmatter.test.cjs +++ b/tests/agent-frontmatter.test.cjs @@ -435,6 +435,7 @@ describe('EDITWRITE: section-writer agents must have both Write and Edit in tool 'gsd-phase-researcher', 'gsd-ui-researcher', 'gsd-debug-session-manager', + 'gsd-planner', // #973: planner lacked Edit; whole-file Write truncated ROADMAP.md ]; for (const agent of SECTION_WRITER_AGENTS) {