# Copilot Review

> Create a GitHub PR with Copilot as reviewer and poll until the review arrives.

- Skill: `ultimatile/copilot-review` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add ultimatile/copilot-review`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ultimatile/copilot-review/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: ultimatile (https://skillmd.com/u/ultimatile)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/ultimatile/copilot-review

---


# Copilot Review

Create a GitHub PR, request Copilot review, poll until it arrives, and triage the results.

## How it works

**Use `pr-with-copilot-review.sh` for the entire flow.** Do NOT create the PR separately with `gh pr create` and then try to poll — the script handles PR creation, Copilot review request (`--reviewer @copilot`), and polling in one shot.

### Normal mode: create PR + review + poll

```bash
${CLAUDE_SKILL_DIR}/scripts/pr-with-copilot-review.sh --title "fix: foo" --body-file /tmp/body.md --base main
```

All arguments are forwarded to `gh-post pr create --reviewer @copilot`. The script then polls until Copilot's review arrives, outputting the review body and inline comments to stdout.

Inline `--body <string>` and `-b <string>` are rejected by `gh-post` — the wrapper exists to keep every body through its hardwrap validator. Use `--body-file <path>` (preferred) or `--body-stdin`.

### Re-review mode: after pushing fixes

After pushing fix commits to an existing PR, trigger a new Copilot review and wait:

```bash
${CLAUDE_SKILL_DIR}/scripts/pr-with-copilot-review.sh --re-review https://github.com/owner/repo/pull/123
```

This records the current review count, runs `gh pr edit --add-reviewer @copilot` to trigger a new review, then polls until a new review appears (ignoring previous ones).

### Poll-only mode: recovery for existing PRs

If the PR was already created and review already requested:

```bash
${CLAUDE_SKILL_DIR}/scripts/pr-with-copilot-review.sh --poll https://github.com/owner/repo/pull/123
```

This skips PR creation and review request, going straight to polling.

### Environment variables

- `COPILOT_POLL_INITIAL` — initial poll interval in seconds (default: 60)
- `COPILOT_POLL_MAX` — max poll interval (default: 300)
- `COPILOT_POLL_ATTEMPTS` — max attempts (default: 10)

## Triage the review

Copilot reviews see the diff and file contents but lack project-specific knowledge about design decisions, test coverage, or runtime verification.

For each finding:

1. **Cross-check** against what you already know from the current conversation — code you've read, tests you've run, decisions made with the user
2. **Classify** under the `finding-triage` SSOT dispositions, applying each per its definition there — typically `actionable`, `false-positive`, or `uncertain-validity`
3. **Present the triage** to the user — don't dump raw review output

## Respond to review

Each Copilot finding lives on an inline thread; that thread is the unit of response. Each reply names the disposition the triage step gave that finding, by its `finding-triage` slug, and states in one or two sentences the reasoning and what the run will do about the finding.

After triaging, reply within each thread via `gh-post reply-inline` — every reply body is validated (hardwrap detector + halt-before-send) and a single batch covers the full review:

```bash
# 1. Collect target threads — by default this filters to Copilot-authored heads
#    and reports per-thread state (resolved? has reply? outdated?).
#    Use --unresolved --unreplied to narrow to threads that actually need a reply.
${CLAUDE_SKILL_DIR}/scripts/list-pr-threads.sh {owner}/{repo} {number} --unresolved --unreplied

# 2. Build a JSONL file: one {"id": <head-comment-id>, "body": "<reply text>"} per line.

# 3. Send the batch. The wrapper validates every body BEFORE any send; on a body
#    failure no replies post. On a mid-batch API failure it prints un-sent indices
#    and exits non-zero.
gh-post reply-inline {owner}/{repo} {number} < /tmp/replies.jsonl
```

If `list-pr-threads.sh --unresolved --unreplied` returns zero lines: every Copilot thread is already resolved or already has a reply — do NOT post additional replies. Surface this to the user and ask before doing anything else. Stacking a duplicate "addressed in …" reply on a closed thread is the failure mode this wrapper exists to prevent.

Direct `gh api .../comments/{id}/replies -F body=...` is still possible but defeats both the body-validation guarantee and the thread-state filter — use it only for one-off cases where the JSONL ceremony is overhead, and verify thread state via `list-pr-threads.sh` first.

## Prerequisites

- `gh` CLI >= 2.88.0 (for `--reviewer @copilot` support)
- `gh-post` on `PATH` — the script routes PR creation through `gh-post pr create` so the body passes the wrapper's validator stack
- Copilot code review enabled for the repository (via GitHub plan + org/repo settings)
- Alternative: configure automatic Copilot review via Repository Rulesets (Settings > Rules)

## Combined pipeline with codex-review

```
codex review loop (pre-PR, local)
    ↓ clean
${CLAUDE_SKILL_DIR}/scripts/pr-with-copilot-review.sh (creates PR + polls for review)
    ↓ review received
Triage + respond to each inline comment via gh-post reply-inline (JSONL batch)
    ↓ if fixes needed
Push fixes
    ↓
${CLAUDE_SKILL_DIR}/scripts/pr-with-copilot-review.sh --re-review <PR_URL>
    ↓ new review received
Triage + respond again
```

