Files
improvise/.agents/skills/pr-watch/SKILL.md
T
Cursor Agent ec30c8a7de fix: address three Codex P2 review findings
1. Shebang (Codex P2): scripts/agent-bootstrap.sh and .claude/hooks/session-start.sh
   now use #!/usr/bin/env bash instead of #!/bin/bash, matching repo convention
   enforced by checkers.sh.

2. Fail-closed bd pin (Codex P2): .claude/hooks/session-start.sh local path now
   exits 2 with a warning when pinned or running version cannot be parsed,
   instead of silently succeeding. An unparseable version is not a pass.

3. pr-reviews failure (Codex P2): scripts/pr-watch no longer uses '|| true' to
   swallow pr-reviews failures. If pr-reviews fails (fetch error, malformed
   response), pr-watch now exits 6 with a warning. Empty successful fetch
   (no reviews) is still exit 0.

   - Added exit 6 to usage text and skill documentation
   - Added test case 'pr-reviews failure exits 6' using malformed JSON fixture
   - All 57 pr-watch tests pass

Co-authored-by: Ed Langley <fiddlerwoaroof@users.noreply.github.com>
2026-09-26 01:30:00 +00:00

5.1 KiB

name, description
name description
pr-watch Wait for a GitHub PR's checks to finish before merging or reporting on it. Use whenever you need to know whether a PR is green. Do not hand-roll a polling loop over gh pr view.

Waiting on a PR

"Watching a PR" means tracking both CI checks and GitHub comments/reviews. A PR is not ready to merge just because checks are green — review feedback must be read and answered first.

CI Checks

scripts/pr-watch <pr-number> [--expect <sha>] [--timeout <sec>] [--interval <sec>] [--reviews]
exit 0  every check COMPLETED, none failed, MERGEABLE with a state of
        CLEAN, UNSTABLE or HAS_HOOKS, and reviews fetched (if --reviews)
exit 1  a check failed (under any mergeability, BLOCKED included), or the
        PR is not mergeable (CONFLICTING, DIRTY, BEHIND, DRAFT, or a state
        it does not know)
exit 2  the head moved during the wait, or is not --expect
exit 3  timed out with checks still running, mergeability still UNKNOWN,
        or still BLOCKED with every registered check green
exit 4  usage or environment error
exit 5  the PR is MERGED or CLOSED, at the head the watch pinned; it ends on
        the first poll that sees it closed, which may be mid-wait
exit 6  checks green/mergeable, but pr-reviews failed (fetch error, not "no reviews")

--reviews (default): after checks complete, print all reviews and comments via scripts/pr-reviews. Use --no-reviews to skip this. The reviews are shown but do not change the exit code — answering them is your responsibility.

Exit 0 now means mergeable, not only green. GitHub computes mergeability lazily and reports UNKNOWN for a while after the base moves. It also reports BLOCKED for the moment before a required check that needs: another job has registered. The watch waits out both, so you do not need to re-check mergeable by hand after it. Exit 3 is not a pass. It may be a required check that never ran. Why each state counts the way it does: context/code/tooling.md, "pr-watch and mergeability" (improvise-edpl).

Pass --expect <sha> whenever you know which commit you are asking about. It is the difference between "this PR is green" and "the thing I pushed is green", and a PR here was once merged at a head that predated its own fix.

Reviews and Comments

Reviews are a separate API surface from the comment thread. Codex/ChatGPT reviews arrive as reviews, not comments — checking only the comment thread misses them entirely. pr-watch --reviews (the default) shows all three:

Surface What it is
[REVIEW] The review's own body (pulls/{n}/reviews)
[INLINE] A comment on a line of the diff (pulls/{n}/comments)
[COMMENT] A comment on the PR thread (issues/{n}/comments)

To fetch reviews directly: ./scripts/pr-reviews <n> — do not pipe it. The script's $? must be read directly; piping or chaining loses the exit code. Redirect to a file if you need to process it.

Review Obligations

Before treating a PR as done or merging:

  1. Read all review feedback — reviews against an earlier head may still apply
  2. Answer substantive feedback with attribution — every reply must include the attribution line (see below)
  3. Re-check after each push that new reviews target the current head SHA
  4. Don't wait indefinitely on Copilot/Codex — reviews arrive as quota allows; an absent review and an unrequested one look identical. The adversarial review that gates a merge is dispatched by the orchestrator; external reviews are read in addition, not instead.

GitHub Attribution (Hard Rule)

Every GitHub write must include an attribution line. Posts go out under the repository owner's account — an unattributed post reads as the human's words.

Open the body with: _Posted by <Agent Stack> (<model>)_

Examples:

  • _Posted by Claude (Opus 4.5)_
  • _Posted by Cursor Agent (claude-sonnet-5)_

A thread is not "answered" until the reply carries this attribution.

See AGENTS.md "GitHub Attribution" for the full rule and "Shipping a wave" for Copilot request mechanics and how to answer declined findings.

Do not write the loop yourself

Four signals look like "finished" and are not:

  • "nothing pending" — also true when no check has registered yet. Require a non-zero total as well.
  • conclusion == null — conclusion is a string, empty while running. Test status == "COMPLETED".
  • a verdict from before the head moved — re-check the head, or --expect.
  • mergeable: UNKNOWN — GitHub has not computed it yet. It is not a pass, and PRs #172 and #179 both went green on it.

gh is shell-aliased to a different tool in these shells; pr-watch locates a real one itself. Do not reintroduce a bare gh call in a script here.

Before deciding a PR is stuck

Check suites can take several minutes to appear at all, and a Copilot review takes about six. Compare date -u against the GitHub timeline rather than a felt interval, and do not close and reopen a PR to unstick it.

scripts/pr-watch-test.sh drives every case from fixtures.