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: #7
Co-authored-by: Danny Kim <temeddix@gmail.com>
Co-committed-by: Danny Kim <temeddix@gmail.com>
This commit was merged in pull request #7.
This commit is contained in:
2026-09-13 16:54:54 +00:00
committed by temeddix
parent a2bc4c9541
commit 0d109ebec8
2 changed files with 23 additions and 19 deletions
+12 -14
View File
@@ -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 For a `pull_request` event, review the PR without changing code, using the
`requesting-code-review` skill from superpowers: run its code reviewer template `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 against the PR's base and head. Run the project's checks on the head and treat a
instead of the short comment style above. Run the project's checks on the head failure as at least Important. Check the whole repository against the code rules
and treat a failure as at least Important. Check the whole repository against at the end of this prompt, not only the diff; a violation is at least Important
the code rules at the end of this prompt, not only the diff; a violation is at even when the diff did not cause it. Write the complete review, and nothing
least Important even when the diff did not cause it. Your final response is else, to the file `${REVIEW_PATH}`: it is posted verbatim as a pull request
posted verbatim as a pull request review from the bot account, so it is the review from the bot account, and your final response is not posted at all. The
review text and nothing else: no narration about what you did, verified, or are file's first line must be exactly the template's verdict and nothing else:
about to post, whether you reviewed yourself or relayed a reviewer subagent. Its `Yes`, `No`, or `With fixes`. `Yes` approves and the other two request changes;
first line must be exactly the template's verdict and nothing else: `Yes`, `No`, any other first line is posted as a plain comment, which wastes the run. Minor
or `With fixes`. `Yes` approves and the other two request changes; any other issues alone never block, and neither does a finding the author has answered in
first line is posted as a plain comment, which wastes the run. Minor issues the comment history below as intended or a false alarm, once the code or docs
alone never block, and neither does a finding the author has answered in the make that clear. When the verdict is not `Yes`, the second line names what must
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 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 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 result is aligned, clean, and pixel-perfect, and that included screenshots prove
+11 -5
View File
@@ -59,9 +59,11 @@ async function postComment(body: string): Promise<void> {
}); });
} }
// A pull request event is a review request, so the response becomes a review. // A pull request event is a review request, so the review is posted instead
// Its first line is the verdict; anything unexpected only comments, never // of the response. It comes through a file, because a final chat message picks
// approves. // 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<string, string> = { const VERDICTS: Record<string, string> = {
Yes: "APPROVED", Yes: "APPROVED",
No: "REQUEST_CHANGES", No: "REQUEST_CHANGES",
@@ -70,9 +72,12 @@ const VERDICTS: Record<string, string> = {
async function postResult(body: string): Promise<void> { async function postResult(body: string): Promise<void> {
if (EVENT !== "pull_request") return postComment(body); 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`, { await gitea(REVIEWER_TOKEN, `repos/${REPO}/pulls/${INDEX}/reviews`, {
body: stripAnsi(body), body: stripAnsi(review),
event: VERDICTS[verdict.trim()] ?? "COMMENT", event: VERDICTS[verdict.trim()] ?? "COMMENT",
}); });
} }
@@ -118,6 +123,7 @@ async function renderPrompt(): Promise<string> {
GITEA_API_URL: API, GITEA_API_URL: API,
GITEA_REPOSITORY: REPO, GITEA_REPOSITORY: REPO,
ISSUE_INDEX: INDEX, ISSUE_INDEX: INDEX,
REVIEW_PATH,
}; };
const template = await Deno.readTextFile( const template = await Deno.readTextFile(
new URL("prompt.md", import.meta.url), new URL("prompt.md", import.meta.url),