Open source PRs die in review. The logic is often fine. The diff is untested, oversized, or off-style.
Maintainers bounce it back for another round. Coding agents turned every developer into a reviewer. The reviewer's output rarely gets verified.
This loop verifies it before a human does. It has four phases: reproduce, patch, test, review.
Free model access makes the last phase nearly free. MonkeyCode is an open source coding agent with a current free tier of 10M tokens and a free server option. Disclosure: This article was prepared as part of MonkeyCode's product outreach.
1. Reproduce: the failing test is the contract
A bug report is not evidence. A failing test is evidence.
Write the test first. Watch it fail. Save the output.
- Clone the repository.
- Create a branch from the default branch.
- Write a test that documents the bug.
- Run only that test.
- Confirm it fails for the reported reason.
git clone https://github.com/example/project.git
cd project
git checkout -b fix/issue-123
cat > tests/repro-123.test.js <<'EOF'
// Documents issue #123: empty input crashes the parser
const { parse } = require('../src/parser');
test('parse handles empty input', () => {
expect(parse('')).toEqual([]);
});
EOF
npm test -- tests/repro-123.test.js
Expected output: one failing test. Copy that output. It becomes the evidence section of the PR description and the constraint for the model.
2. Patch: constrain the model to the failure
Send the failure log to a coding model. The instruction is short. The failing test is the constraint.
The model must not touch anything else.
The test tests/repro-123.test.js fails. The failure log is below.
Propose the smallest diff that makes it pass.
Do not refactor unrelated code. Do not add features.
Output only a unified diff.
Apply the diff manually. Read it first. Never apply a diff you do not understand.
# save the model output to /tmp/fix.patch, then:
git apply /tmp/fix.patch
npm test -- tests/repro-123.test.js
The test passes now. Run the full suite. A fix that breaks two other tests is not a fix.
npm test
3. Test: the full suite is the gate
The full suite is the real reviewer. It catches regressions the model cannot see. It catches assumptions the model made.
Green means the patch is safe. Green does not mean the patch is good. Run the suite in a clean environment.
The loop mutates a repository. A disposable sandbox prevents local state from leaking into the result. MonkeyCode's free server option is one way to get that sandbox.
4. Review: a second model pass on your own diff
The final phase is a strict self-review. A fresh model pass reads the diff with cold eyes. It looks for edge cases, missing tests, and style drift.
Review this diff as a strict maintainer.
List only: missing edge cases, untested branches, style violations, scope creep.
Do not praise the code. Number each item.
Compare the model's list with the diff. Fix what is real. Ignore what is noise.
The model is a second pair of eyes. It is not the first pair.
Run this pass before opening the PR. Maintainers see the second version, not the first.
The model review compresses the feedback loop. One round-trip becomes zero.
| The model review catches | The model review misses |
|---|---|
| Missing edge-case tests | Architectural debt |
| Style drift from project conventions | Cross-module side effects |
| Scope creep in the diff | Performance regressions at scale |
| Null-handling and off-by-one errors | License and compliance problems |
The complete loop as a script
The four phases fit in one script. The patch step stays manual. Model output needs human judgment before git apply.
#!/usr/bin/env bash
# pr-loop.sh — reproduce, patch, test, review
# usage: ./pr-loop.sh <repo-url> <issue-number> <test-file>
set -euo pipefail
REPO_URL="$1"
ISSUE="$2"
TEST_FILE="$3"
WORKDIR="$(mktemp -d)"
trap 'rm -rf "$WORKDIR"' EXIT
git clone --quiet "$REPO_URL" "$WORKDIR/repo"
cd "$WORKDIR/repo"
git checkout --quiet -b fix/issue-"$ISSUE"
echo "[1/4] Reproduce"
npm test -- "$TEST_FILE" > /tmp/repro.log 2>&1 || true
grep -q "fail" /tmp/repro.log || { echo "Bug not reproduced"; exit 1; }
echo "[2/4] Patch — save the model diff to /tmp/fix.patch"
test -f /tmp/fix.patch || { echo "Missing /tmp/fix.patch"; exit 1; }
git apply /tmp/fix.patch
echo "[3/4] Verify"
npm test -- "$TEST_FILE" > /tmp/verify.log 2>&1 || true
grep -q "pass" /tmp/verify.log || { echo "Patch incomplete"; exit 1; }
echo "[4/4] Full suite"
npm test
echo "Review this diff:"
git diff main...HEAD # adjust if the default branch is not main
The grep checks are deliberately naive. Test runners format output differently. Adapt them to your runner.
The script is a template, not a product.
The trap deletes the working directory on exit. Debug failures outside the loop. Rerun the loop after each fix.
Limitations
Free models hallucinate test expectations. A passing test with the wrong assertion is worse than no test.
The model review finds style issues, not architectural ones. It cannot see the whole codebase.
The loop assumes tests already exist. It does not rescue a legacy project with no suite. The script also assumes npm and a default branch named main.
Who should not use this
Security patches need a human reviewer with context. Compliance-heavy teams need a written AI policy first. Developers who cannot read a diff should not trust a model to read it for them.
The loop amplifies judgment. It does not replace it.
Try it on your next PR
The loop is the point. Reproduce. Patch. Test. Review.
The discipline makes the first three reliable. Free models make the last one cheap.
If you want to run it without an API key, the MonkeyCode free tier is enough to test the loop. The project is open source. Run the loop and decide for yourself.
Top comments (0)