pr-review-loop
Open a PR (or attach to an existing one), then settle it against the two signals only GitHub can supply — CI status and comments from human reviewers — in a small, bounded number of rounds. It never merges, never posts comments, and never waits on a review bot.
- Triggered by:
xiv pr refine [prNumber](aliasprl) — see /commands/pr - Also runs as: the tail subflow of
linear-to-pr(xiv ship) - Source:
pack/workflows/pr-review-loop.tsx
Task graph
Open vs. attach
The branch splits on whether the caller supplied a PR number:
- No
prNumber(thexiv shippath, orxiv pr refine --branch <name>):describereads the branch's diff against the base and writes a structured description (Summary/Test Plan/Known gaps— theprDescriptionSchema), thenopen-prcreates the PR with that body plus a deterministic footer (Linear issue link).describeiscontinueOnFail: if description generation breaks, the PR still opens with a plain template body — a thin PR beats no PR. prNumbersupplied (xiv pr refine 1234, or the current branch's PR):describeandopen-prare skipped andattach-prdoes a read-onlygh pr viewto confirm the PR exists and is open, capturing its URL and head SHA. It never creates a PR or pushes.
The loop: signals → fix
Each round takes one snapshot and acts on it — the signals step is explicitly not a poll; nothing sleeps or waits on a remote reviewer.
rev:signalsreads all four comment surfaces (gh pr checks, formal reviews, inline diff comments, issue/conversation comments), classifies CI aspassing/failing/pending/none, and emits findings: one per failing check (source: "ci", quoting the actual failure) and one per unresolved human comment (source: "human"). Bot comments are ignored entirely; "unresolved" is judged against the actual code at the head, not GitHub's thread flags. It setsclean: trueonly when CI is passing-or-none and there are no findings.rev:fixaddresses each finding properly (root cause, not check-loosening; meaningful tests for behavior comments), pushes, and reportsaddressed[]/skipped[](skips carry an honest, human-readable reason). It is skipped when there is genuinely nothing to fix: no snapshot yet, everything already clean, or checks still pending with nothing red and no comment to answer —clean: falsealone is not a reason to edit code.
Exit conditions and cap
The loop exits when the latest snapshot reports clean: true. The cap is maxRounds, default
2, and hitting it returns the last state rather than failing (onMaxReached="return-last").
The cap is deliberately low: code review already happened locally before the PR existed (see /concepts/review-model), so the only things left to chase here are a red build and a human comment. Two rounds fixes a failure and confirms the fix; anything beyond that is a person's call, not a loop's.
After the loop, rev:report states the final position: either "green and no outstanding
comment — ready for a human to review and merge", or exactly what is still open and who needs to
act.
Inputs and outputs
Input:
| Field | Default | Meaning |
|---|---|---|
prNumber | (optional) | Attach to this existing PR instead of opening one. |
branch | "" | Branch to open a PR from (falls back to the current branch). |
base | "main" | PR base branch. |
issueContext | (optional) | { key, title, url, acceptanceCriteria[] } — used for the title, body, and footer. |
knownGaps[] | [] | Findings the implement loop shipped without fixing, forwarded by linear-to-pr so the PR names its own limitations. Empty when run directly against a branch. |
maxRounds | 2 | Loop cap. |
Output (the table named output, consumed by linear-to-pr):
| Field | Meaning |
|---|---|
prUrl | The PR settled or opened. |
resolved | True only when clean at exit. |
pending[] | Outstanding items for a human. |
summary | Final state in prose. |
Models and failure semantics
Every task here runs on the autonomous agent pool — the heavy Claude model with git/gh
permissions and a hard never-merge system prompt — except rev:fix, which runs on the smart
pool (heavy Claude, tiered by XIV_TIER). Under XIV_ENGINE=codex everything moves to the codex
agent. See /concepts/model-tiers.
describeis the onlycontinueOnFailtask; its fallback is the plain template body.attach-prfailing (PR closed or missing) fails the run rather than opening a duplicate.- Task timeouts: 15 minutes for describe/open, 10 for signals, 30 for fix, all with heartbeats.