Here's the idea: a reviewer leaves a comment on my PR at 11 at night, and by morning the fix is pushed and the thread says "Addressed in" with a commit hash.

I built it this week. It's the first agent in my system that writes, unattended, to code other people review, so most of the work went into what it isn't allowed to do.

Where it runs

I work across several laptops. Each one runs a small watcher of mine that reports my open PRs and runs jobs I sign from my Mac or my phone. A laptop acts only on what my devices signed.

The PR fixer is one more job for that watcher, and it ships switched off. It runs only when a setting signed by my Mac turns it on for that laptop.

One review comment, end to end
  1. 1A reviewer comments
  2. 2Ten minutes to settle
  3. 3The model edits files
  4. 4Code commits, pushes, replies

The guardrails, in order

  1. 1When it may actOnly on an unresolved thread whose newest comment comes from a reviewer on that PR, or from a login or review bot I named for that repo. Not "any member of the org": on a client's repo, that's the whole client. Never my own comments, never anything carrying the hidden marker its own replies include, and only after a 10-minute settle. An edit after that counts as a new comment.
  2. 2What it may touchA clean, detached checkout of the PR's head, with no .env. The model gets file tools, read and edit, and nothing else: no shell, no web, no connectors, no gh. The reviewer's words arrive fenced as data, never as instructions.
  3. 3What code does insteadPlain Node does every git and GitHub step. It checks the diff, commits with hooks disabled so no script from the branch can run, and pushes to the PR's own branch: never with force, and never if the remote moved underneath it. It runs no repo code locally; CI judges the fix.
  4. 4CapsA diff guard: at most 15 files and 400 lines, and no changes to CI config, git hooks, agent settings, CLAUDE.md, package scripts or lockfiles. At most 2 fix rounds per thread and 5 per PR per day. One model run at a time per laptop, checked every 15 minutes.
  5. 5RepliesEvery comment it handles gets an answer: "Addressed in" and the commit, or "Not changed" and the reason. I wanted an answer on everything; one setting turns the second kind off. Handled comment ids are stored, so it never answers the same comment twice.

≤0

files per fix, and no more than 400 lines

a bigger diff is refused, not trimmed

Your prompts get your powers. A reviewer's words get a cage.

This is the rule I'd keep if I threw the rest away. When I ask a laptop to do something, the request is mine, signed by my device, and it runs with tools I chose.

A review comment is text somebody else wrote. If the model treats it as instructions, anyone who can comment on your PR can drive your agent. So the comment goes in as fenced data, and the model that reads it has nothing dangerous to reach for.

Before

If the model held the keys

  • A comment saying "also push this to main" is a request it might follow
  • A hook script on the branch runs when it commits
  • Its own replies can trigger the next round
  • A sweeping "fix" nobody asked for lands in one push

After

With code holding the keys

  • The model can only edit files in a clean checkout
  • Commits run with hooks disabled
  • Its replies carry a marker that never triggers it
  • Anything over 15 files or 400 lines is refused

Why the model gets no shell

I could have given the model git and asked it nicely never to force-push. Instead it can't run a command at all. Every step that could hurt someone, the commit, the push, the reply, is ordinary code with ordinary tests.

That's also why the fixer is Claude Code only. Cursor's agent CLI, which the watcher also supports for other jobs, can't take the shell and the web away from a run, and this cage needs exactly that.

How it's tested

The decision to act is a pure function with 16 checks: an outsider's comment, my own, one carrying the marker, an edit after the settle, the round caps. A pure function is the easiest thing there is to test, which is exactly why the riskiest decision lives in one.

An end-to-end smoke test runs against a real PR branch, with 8 checks: fix, push, reply, remember; no second pass on its own reply; "Not changed" when there's nothing to change; my own comment ignored; a protected file refused.

The rollout starts on a scratch repo. A comment from a second account must produce a fix, a push and a reply, while my own reply, a bot's reply and a round past the cap must produce nothing at all.

What stays mine

The fixer answers review comments. It doesn't merge, and it doesn't overrule a reviewer on my behalf: when it changes nothing, it says so with the reason, and the thread is mine again.

Give the model the smallest job that's still useful. Give everything else to code you can test.

Building with AI?Let's talk about your appBook a free intro call