From a512bd79d7b5ecc789ed5baa7fa8c5f37b8c8997 Mon Sep 17 00:00:00 2001 From: Tom Boucher Date: Sun, 24 May 2026 19:19:50 -0400 Subject: [PATCH] fix(#222): enforce research synthesizer write-only summary contract (#240) --- .changeset/curious-tigers-jump.md | 5 ++ agents/gsd-research-synthesizer.md | 12 +++- ...search-synthesizer-write-contract.test.cjs | 56 +++++++++++++++++++ 3 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 .changeset/curious-tigers-jump.md create mode 100644 tests/bug-222-research-synthesizer-write-contract.test.cjs diff --git a/.changeset/curious-tigers-jump.md b/.changeset/curious-tigers-jump.md new file mode 100644 index 000000000..1b7df33cb --- /dev/null +++ b/.changeset/curious-tigers-jump.md @@ -0,0 +1,5 @@ +--- +type: Fixed +pr: 222 +--- +**`gsd-research-synthesizer` now hard-enforces Write-tool SUMMARY creation semantics** — Step 6 now declares SUMMARY.md-on-disk as canonical output and adds explicit hard rules against returning content in the response, permission-asking, and heredoc fallback. This closes the intermittent hallucinated write-restriction failure mode reported in #222. (#222) diff --git a/agents/gsd-research-synthesizer.md b/agents/gsd-research-synthesizer.md index d1c61090b..e0ed845f2 100644 --- a/agents/gsd-research-synthesizer.md +++ b/agents/gsd-research-synthesizer.md @@ -128,11 +128,19 @@ Identify gaps that couldn't be resolved and need attention during planning. ## Step 6: Write SUMMARY.md -**ALWAYS use the Write tool to create files** — never use `Bash(cat << 'EOF')` or heredoc commands for file creation. +**This is the canonical output of this agent. The orchestrator depends on `.planning/research/SUMMARY.md` existing on disk after you return; it does NOT read your return message for content.** + +**Hard rules (must follow):** + +1. **Use the `Write` tool** to write the file. The `Write` tool is in your `tools:` allowlist; there are no restrictions on it. Do not assume restrictions that the frontmatter does not impose. +2. **Do NOT return the SUMMARY.md content in your response.** Your return message is a brief confirmation (see `` below); the content lives on disk. +3. **Do NOT ask permission to write.** Writing `.planning/research/SUMMARY.md` is the explicit purpose of this agent. Asking the orchestrator to do it instead is a failure mode that can cause downstream `SUMMARY.md not found` failures. +4. **Do NOT use `Bash(cat << 'EOF')` or heredoc** for file creation. Use the `Write` tool. +5. **If the Write tool errors,** surface the actual error in your return message. Do not silently fall back to returning content; that hides the failure from the orchestrator. Use template: ~/.claude/get-shit-done/templates/research-project/SUMMARY.md -Write to `.planning/research/SUMMARY.md` +Write to `.planning/research/SUMMARY.md`. ## Step 7: Commit All Research diff --git a/tests/bug-222-research-synthesizer-write-contract.test.cjs b/tests/bug-222-research-synthesizer-write-contract.test.cjs new file mode 100644 index 000000000..fa7f53510 --- /dev/null +++ b/tests/bug-222-research-synthesizer-write-contract.test.cjs @@ -0,0 +1,56 @@ +// allow-test-rule: source-text-is-the-product +'use strict'; + +const { test, describe } = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const path = require('node:path'); + +const REPO_ROOT = path.resolve(__dirname, '..'); +const SYNTHESIZER_PATH = path.join(REPO_ROOT, 'agents', 'gsd-research-synthesizer.md'); + +function readSynthesizerPrompt() { + return fs.readFileSync(SYNTHESIZER_PATH, 'utf8'); +} + +describe('bug #222: research synthesizer must write SUMMARY.md via Write tool', () => { + test('step 6 has explicit hard-rule block forbidding return-message content fallback', () => { + const prompt = readSynthesizerPrompt(); + + assert.match( + prompt, + /canonical output of this agent[\s\S]*existing on disk after you return/i, + 'Step 6 must define SUMMARY.md-on-disk as canonical output.' + ); + assert.match( + prompt, + /Hard rules \(must follow\):/i, + 'Step 6 must contain explicit hard rules block.' + ); + assert.match( + prompt, + /Use the `Write` tool[\s\S]*there are no restrictions/i, + 'Rule 1 must force Write tool usage and reject hallucinated restrictions.' + ); + assert.match( + prompt, + /Do NOT return the SUMMARY\.md content in your response/i, + 'Rule 2 must forbid returning SUMMARY content in the response body.' + ); + assert.match( + prompt, + /Do NOT ask permission to write/i, + 'Rule 3 must forbid write-permission asks for this agent.' + ); + assert.match( + prompt, + /Do NOT use `Bash\(cat << 'EOF'\)` or heredoc/i, + 'Rule 4 must forbid heredoc/Bash file creation fallback.' + ); + assert.match( + prompt, + /If the Write tool errors[\s\S]*Do not silently fall back to returning content/i, + 'Rule 5 must force explicit error reporting for Write failures.' + ); + }); +});