Reviews
One review pass over a pull request: launch it, read the per-reviewer telemetry, and see the outcome stamped on the PR.
A review is one pass over a pull request. IonWarp reads the diff, picks a reviewer lineup, runs each reviewer, merges and dedupes what they found, and posts the result back to GitHub as PR comments. It runs automatically on every watched PR, and you can start one yourself from the PR page, from a task, or from the API.
Where a review got to is recorded on the pull request itself — review_status,
last_reviewed_at, last_review_run_id — so the PR row is what you query:
List pull requests — GET /api/v1/pull-requests
curl "https://ionwarp.com/api/v1/pull-requests?state=open" \
-H "Authorization: Bearer $DASH_API_KEY" \
-H "Project-ID: pr_acme"Which pull requests are still waiting on changes?data_query {
"collection": "pull_requests",
"where": {
"state": "open"
}
}{
"object": "list",
"data": [
{
"object": "pull-request",
"id": "obj_123",
"number": 630,
"title": "Collapse the review dispatch fast path",
"state": "open",
"created_at": "2026-07-25T12:00:00.000Z",
"updated_at": "2026-07-25T12:00:00.000Z"
}
],
"has_more": false,
"next_cursor": null
}Every field, filter and sort is in the
pull-requests reference, and the PR objects
reviews hang off are documented in Repositories & PRs.
What to know before you call it
One ionwarp-review task runs per pull request,
and three project settings shape it: ionwarp.review.enabled turns automatic
review on or off, ionwarp.review.guidance adds project-specific instruction to
every reviewer, and ionwarp.review.budget_usd (default 1) tells the planner
how wide a lineup to buy — a scale signal, not a ceiling.
Read per-reviewer results from reviewers_telemetry, never from the PR
comment. GET /api/v1/tasks/{task_id}/status returns one row per reviewer
carrying the model that actually ran, its grade and verdict, findings count, real
cost, and phase timings. The comment is a rendering of that table; the table is
the record. A reviewer that timed out or delivered nothing appears there as a
named row rather than as silence.
A review that cannot run says so by name on the run — read it with Debugging a Run. The project-level brakes that stop one before it starts (pause, approval window, spend cap) are on Control Plane, and PR-level skips are recorded on the pull request itself.
The review lifecycle & the GitHub check
Every review moves through ONE named lifecycle, persisted on the pull request
(review_status) and mirrored 1:1 by the status pill and the GitHub check run
IonWarp / Review:
push ──▶ TRIGGER ──▶ REVIEW GITHUB CHECK "IonWarp / Review"
(filters, planning queued
dedupe) skipped/errored ──────────▶ skipped · reason in title
reviewing in_progress
reviews_available in_progress (findings post inline)
summarizing in_progress
reviewed ─────────────────▶ P0 → action_required
P1 → neutral · clean → success
⏱ deadline (≤10m) ────────▶ neutral "still reviewing"
→ PATCHed to verdict when doneThe terminal success state is Reviewed. If the review outlives its deadline
(auto: the p75 latency for the PR's size bucket, hard cap 10 min), the check
completes neutral — "Review still running — findings will post as comments" —
and is updated to the real verdict when the review lands. A slow review never
blocks a merge.
Check conclusions
| Review outcome | GitHub check conclusion | Merge button, if you made the check required |
|---|---|---|
| P0 open | action_required | ❌ blocked — the only blocking state |
| P1 only | neutral | ✅ mergeable |
| Clean / P2 only | success | ✅ mergeable |
| Not reviewed — filtered | skipped · title: "Skipped: draft / release PR / no reviewable diff" | ✅ mergeable |
| Not reviewed — errored | skipped · title: "Skipped: review failed to run — @ionwarp review now to retry" | ✅ mergeable |
With the check not required — the default — every row is mergeable and the
conclusion is purely informational. The per-severity conclusions are workspace
settings (ionwarp.checks.p0_conclusion / p1 / p2); "not reviewed →
skipped" is fixed — a no-show must never block. Blocking = you mark the check
required in GitHub branch protection; there is no separate IonWarp "hard block"
switch. The structured fix prompt rides check output.text, so a coding agent
reads it from the Checks API without comment parsing.
Findings survive re-runs: the conclusion derives from the per-PR
open-findings ledger (the union of findings across runs, keyed by the
inline thread markers). A finding leaves "open" only by being fixed (its
anchor outdated by a push and not re-found) or dispositioned — reply
wontfix: <reason> on the finding thread (or resolve it) and the check
re-computes without a new review run.
PR commands
GitHub has no typeahead for app commands — a bare @ionwarp comment replies
with this list:
@ionwarp review off [reason]— disable automatic review for this PR@ionwarp review on— re-enable and review now@ionwarp review now— one review without changing the setting@ionwarp review force— re-review even on an unchanged head SHA@ionwarp review status— the PR's current review state@ionwarp help— the command list
Replying to a finding thread: wontfix: <reason> dismisses it (the check
re-computes), fix this opens a fix task.
Autofix contract (for coding agents)
The AGENTS.md block IonWarp installs (see the enablement PR from Settings → Coding Agents) tells your coding agent, after pushing to a PR:
wait for the "IonWarp / Review" check to finish (findings + summary) or its deadline:
finished → triage each finding with the summary as context:
helpful → reply "✔ fixing", fix it · wrong → reply "wontfix: <reason>"
then post ONE "⚡🛸 IonWarp autofix round 1" comment (✅ fixed / 🚫 wontfix
lines + a "✅ Verified:" line naming what was run) and push all fixes as
ONE fixpack commit (re-review resolves the ledger, tagged autofix)
deadline passed → proceed without IonWarp (never block on it) · max 2 roundsLet the agent do it
Reviews start themselves on every watched PR; this is how you ask for one by
hand — the same ionwarp-review task either way.
tasks_create { "project_id": "pr_acme", "skill_id": "ionwarp-review", "run": true,
"input": { "pr_number": 128 } }
tasks_status { "task_id": "ts_…" } // → per-reviewer telemetry, findings count, costNext: Reviewers & Models to choose who reviews and with which model, Repositories & PRs for the PR objects reviews hang off, Scorecards for repo-wide rubrics instead of per-PR passes.