From 0d109ebec8c5dc4c5750277df68e4ff9a307aea0 Mon Sep 17 00:00:00 2001 From: Danny Kim Date: Sun, 13 Sep 2026 16:54:54 +0000 Subject: [PATCH] Review file (#7) The reviewer writes its review to a file whose first line is the verdict, instead of relying on a narration-free final message. Sonnet put `Yes` after three paragraphs of narration on memona #936 (review 595), which the fail-closed verdict posted as a plain comment. Reviewed-on: https://git.capsulizers.com/commons/bot-agents/pulls/7 Co-authored-by: Danny Kim Co-committed-by: Danny Kim --- prompt.md | 26 ++++++++++++-------------- run.ts | 16 +++++++++++----- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/prompt.md b/prompt.md index ba0f4f5..5b360a7 100644 --- a/prompt.md +++ b/prompt.md @@ -20,20 +20,18 @@ they bear on the task, and push from there. For a `pull_request` event, review the PR without changing code, using the `requesting-code-review` skill from superpowers: run its code reviewer template -against the PR's base and head, and make its complete output your final response -instead of the short comment style above. Run the project's checks on the head -and treat a failure as at least Important. Check the whole repository against -the code rules at the end of this prompt, not only the diff; a violation is at -least Important even when the diff did not cause it. Your final response is -posted verbatim as a pull request review from the bot account, so it is the -review text and nothing else: no narration about what you did, verified, or are -about to post, whether you reviewed yourself or relayed a reviewer subagent. Its -first line must be exactly the template's verdict and nothing else: `Yes`, `No`, -or `With fixes`. `Yes` approves and the other two request changes; any other -first line is posted as a plain comment, which wastes the run. Minor issues -alone never block, and neither does a finding the author has answered in the -comment history below as intended or a false alarm, once the code or docs make -that clear. When the verdict is not `Yes`, the second line names what must +against the PR's base and head. Run the project's checks on the head and treat a +failure as at least Important. Check the whole repository against the code rules +at the end of this prompt, not only the diff; a violation is at least Important +even when the diff did not cause it. Write the complete review, and nothing +else, to the file `${REVIEW_PATH}`: it is posted verbatim as a pull request +review from the bot account, and your final response is not posted at all. The +file's first line must be exactly the template's verdict and nothing else: +`Yes`, `No`, or `With fixes`. `Yes` approves and the other two request changes; +any other first line is posted as a plain comment, which wastes the run. Minor +issues alone never block, and neither does a finding the author has answered in +the comment history below as intended or a false alarm, once the code or docs +make that clear. When the verdict is not `Yes`, the second line names what must change in one line, addressed to the author; the author's own agent picks the fixes up, so never ask `@bot` to make them. For UI changes, check that the result is aligned, clean, and pixel-perfect, and that included screenshots prove diff --git a/run.ts b/run.ts index 21c358c..b812b21 100644 --- a/run.ts +++ b/run.ts @@ -59,9 +59,11 @@ async function postComment(body: string): Promise { }); } -// A pull request event is a review request, so the response becomes a review. -// Its first line is the verdict; anything unexpected only comments, never -// approves. +// A pull request event is a review request, so the review is posted instead +// of the response. It comes through a file, because a final chat message picks +// up narration while a file's first line is written on purpose. That line is +// the verdict; anything unexpected only comments, never approves. +const REVIEW_PATH = `${await Deno.makeTempDir()}/review.md`; const VERDICTS: Record = { Yes: "APPROVED", No: "REQUEST_CHANGES", @@ -70,9 +72,12 @@ const VERDICTS: Record = { async function postResult(body: string): Promise { if (EVENT !== "pull_request") return postComment(body); - const [verdict] = body.split("\n", 1); + const review = await Deno.readTextFile(REVIEW_PATH).catch(() => { + throw new Error(`no review was written to ${REVIEW_PATH}`); + }); + const [verdict] = review.split("\n", 1); await gitea(REVIEWER_TOKEN, `repos/${REPO}/pulls/${INDEX}/reviews`, { - body: stripAnsi(body), + body: stripAnsi(review), event: VERDICTS[verdict.trim()] ?? "COMMENT", }); } @@ -118,6 +123,7 @@ async function renderPrompt(): Promise { GITEA_API_URL: API, GITEA_REPOSITORY: REPO, ISSUE_INDEX: INDEX, + REVIEW_PATH, }; const template = await Deno.readTextFile( new URL("prompt.md", import.meta.url),