From f97272c240181b48a4efab197e3b646acd8103fe Mon Sep 17 00:00:00 2001 From: Josh Mabry Date: Fri, 3 Jul 2026 12:48:44 -0700 Subject: [PATCH] =?UTF-8?q?fix:=20coder's=20final=20reply=20becomes=20the?= =?UTF-8?q?=20PR=20body=20verbatim=20=E2=80=94=20ask=20for=20a=20clean=20s?= =?UTF-8?q?ummary=20(v0.26.1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit open_pr(..., body=(result or "")[:4000]) uses the coder's raw final ACP reply as the PR description with zero post-processing, in both the plain single-dispatch path and Max-Mode's winner. Nothing in _build_prompt ever told the coder its final message would be used this way, so an un-briefed coder (proto or claude) defaults to narrating its whole process -- exploration, step-by-step reasoning, restating the task -- straight into the PR body. Add an explicit rule: the final message becomes the PR description verbatim, so end with a short, clean summary for a reviewer (what changed + why, 2-6 sentences or a few bullets) -- not the coder's scratch reasoning. coder_seam.dispatch's path (ADR 0064) already synthesizes its own clean result_text and is unaffected. Co-Authored-By: Claude Opus 4.8 --- loop.py | 6 ++++++ protoagent.plugin.yaml | 2 +- pyproject.toml | 2 +- tests/test_loop.py | 11 +++++++++++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/loop.py b/loop.py index 8fde737..a05986f 100644 --- a/loop.py +++ b/loop.py @@ -1240,6 +1240,12 @@ def _build_prompt(self, feature: dict, lessons: str = "") -> str: f"write a single line `NO_TEST_NEEDED: ` in your final message instead.\n" f"- You cannot run shell commands (edit-only); the tests you write run in CI " f"on the PR, so they must be correct and self-contained.\n" + f"- **Your FINAL message becomes the PR description, verbatim.** End with a " + f"short, clean summary for a reviewer — what changed and why, 2-6 sentences " + f"or a few bullet points. Do NOT narrate your process: no step-by-step " + f'exploration, no "I first looked at..."/"Let me...", no restating these ' + f"instructions or the acceptance criteria back. If you used scratch " + f"reasoning to get here, leave it out of this message entirely.\n" f"- You are done when the listed files exist, tests cover the change, and " f"every acceptance criterion is satisfied." ) diff --git a/protoagent.plugin.yaml b/protoagent.plugin.yaml index e680125..0b0cafb 100644 --- a/protoagent.plugin.yaml +++ b/protoagent.plugin.yaml @@ -1,6 +1,6 @@ id: project_board name: Project Board (coding orchestration) -version: 0.26.0 +version: 0.26.1 description: >- A board-driven coding-orchestration plugin: a lean 6-state board (backlog → ready → in_progress → in_review → done, + a blocked flag) backed by **beads** (`br`), an diff --git a/pyproject.toml b/pyproject.toml index 9175a50..29d4408 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "project-board" -version = "0.26.0" +version = "0.26.1" description = "Board-driven coding-orchestration plugin for protoAgent (beads board + ACP spawn loop + planning layer + console view)." requires-python = ">=3.11" diff --git a/tests/test_loop.py b/tests/test_loop.py index 23eb589..951db09 100644 --- a/tests/test_loop.py +++ b/tests/test_loop.py @@ -102,6 +102,17 @@ def test_build_prompt_requires_tests(): assert "rejected before the pr opens" in prompt +def test_build_prompt_asks_for_a_clean_pr_summary_not_raw_reasoning(): + """The coder's final reply is used VERBATIM as the PR body (loop.py's + `open_pr(..., body=(result or "")[:4000])`) with no post-processing — so an + un-briefed coder narrating its whole thought process ships that straight into + the PR description. The prompt must say so explicitly and ask for a short, + clean summary instead.""" + prompt = BoardLoop({})._build_prompt(FEATURE) + assert "final message becomes the pr description" in prompt.lower() + assert "do not narrate your process" in prompt.lower() + + def test_is_test_path_classification(): """The deterministic gate's path classifier — what counts as a test vs code.""" from project_board.loop import _is_code_path, _is_test_path