Skip to main content
The shape: a workflow on pull_request runs one script. The script sends the PR’s diff to a session, collects a structured review, and posts it as a single comment it edits on every later push. A blocking finding fails the job. The script is standard library only — node review.mjs, nothing to install.

The workflow

cancel-in-progress stops the review of a commit that has already been superseded. That kills the script mid-run, which is why the script deletes its session on SIGTERM — see below. Pull requests from forks get no secrets under pull_request, so the job fails for them. Do not switch to pull_request_target to fix that without reading GitHub’s guidance on it: that event runs with your secrets against code you have not reviewed.

The script

Five choices in it that are not obvious: The diff goes in as a file, not in the prompt. environment.files puts it at /workspace/pr.diff, where the agent can read, grep and re-read it. A file may be 5 MiB; a whole request body without files is 1 MiB. See limits. No repository connection is needed. The session holds the diff and nothing else — no clone, no credential to your repository — which also means nothing it does can reach your code. The review is a file, and the script checks it. A schema in the instructions is a request, not a guarantee. The agent writes /workspace/outputs/review.json; the script parses and validates it, and on a bad one sends a single correction and reads again. In testing, a reply-only version of this script failed on its first run because the agent’s reply began with prose. See Turn documents into structured JSON. One comment, found by a marker. <!-- gobare-review --> opens the comment, so the next push edits it rather than stacking a new one on the PR. The idempotency key includes the run and attempt. Within one run, a retried POST replays the same session. “Re-run job” is a new attempt and gets a fresh review — the previous attempt’s session is already deleted, and a key that replayed it would point at nothing. GitHub’s own API address is read from GITHUB_API_URL, which Actions sets. That is also what makes the script testable against a mock.

What was verified

Run against production, with a local mock of the GitHub API standing in for github.com — it served a real diff and recorded the comments:
  • A real pull request. The diff of expressjs/express#7459, fetched from GitHub, was reviewed; the agent summarised the fix correctly and reported no findings. The job passed.
  • A bad one. A diff that replaced a parameterised query with an interpolated one produced two blocking findings — the SQL injection, and the signature change that broke existing callers — and the job exited 1.
  • Re-runs. A second run on the same PR edited comment #1 rather than posting a second one.
  • Cancellation. SIGTERM eight seconds into a run exited 130 and left no session behind.
Not verified: GitHub itself — permissions, fork behaviour and the Actions runner were not exercised.

Next