DEV Community

Javier Leandro Arancibia
Javier Leandro Arancibia

Posted on

Felipe — The AI CEO That Reviews My Pull Requests

I have an AI fleet that opens pull requests on my repos every day. It builds, tests, and verifies each one before pushing. But someone still has to review and merge them. That someone was me — and I was the bottleneck.


My AutoMaintainer fleet runs 24/7. It opens PRs, rebases them, and tries to merge them automatically. About 80% get merged without human help. The remaining 20% escalate to a human-in-the-loop queue — merge conflicts the rebaser can't resolve, PRs that need a judgment call, duplicates that need triage.

Last week I checked the queue. 20 PRs waiting for my decision. The oldest had been stuck for 703 hours — 29 days. Some were duplicates of each other. Some were trivial doc fixes blocked by context drift. All needed me to type one of:

  • CEO: approve — merge as-is
  • CEO: approve rebase — try to resolve conflicts
  • CEO: deny — close the PR
  • CEO: split into <description> — break into smaller PRs

I was the bottleneck. Not because the decisions are hard — most are obvious — but because I don't check the queue every day, and 20 PRs pile up fast.

The fix: an AI persona that replaces me

I already had the infrastructure. My fleet-cli engine runs scheduled loops on a Proxmox container. The am-fleet HITL system already watches for replies on GitHub issues and applies decisions automatically. The only missing piece was someone to actually make the decisions.

So I built Felipe — a fleet persona that runs once a day, reads every stuck PR, and posts a CEO decision as a comment on the GitHub issue. The existing am-hitl-resume script picks up the reply within 5 minutes and applies it.

The whole loop is ~200 lines of bash.

How Felipe works

Once a day at 09:00 UTC, a systemd timer fires fleet-felipe-decide.service on rbm21. The script does five things:

1. Fetch the queue. gh issue list --repo javimosch/am-fleet --label mago:hitl --state open — same command I'd run manually.

2. Read each PR. For every issue, Felipe extracts the repo and PR number, fetches the diff (first 500 lines), CI status, and merge state via gh pr view and gh pr diff.

3. Ask the LLM. A structured prompt goes to devin --model glm-5-2 --print — GLM-5.2 High, which is free on Devin. The prompt includes the PR size, file list, CI status, the full diff, and the HITL issue body. The rules are explicit:

1. CEO: approve       — if PR <200 lines, CI passes, changes look safe
2. CEO: deny           — if stale >7 days, duplicate, or broken
3. CEO: approve rebase — if conflicts but valuable and <100 lines
4. CEO: split into     — if >500 lines mixing unrelated changes
5. CEO: <custom>       — anything else
Enter fullscreen mode Exit fullscreen mode

4. Post the decision. Felipe parses the LLM response for a line starting with CEO: and posts it as a comment on the GitHub issue, signed "Felipe (AI CEO)".

5. The existing machinery takes over. am-hitl-resume.sh runs every 5 minutes, detects the reply, parses the decision, and applies it — approving the PR, closing it, or requesting a rebase. The HITL issue is closed. The loop is complete.

The first run: 20 PRs in 5 minutes

I deployed Felipe and triggered a manual run. It processed all 20 HITL issues in about 5 minutes:

  • 14 × approve rebase — small PRs with trivial conflicts worth retrying
  • 4 × deny — stale conflicts older than 7 days, too large to rebase
  • 2 × split — 350+ line PRs mixing unrelated concerns

The decisions were good. Felipe caught that supercli#370 was a small doc-only change (24 lines, install instructions) and approved a rebase. It caught that scan-and-fill#7 was 352 lines mixing three unrelated features (amount parsing, AI caching, async refactor) and asked for a split. It denied superinsights#29 because it had been conflicting for 8 days and the conflicts spanned multiple files.

Within 10 minutes, am-hitl-resume had processed the first reply — approved PR #568, labeled it human-approved, and closed the HITL issue. The rebaser picked it up on the next cycle.

The cost: $0

Felipe uses GLM-5.2 High, which is free on Devin. Each PR review sends an ~8K token prompt (diff + metadata + rules). For 20 PRs, that's ~160K tokens in, ~5K tokens out. On a paid model like Claude Sonnet 5 Low, that would cost ~$0.40. On GLM-5.2, it costs nothing.

The quality is good enough for this task. PR review decisions are bounded — there are only 5 possible actions, the rules are explicit, and the LLM just needs to read a diff and pick one. GLM-5.2 handles this without trouble. I wouldn't trust it with open-ended architecture decisions, but for "should I rebase this 24-line doc fix?" it's more than sufficient.

The bug that almost killed it

The first version processed 1 PR and stopped. The cause was a classic bash gotcha: commands inside a while read loop consume stdin from the loop's file redirect.

The loop was while IFS= read -r issue; do ... done < issues.jsonl. Inside the loop, gh pr diff and gh issue comment were reading from stdin — which was the JSONL file. They ate the remaining lines, so read got EOF on the next iteration.

The fix: </dev/null on every command inside the loop. One redirect, three commands, 20 PRs processed.

# Before (broken — gh eats the loop's stdin)
gh pr diff "$pr_num" --repo "$repo" 2>/dev/null
gh issue comment "$issue_num" --repo "$FLEET_REPO" --body "$body"

# After (fixed — /dev/null on every subcommand)
gh pr diff "$pr_num" --repo "$repo" </dev/null 2>/dev/null
gh issue comment "$issue_num" --repo "$FLEET_REPO" --body "$body" </dev/null
Enter fullscreen mode Exit fullscreen mode

Why this matters

The pattern here is bigger than PR review. It's delegation to an AI persona. Felipe has a name, a role (CEO), a decision framework, and a schedule. It doesn't replace me — I can override any decision by replying on the GitHub issue. But it handles the 90% of decisions that are obvious, so I only see the 10% that actually need human judgment.

The architecture is composable:

  • fleet-cli provides the scheduling and execution framework
  • am-fleet provides the HITL queue and decision-application machinery
  • Devin + GLM-5.2 provides the LLM inference, free
  • GitHub Issues provides the communication channel — Felipe posts a comment, the resume script reads it, no custom API needed

No new infrastructure. No new database. No new API. Just a 200-line bash script that bridges three existing systems.

Try it

Felipe is part of my private fleets repository. The fleet-cli engine is open source. The am-fleet HITL scripts are on GitHub.

If you have a fleet-cli setup and want to add Felipe, the loop is straightforward: fetch your HITL queue, read each PR, ask an LLM for a decision, post it back. The hardest part is the bash stdin bug. Now you know about it.

Next in this series: The Ratchet — how the AI merger self-unblocks 80% of PRs before escalating to Felipe.

Top comments (0)