fix: specify write tool for PROJECT.md in milestone/slice prompts (#3238)

* fix: specify write tool for PROJECT.md in complete-milestone/slice prompts (#2946)

The prompts for complete-milestone step 11 and complete-slice step 13
gave ambiguous instructions to "update PROJECT.md" without naming which
tool to use. This caused LLMs to call `edit` with only `newText`,
missing the required `path` and `oldText` parameters. Now both prompts
explicitly instruct the LLM to use the `write` tool with the correct
parameters, since PROJECT.md updates are full-document refreshes.

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

* fix: preserve 'refresh current state if needed' phrase in complete-slice step 13

The PR's rewrite of step 13 to specify the write tool accidentally
removed the phrase that an existing test asserts on. Re-add it while
keeping the explicit write-tool instruction.

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

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher 2026-03-30 16:31:04 -04:00 committed by GitHub
parent 3d0eb32756
commit 0cedaf5fb9
4 changed files with 50 additions and 2 deletions

View file

@ -56,7 +56,7 @@ Then:
- `followUps` (string) — Follow-up items for future milestones
- `deviations` (string) — Deviations from the original plan
10. For each requirement whose status changed in step 8, call `gsd_requirement_update` with the requirement ID and updated `status` and `validation` fields — the tool regenerates `.gsd/REQUIREMENTS.md` automatically.
11. Update `.gsd/PROJECT.md` to reflect milestone completion and current project state.
11. Update `.gsd/PROJECT.md`: use the `write` tool with `path: ".gsd/PROJECT.md"` and `content` containing the full updated document reflecting milestone completion and current project state. Do NOT use the `edit` tool for this — PROJECT.md is a full-document refresh.
12. Review all slice summaries for cross-cutting lessons, patterns, or gotchas that emerged during this milestone. Append any non-obvious, reusable insights to `.gsd/KNOWLEDGE.md`.
13. Do not commit manually — the system auto-commits your changes after this unit completes.
- Say: "Milestone {{milestoneId}} complete."

View file

@ -31,7 +31,7 @@ Then:
10. Review task summaries for patterns, gotchas, or non-obvious lessons learned. If any would save future agents from repeating investigation or hitting the same issues, append them to `.gsd/KNOWLEDGE.md`. Only add entries that are genuinely useful — don't pad with obvious observations.
11. Call `gsd_complete_slice` with milestone_id, slice_id, the slice summary, and the UAT result. Do NOT manually mark the roadmap checkbox — the tool writes to the DB and renders the ROADMAP.md projection automatically.
12. Do not run git commands — the system commits your changes and handles any merge after this unit succeeds.
13. Update `.gsd/PROJECT.md` if it exists — refresh current state if needed.
13. Update `.gsd/PROJECT.md` if it exists — refresh current state if needed: use the `write` tool with `path: ".gsd/PROJECT.md"` and `content` containing the full updated document reflecting current project state. Do NOT use the `edit` tool for this — PROJECT.md is a full-document refresh.
**Autonomous execution:** Do not call `ask_user_questions` or `secure_env_collect`. You are running in auto-mode — there is no human available to answer questions. Make reasonable assumptions and document them in the slice summary. If a decision genuinely requires human input, note it in the summary and proceed with the best available option.

View file

@ -237,6 +237,33 @@ describe("complete-milestone", () => {
}
});
test("step 11 specifies write tool for PROJECT.md update (#2946)", () => {
const prompt = loadPromptFromWorktree("complete-milestone", {
workingDirectory: "/tmp/test-project",
milestoneId: "M001",
milestoneTitle: "Tool Guidance Test",
roadmapPath: ".gsd/milestones/M001/M001-ROADMAP.md",
inlinedContext: "context",
milestoneSummaryPath: ".gsd/milestones/M001/M001-SUMMARY.md",
skillActivation: "",
});
// Step 11 must explicitly name the `write` tool so the LLM doesn't
// confuse it with `edit` (which requires path + oldText + newText).
// See: https://github.com/gsd-build/gsd-2/issues/2946
assert.ok(
/PROJECT\.md.*\bwrite\b/i.test(prompt) || /\bwrite\b.*PROJECT\.md/i.test(prompt),
"step 11 must name the `write` tool when updating PROJECT.md",
);
// The prompt must NOT leave tool choice ambiguous for PROJECT.md
// Verify it mentions the required parameter (`content` or `path`)
assert.ok(
prompt.includes("`.gsd/PROJECT.md`") || prompt.includes('".gsd/PROJECT.md"'),
"step 11 must reference the PROJECT.md path explicitly",
);
});
test("deriveState completing-milestone integration", async () => {
const { deriveState, isMilestoneComplete } = await import("../state.ts");
const { invalidateAllCaches: invalidateAllCachesDynamic } = await import("../cache.ts");

View file

@ -406,6 +406,27 @@ console.log('\n=== complete-slice: handler with missing roadmap ===');
cleanup(dbPath);
}
// ═══════════════════════════════════════════════════════════════════════════
// complete-slice: step 13 specifies write tool for PROJECT.md (#2946)
// ═══════════════════════════════════════════════════════════════════════════
console.log('\n=== complete-slice: step 13 specifies write tool for PROJECT.md (#2946) ===');
{
const promptPath = path.join(
path.dirname(new URL(import.meta.url).pathname),
'..', 'prompts', 'complete-slice.md',
);
const prompt = fs.readFileSync(promptPath, 'utf-8');
// Step 13 must explicitly name the `write` tool so the LLM doesn't
// confuse it with `edit` (which requires path + oldText + newText).
// See: https://github.com/gsd-build/gsd-2/issues/2946
const mentionsWriteTool =
/PROJECT\.md.*\bwrite\b/i.test(prompt) ||
/\bwrite\b.*PROJECT\.md/i.test(prompt);
assertTrue(mentionsWriteTool, 'step 13 must name the `write` tool when updating PROJECT.md');
}
// ═══════════════════════════════════════════════════════════════════════════
report();