# How we stop code review from getting stuck

A Machine agent spots pull requests that are overdue for review, have gone quiet, or still owe answers to requested changes, then pings the author or reviewer in Slack with what's holding them up.

Canonical page: https://themachine.myapps.ai/use-cases/pr-review-nudge

Nobody gets an alert when a pull request has waited three days for a review.
It just slides down the list as fresh ones pile on top. Eventually the author
messages someone directly, or quietly moves on to other work. Spread that
over a whole team and your review SLA turns into a nice idea that nobody
checks. "Changes requested" stops being a step and becomes a place where PRs
go to live.

So we gave the job to an agent on Machine. Once a day it reads every open
pull request across our repos and sends a Slack nudge for each one that's
stuck: overdue for review, gone quiet, or still waiting on requested changes.
It only reads GitHub. A Slack nudge is the one thing it produces. Merging,
closing and approving are never on the table.

- **Team:** Machine
- **Runs on:** A cron once a day
- **Connected systems:** GitHub · Slack
- **Mode:** Reads GitHub, never writes · nudges and short summaries only

## The problem

Writing a review SLA down takes a minute. Sticking to it is another matter.
No one watches the queue all day, so a PR opened first thing Monday that's
still untouched by Wednesday afternoon looks just like one opened a moment
ago, until somebody happens to scroll down far enough. Stale PRs are worse.
A branch with a week of no commits, no comments and no reviews has usually
been forgotten, and it decays out of sight until a merge conflict drags
someone back to it. Then there's "requested changes", which is a label, not
a warning. The reviewer asks for edits, the author reads them, the week gets
busy, and the PR sits there looking open when really it's blocked.

The usual habits leave gaps. GitHub notifications are easy to mute, and they
treat a PR from five minutes ago the same as one from five days ago. A weekly
standup hears about the loud blockers and misses the quiet ones. Reading
through the PR list by hand is careful the first week and forgotten by the
next.

## What we built

Each day, a cron on Machine starts an agent in a brand new session that can
read GitHub through the `gh` CLI and do nothing else there. It gathers every
open PR in our repos and checks it against three rules. Is it waiting on a
review past the SLA? Has it seen no activity for N days? Does it have
requested changes the author hasn't dealt with? When a PR breaks a rule, the
agent sends a Slack nudge to whoever has to act next. That's the requested
reviewer when a review is late, and the author when the PR is stale or has
open feedback. Each nudge carries one line saying what's really in the way.

## How it works

### Start once a day

A **cron trigger** kicks off the agent daily. Each run gets a new
**session**, a fresh cloud computer set up just for that run. One day means
one run, so the picture is rebuilt from what GitHub shows right now.
Yesterday's nudges aren't remembered or reused.

### Hand it the review rules

What "past SLA", "stale" and "not addressed" mean is written into a
**skill**, the playbook the agent carries. It holds the review clock, the
stale window, how to tell whether a requested-changes review has truly been
dealt with or is just sitting there, and who should get the nudge in each
case.

### Give it read-only GitHub access

The agent reads GitHub through the `gh` CLI, signed in with a scoped
**GH_TOKEN** secret that's added at run time. It looks at:

- **Every open pull request** in the repos we've listed: how old it is, who
wrote it, who's been asked to review, and where it stands.
- **The reviews and comments** on each one: whether a review came in, what it
asked for, and whether the author has pushed or answered since.
- **Slack, for posting.** The nudge itself, with a link to the PR and what's
blocking it.

### Draw the lines

The agent has **no write access to GitHub at all**. Merging, closing,
approving and dismissing reviews are all out of reach. The Slack nudge is its
only output. Credentials are stored encrypted in the Secrets Manager, added
at run time, and only handed to the agents you grant them to.

### Nudge whoever's up next

The result: each morning, every stuck PR gets its own Slack nudge. It names
who's being nudged, gives the reason (late review, gone quiet, or changes not
dealt with), says how long it's been like that, and links straight to the PR.
What happens next is the team's call. Review it, chase the reviewer in
person, or close it yourself.

> **The pattern**
> Once a day a **cron** opens a new session that reads open PRs through the
> `gh` CLI using a scoped **GH_TOKEN**. A **skill** holds the rules for late
> reviews, stale PRs and changes not dealt with. The agent can see it all,
> and the only thing it writes is a Slack nudge.

## Guardrails

The agent can see across every repo we point it at, so its reach is narrow
and flows one way only:

- **Kept apart.** Each run gets its own sealed-off sandbox. The session can
  reach GitHub, read-only, and Slack, and it sends nothing anywhere else.
- **Locked-down key.** The `GH_TOKEN` is stored encrypted in the Secrets
  Manager, added to the sandbox at run time, and only handed to the agents
  you grant it to.
- **Nudges and summaries, that's all.** It won't merge, close or approve a
  PR, and it won't dismiss or resolve a review. It says what's stuck and
  leaves the call to a person.
- **All of it lives in code.** The SLA limits, the stale window and what the
  agent may do in GitHub are files in the repo. They're versioned, and you
  change them with a reviewed **change request**, not a setting on a
  dashboard.

## The outcome

- **Every day:** Each open PR checked again against GitHub as it is now
- **3 checks:** Late for review, gone quiet, changes not dealt with, all in one run
- **Zero:** PRs merged, closed or approved by the agent

Our review queue used to depend on someone remembering to scroll it. Now it
reports its own blockers each morning in Slack, to whoever needs to move
next. The agent reads and nudges, nothing more. Every PR's fate is still the
team's decision.
