From 7bbc0dd621619951d1308637a9a5722ed118cc7f Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Sun, 5 Apr 2026 01:04:52 -0400 Subject: [PATCH] fix: add filesystem safety guard to complete-slice.md (#3304) * fix: add filesystem safety guard to complete-slice.md (closes #2935) Port the EISDIR prevention instruction from complete-milestone.md to complete-slice.md so the LLM never passes a directory path to the read tool when task summaries are truncated by the 30k-char cap. Co-Authored-By: Claude Opus 4.6 * chore: retrigger CI (flaky #2912 MERGE_HEAD test) * chore: retrigger CI --------- Co-authored-by: Claude Opus 4.6 Co-authored-by: trek-e --- .../extensions/gsd/prompts/complete-slice.md | 2 ++ .../gsd/tests/prompt-contracts.test.ts | 25 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/resources/extensions/gsd/prompts/complete-slice.md b/src/resources/extensions/gsd/prompts/complete-slice.md index 7066d5fd9..2a3d0ca7b 100644 --- a/src/resources/extensions/gsd/prompts/complete-slice.md +++ b/src/resources/extensions/gsd/prompts/complete-slice.md @@ -35,6 +35,8 @@ Then: **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. +**File system safety:** Task summaries are preloaded in the inlined context above. If you need to re-read any of them, use `find .gsd/milestones/{{milestoneId}}/slices/{{sliceId}}/tasks -name "*-SUMMARY.md"` to list file paths first — never pass `{{slicePath}}` or any other directory path directly to the `read` tool. The `read` tool only accepts file paths, not directories. + **You MUST call `gsd_complete_slice` with the slice summary and UAT content before finishing. The tool persists to both DB and disk and renders `{{sliceSummaryPath}}` and `{{sliceUatPath}}` automatically.** When done, say: "Slice {{sliceId}} complete." diff --git a/src/resources/extensions/gsd/tests/prompt-contracts.test.ts b/src/resources/extensions/gsd/tests/prompt-contracts.test.ts index 5f6f938a5..c653b6a17 100644 --- a/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +++ b/src/resources/extensions/gsd/tests/prompt-contracts.test.ts @@ -231,6 +231,31 @@ test("complete-slice prompt uses camelCase parameter names matching TypeBox sche assert.match(toolCallLine!, /sliceId/); }); +// ─── File system safety: complete-slice parity with complete-milestone (#2935) ── + +test("complete-slice prompt includes filesystem safety guard against EISDIR", () => { + const prompt = readPrompt("complete-slice"); + assert.match( + prompt, + /File system safety/i, + "complete-slice.md must include a 'File system safety' instruction to prevent EISDIR errors when the LLM passes a directory path to the read tool" + ); + assert.match( + prompt, + /never pass.*directory path.*directly to the.*read.*tool/i, + "complete-slice.md must warn against passing directory paths to the read tool" + ); +}); + +test("complete-milestone prompt still has its filesystem safety guard (regression)", () => { + const prompt = readPrompt("complete-milestone"); + assert.match( + prompt, + /File system safety/i, + "complete-milestone.md must keep its filesystem safety guard" + ); +}); + test("reactive-execute prompt references tool calls instead of checkbox updates", () => { const prompt = readPrompt("reactive-execute"); assert.doesNotMatch(prompt, /checkbox updates/);