PR Babysitter
openinterpreter/openinterpreter
Watches an open GitHub pull request until it merges, handling review comments, diagnosing CI failures and retrying flaky checks along the way.
Works through every review comment on a pull request in one pass, fixes what it can, escalates human decisions and keeps CI churn to a single push.
$ npx skills add weave-os/router --skill pr-merge-ready -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install weave-os/router pr-merge-ready --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/weave-os/router.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/pr-merge-ready .claude/skills/pr-merge-ready && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "pr-merge-ready" agent skill from https://github.com/weave-os/router/tree/main/.agents/skills/pr-merge-ready into .claude/skills/pr-merge-ready/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "pr-merge-ready", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/weave-os/router/tree/main/.agents/skills/pr-merge-readyType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add weave-os/router --skill pr-merge-ready -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install weave-os/router pr-merge-ready --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/weave-os/router.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.agents/skills/pr-merge-ready .agents/skills/pr-merge-ready && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "pr-merge-ready" agent skill from https://github.com/weave-os/router/tree/main/.agents/skills/pr-merge-ready into .agents/skills/pr-merge-ready/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "pr-merge-ready", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add weave-os/router --skill pr-merge-ready -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install weave-os/router pr-merge-ready --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/weave-os/router.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.agents/skills/pr-merge-ready .cursor/skills/pr-merge-ready && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "pr-merge-ready" agent skill from https://github.com/weave-os/router/tree/main/.agents/skills/pr-merge-ready into .cursor/skills/pr-merge-ready/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "pr-merge-ready", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/weave-os/router.git --path .agents/skills/pr-merge-ready--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add weave-os/router --skill pr-merge-ready -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install weave-os/router pr-merge-ready --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/weave-os/router.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.agents/skills/pr-merge-ready .gemini/skills/pr-merge-ready && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "pr-merge-ready" agent skill from https://github.com/weave-os/router/tree/main/.agents/skills/pr-merge-ready into .gemini/skills/pr-merge-ready/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "pr-merge-ready", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install weave-os/router pr-merge-readyInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add weave-os/router --skill pr-merge-ready -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/weave-os/router.git skills-src && mkdir -p .github/skills && cp -r skills-src/.agents/skills/pr-merge-ready .github/skills/pr-merge-ready && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "pr-merge-ready" agent skill from https://github.com/weave-os/router/tree/main/.agents/skills/pr-merge-ready into .github/skills/pr-merge-ready/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "pr-merge-ready", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add weave-os/router --skill pr-merge-ready -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install weave-os/router pr-merge-ready --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/weave-os/router.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.agents/skills/pr-merge-ready .opencode/skills/pr-merge-ready && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "pr-merge-ready" agent skill from https://github.com/weave-os/router/tree/main/.agents/skills/pr-merge-ready into .opencode/skills/pr-merge-ready/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "pr-merge-ready", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
pr-merge-readyWorks through every review comment on a pull request in one pass, fixes what it can, escalates human decisions and keeps CI churn to a single push.
The skill follows a speed contract for getting a PR merge-ready: triage every open review thread before editing, apply all fixes in one pass grouped by file, validate once at the end scoped to the touched files, make one commit and one push per iteration, and handle comments before waiting on CI. Extra pushes cost several minutes of CI each and retrigger bot reviewers.
Decisions that belong to a human, such as product, architecture, scope or intent trade-offs, are escalated with options instead of guessed. The agent never posts replies or comments on PR threads; where it would decline or record a decision, it resolves the thread silently and explains the reasoning to you in chat. Some bot reviewers close their own threads after re-scanning a push, so a table lists which ones need no manual resolution. Because the repository is public, commit messages and fixtures must not contain customer names, org IDs, emails or private ticket links. The script `scripts/pr-fix-plan.sh` supports the plan.
8 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 37d585b. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Ships 1 file in scripts/ (Shell), which the agent can run.
Shell commands in SKILL.md call:
gitghmakegonpmpython3nodeFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md. Its commands use git, gh and npm, which can reach the network depending on how they are called.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
PR Merge Ready loads about 10k tokens when it runs. Until then it costs about 55 tokens; SKILL.md has 3,588 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); the scripts in this folder are not scanned.
The full file from weave-os/router at commit 37d585b, republished under its Apache-2.0 licence (© weave-os). 3,588 words, ~10,011 tokens.
.claude/skills/pr-merge-ready/SKILL.md (or your agent's skills folder). This skill also uses 1 other file; get the full folder from GitHub.It follows the same merge-ready loop (threads resolved, reviewers done, CI green) with a speed contract:
Do not auto-fix decisions that belong to a human. Product/architecture/scope/intent trade-offs are Escalate — pause, ask with options grounded in existing patterns, wait. Never guess.
This repo is public. Commit messages, code comments, and fixtures written while fixing feedback follow the root CLAUDE.md rule: no customer/org names, org IDs, emails, or private ticket/Slack links — describe triggers generically.
Never post a reply or comment on any PR review thread — no addPullRequestReviewThreadReply, no gh pr comment, no posted text of any kind. Where you would otherwise reply (declines, decided escalations), resolve the thread silently and surface the rationale to the user in chat. Resolving a thread is a status change, not a reply, and is allowed.
Not every reviewer needs a manual resolveReviewThread call. Some bots re-scan the pushed commit and close their own thread once the flagged issue is gone; calling resolveReviewThread on those threads yourself is redundant, and for weave-checks it's actively wrong — it marks a thread resolved against a code state the bot never re-verified.
| Reviewer | Auto-resolves? | Action |
|---|---|---|
cursor[bot] (Cursor Bugbot) | Yes, once it re-scans and the issue is gone | Fix: push the code change only — do not call resolveReviewThread. Decline: resolve manually (there's nothing new for the bot to re-scan). |
cubic-dev-ai[bot] (Cubic) | Yes, same as above | Same as Cursor Bugbot. |
weave-checks[bot] (body carries <!-- weave-check:<slug> -->) | Yes, same as above | Same as Cursor Bugbot. |
greptile-apps[bot] | No | Always resolve manually — Fix and Decline both. |
| Human reviewers | No | Always resolve manually — Fix and Decline both. |
| Any other/unrecognized author | Assume no | Always resolve manually. Only skip the mutation for the three named auto-resolving bots above. |
Identify the author(s) from each unresolved_comments entry's authors field (or the <!-- weave-check: --> marker in body) during triage, and carry that classification into the resolve step.
Reading a weave-checks finding against its own criteria. The marker's <slug> maps to a repo-local .weave-checks/<slug>.md if one exists, else to the default check starter-checks/<slug>.md in weave-os/checks at the release tag pinned in .github/workflows/weave-checks.yml (this repo runs with use-default-checks: true). When triaging a weave-checks[bot] thread, read that file — "Do Not Flag"/"Exclusions" is the fastest false-positive check:
REF=$(sed -nE 's#.*weave-os/checks/.github/workflows/weave-checks.yml@(v[0-9]+\.[0-9]+\.[0-9]+).*#\1#p' .github/workflows/weave-checks.yml)
gh api "repos/weave-os/checks/contents/starter-checks/<slug>.md?ref=$REF" -H 'Accept: application/vnd.github.raw'Collapsed threads and mixed authorship: pr-fix-plan collapses same-location threads into one entry with a deduplicated authors list and a separately deduplicated thread_ids list — the two are not positionally paired. Apply the auto-resolving-bot exception only when every entry in authors is one of the three auto-resolving bots. If mixed, resolve all of that entry's thread_ids manually.
gh auth statusjq 1.6+ installedgh stack extension./.claude/skills/pr-merge-ready/scripts/pr-fix-plan.sh; do not assume pr-fix-plan.sh is installed on PATHgolangci-lint is optional locally (reported as blocked by scripts/agent_checks.py when missing — CI still runs it)NEVER git add -A. Stage only files you changed.
git add <specific files>git commit -m "message"git push (in a stack: gh stack submit --auto --open)If the branch is out of date: git push --force-with-lease only after verifying you don't overwrite someone else's work. Never raw git push --force.
0. Identify or create PR; align checkout with the PR's base repository and head SHA
0b. WEAVE-CHECKS PREFLIGHT: read weave-checks criteria for the files in play
1. Fetch fresh state
2. If DONE → exit
3. If actionable threads:
a. TRIAGE ALL threads first (Fix / Decline / Skip / Escalate)
b. Surface Escalates in one AskQuestion batch; wait if they block
c. APPLY ALL Fixes in one pass (group by file)
d. Resolve all Declines (and decided Escalates) silently
e. Validate ONCE, scoped to touched files (Step 4) + re-run weave checks
f. One commit + one push
g. Go to step 1 — do NOT wait for CI yet
4. Else (zero actionable threads):
a. Foreground CI-wait with cheap comment sentinel (Step 6)
b. New comment → abort CI wait, go to Step 1
c. CI all green + DONE → exitExit only when every condition is true on the same poll, after CI has dispatched for the current head SHA:
unresolved_comments is empty, or every remaining entry is Skip, is_outdated: true, or the issue no longer applies (code_context). For any thread in the skip ledger (an auto-resolving-bot thread left open under the exception): if the bot has closed it, count DONE; if the bounded fallback has not yet fired (≥2 pr-fix-plan polls AND ≥5 min), do not count DONE and do not force-resolve — wait for the bot. Only after that window, if the thread is still open, force-resolve, surface the deviation, then count DONE. Escalate threads awaiting a user decision block DONE. Unapplied Fix nits from top_level_comments (including advisory review bodies) also block DONE. If reviews_fetch_failed or conversation_comments_fetch_failed is true, feedback is incomplete and DONE is blocked.checks_summary.pending == 0 and checks_fetch_failed is absent. If checks_fetch_failed: true, CI is unknown — do not count as done.REVIEW_REQUESTED — reviewRequests is empty.CHANGES_REQUESTED — reviewDecision is not CHANGES_REQUESTED; each latestReviews state is APPROVED, COMMENTED, or DISMISSED.Single-PR scoped. Use the user's PR number, else infer from the current branch. If the branch has no PR yet, create one before fetching review state:
git add -A.git push -u origin HEAD when it has no upstream.gh pr create --base main (do not pass --draft unless the user explicitly asks).If there are no changes to publish and no PR exists, stop and ask the user what should be reviewed.
Resolve OWNER/REPO to the PR's base repository from the current repository context. The head owner/repository identify the fork containing the code; do not pass those values to the analyzer, which queries the PR in its base repository.
read -r OWNER REPO < <(gh repo view --json owner,name --jq '[.owner.login, .name] | @tsv')
gh pr view "${PR_NUMBER:-}" --repo "$OWNER/$REPO" \
--json number,headRepositoryOwner,headRepository,headRefName,headRefOid \
-q '{number: .number, head_owner: .headRepositoryOwner.login, head_repo: .headRepository.name, branch: .headRefName, sha: .headRefOid}'If the base repository cannot be resolved from the checkout, use the base repository explicitly; never substitute the fork's head repository.
For a mid-stack PR (gh stack view --short), run gh stack checkout <headRefName> when needed, then verify git rev-parse HEAD equals the PR's headRefOid. For a non-stack PR, use the PR number and base repository so GitHub CLI can select the correct fork ref; do not check out by branch name alone:
# Stop rather than switching away from uncommitted work.
test -z "$(git status --porcelain)" || { echo "Working tree is dirty" >&2; exit 1; }
gh pr checkout "$PR_NUMBER" --repo "$OWNER/$REPO" --branch "pr-${PR_NUMBER}-${HEAD_SHA:0:12}"
test "$(git rev-parse HEAD)" = "$HEAD_SHA" || { echo "PR head SHA mismatch" >&2; exit 1; }If that unique local branch already exists at a different commit, choose another unused local name rather than resetting it. Before editing or pushing, verify the local branch tracks the PR head ref from head_owner/head_repo. If the checkout or upstream cannot be verified safely, stop and ask. Fixes always go on the PR's own head branch.
Read the weave-checks criteria (repo-local .weave-checks/*.md, else the pinned starter-checks/*.md — see "Reading a weave-checks finding" above) that apply to the files this PR touches, plus the CLAUDE.md guide of each touched package. Reviewer asks that contradict those guides (layering, magic provider strings, logging keys, tautological tests) are Decline or Escalate, not Fix.
Re-fetch every iteration. Never reuse stale data. Do not hand-roll GraphQL for review threads.
./.claude/skills/pr-merge-ready/scripts/pr-fix-plan.sh "$PR_NUMBER" --owner "$OWNER" --repo "$REPO" --max-comments 0 --json
gh pr view "$PR_NUMBER" --json headRefOid,reviewDecision,reviewRequests,latestReviews--max-comments 0 = no display cap. pr-fix-plan paginates all review threads (fails loudly past 20 pages / 2,000 threads). GitHub orders threads oldest-first and resolved threads never leave the connection — a single-page first:100 fetch goes permanently blind past 100 total threads.
pr-fix-plan --json returns:
head_branch, head_sha, and is_checked_out — is_checked_out means the local HEAD SHA equals the PR head SHA; if false, use Step 0's safe checkout rather than checking out by branch name alonechanged_files — do NOT re-derive via git diffunresolved_comments — unresolved, collapsed. Each has file, line, start_line, authors, body, urls, thread_ids, is_outdated, collapsed_count, code_context, related_lines_in_pr, thread_commentstop_level_comments — omitted when emptypattern_summarychecks / checks_summary — if checks_fetch_failed: true, the empty list is NOT trustworthyreviews_fetch_failed / conversation_comments_fetch_failed — if true, feedback is incomplete and DONE is blockedSkip is_outdated: true entries when counting actionable work. Also skip any whose code_context shows the issue is already addressed.
top_level_comments are not line-anchored. Triage them the same Fix/Decline/Escalate/Skip way, but they have no thread_ids to GraphQL-resolve — Fix by editing, Decline/Skip by noting in chat only.
Advisory review bodies are nitlists. Bots such as workweave-bot post COMMENTED reviews with zero threads and a body listing several nits across files; these arrive as top_level_comments entries with source: "review". Split each body into one item per nit and triage each individually — an empty unresolved_comments does not mean "no feedback". Track which nits you've applied across iterations (by review URL + nit) so an already-applied body isn't re-triaged; it stays on the PR forever.
Comment-first: any non-Skip entry whose issue still applies → Steps 2–5 immediately. Do not wait for CI, even if checks are running or failed.
CI-wait: only when zero actionable comments → Step 6.
Walk every actionable unresolved_comments entry before any edit. Produce a batch plan:
Fix : [thread ids / files]
Decline : [thread ids + one-line rationale]
Escalate: [thread ids]
Skip : [thread ids]Announce this list to the user, then execute. Do not drip-triage (classify one, edit, classify next).
| Category | Type | Action |
|---|---|---|
| Fix | Bug, security, style, clear refactor, nit | Implement in the Step 3 pass |
| Decline | False positive, already handled, would make code worse, out of scope | No code change; resolve silently; note rationale in chat |
| Escalate | Genuine human decision | Do not change code. Ask (Step 3.5) |
| Skip | Pure questions / discussion | Leave unresolved |
Fix vs Escalate. Fix = one objectively-correct resolution matching existing patterns. Escalate signals:
When in genuine doubt between Fix and Escalate, Escalate. (Low-stakes mechanical nits still default to Fix.)
Bot suggestion blocks are authoritative. When a bot (workweave-bot, greptile-apps[bot], cubic-dev-ai[bot], …) offers a fenced ```suggestion replacement — especially comment-length / doc-brevity nits — it is a Fix: apply the replacement verbatim. Don't paraphrase or skip because the existing wording seems fine. Only decline if applying it would break the build or contradict a package CLAUDE.md rule. Free-form bot feedback without a suggestion block is triaged like any other comment.
Do not validate, commit, or push inside this step.
code_context and related_lines_in_pr instead of re-reading the diff / grepping. Open the file only for surrounding context.related_lines_in_pr site (fix the pattern once, everywhere it appears in the PR).Guidelines: only the changes requested; no unrelated refactors.
No code change, no PR reply. Resolve in Step 5. Collect one concise chat line each: what was asked, why you're declining (1–3 sentences, specific).
Leave alone.
Never change code or resolve on your own.
AskQuestion batch.Do not auto-resolve an escalated thread. Do not let it slip through as a Fix because asking felt slower.
Run this after every Fix in this iteration is applied, and never per comment. Skip the whole step if this iteration was all-Decline (no files changed).
Scope to this iteration's uncommitted edits.
CHANGED_FILES=()
while IFS= read -r -d '' file; do
[[ -f $file ]] && CHANGED_FILES+=("$file")
done < <({
git diff --name-only -z
git diff --cached --name-only -z
git ls-files --others --exclude-standard -z
})Format only existing Go paths from this iteration's CHANGED_FILES; do not run a repository-wide write formatter here.
GO_FILES=()
for f in "${CHANGED_FILES[@]}"; do [[ $f == *.go ]] && GO_FILES+=("$f"); done
((${#GO_FILES[@]})) && gofmt -w -- "${GO_FILES[@]}"Generated files must land in the same commit, or CI fails on drift. Never hand-edit internal/sqlc/ or AGENTS.md.
| Touched | Run |
|---|---|
db/queries/, db/migrations/ | make generate |
internal/router/policy/inference_registry.go | make generate-inference-policy |
internal/router/catalog/ model/pricing data | make generate-statusline |
Any CLAUDE.md | make generate-agent-guides |
install/directives.tsv, install/registry.sh | make embed-registry |
# Tests for the touched Go packages only (whole-module `go test ./...` is CI's job)
if ((${#GO_FILES[@]})); then
GO_PKGS=($(for f in "${GO_FILES[@]}"; do echo "./$(dirname "$f")"; done | sort -u))
go vet "${GO_PKGS[@]}" && go test -count=1 "${GO_PKGS[@]}"
fi
# Request-path / dispatch / policy / provider edits:
make inference-boundary
# Discover any other suites the touched paths select (docs, lint, install, frontend, smoke):
python3 scripts/agent_checks.py plan --paths "${CHANGED_FILES[@]}"Run the non-Go suites the planner selects that are cheap and local (docs, install, lint if golangci-lint is installed; cd frontend && npm run typecheck && npm run lint for frontend/). Suites the planner marks integration: true (smoke, database) are left to CI — say so in chat; never report them as passed.
Push before resolving human/Greptile Fix threads. Resolving first and then failing the push leaves threads closed against code that never landed. Declines have no code change, so they can resolve immediately.
Resolve all Decline threads now. After a successful push, resolve Fix threads and decided Escalates — except the auto-resolving-bot exception below. Use each entry's thread_ids (collapsed comments may list more than one — resolve every id).
Auto-resolving-bot exception (Fix only): for a Fix whose authors are entirely cursor[bot], cubic-dev-ai[bot], or weave-checks[bot], do not call resolveReviewThread — push and let the bot close it. Declines on those same threads still need a manual resolve. Greptile and humans always get a manual resolve.
Bounded fallback: carry a skip ledger (thread_id, first_seen_unresolved_at). On every full pr-fix-plan poll inside Step 6 (~2 min cadence, not the cheap sentinel), if a skipped bot thread has been open across ≥2 full pr-fix-plan polls AND ≥5 min since first seen, force-resolve it and tell the user the bot never closed it. Fire this from Step 6 too — Step 5 may never run again during a CI wait.
gh api graphql -f query='
mutation($threadId: ID!) {
resolveReviewThread(input: {threadId: $threadId}) { thread { isResolved } }
}
' -f threadId='{THREAD_ID}'Then one commit and one push (skip if no files changed):
git add path/to/fixed1.go path/to/fixed2.tsx
git commit -m "fix: address PR review feedback (iteration N)
Fixed:
- [summary of fix 1]
- [summary of fix 2]
Declined (with explanation):
- [summary of declined 1]"
git push # stack: gh stack submit --auto --open
PUSHED_HEAD_SHA=$(git rev-parse HEAD)
# now resolve human/Greptile Fix thread_ids (Declines already resolved above)If push is rejected as out of date: gh stack sync (or git fetch origin && git rebase origin/main) then git push --force-with-lease. Never raw --force. Do not resolve Fix threads until the push succeeds.
After push: enter the Step 6 watcher. Do not run an in-band pr-fix-plan. Auto-reviewers file new threads on the new commit asynchronously — "zero threads right before push" is meaningless for the new SHA.
Skipped bot Fix threads may still show isResolved: false until the bot re-scans. If code_context shows the issue is gone, treat as non-actionable — but not merge-ready until the bot closes it or the bounded fallback fires.
Enter only when Step 1 found zero actionable threads. The instant the sentinel detects a new unresolved thread, abort and return to Step 1.
A naive gh pr checks right after push reports pending=0 because GitHub has not dispatched yet. Anchor on the head SHA; validate dispatch before treating CI as done.
Do not run pr-fix-plan every 15s (~2s/call). Poll a cheap GraphQL sentinel; full pr-fix-plan only when something changed.
This pins the session. That is the point: responsiveness.
CAPTURED_UNRESOLVED_COUNT / CAPTURED_THREAD_TOTAL are taken from one sentinel read at CI-wait entry. Baseline and every poll must count the same population — every non-outdated unresolved thread, including Skip and pending auto-resolving-bot threads. Do not derive the baseline from pr-fix-plan's actionable count (that excludes those, so every poll looks like "new thread").
The sentinel MUST read the newest page (last:100, never first:100). GitHub orders oldest-first and resolved threads stay forever, so first:100 reports unresolved=0 on busy PRs while new comments sit at the tail.
gh api graphql -f query='
query($owner: String!, $repo: String!, $pr: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $pr) {
headRefOid
reviewThreads(last: 100) {
totalCount
edges {
node {
isOutdated
isResolved
comments(last: 1) { edges { node { updatedAt } } }
}
}
}
reviews(last: 10, states: [COMMENTED, APPROVED, CHANGES_REQUESTED]) {
edges { node { updatedAt } }
}
comments(last: 1) { edges { node { updatedAt } } }
}
}
}
' -f owner="$OWNER" -f repo="$REPO" -F pr="$PR_NUMBER" --jq '
.data.repository.pullRequest | {
sha: .headRefOid,
thread_total: .reviewThreads.totalCount,
unresolved: [.reviewThreads.edges[].node | select(.isResolved == false and .isOutdated == false)] | length,
latest_thread_timestamp: ([.reviewThreads.edges[].node.comments.edges[].node.updatedAt] | sort | last),
latest_review_timestamp: ([.reviews.edges[].node.updatedAt] | sort | last),
latest_conversation_comment_timestamp: ([.comments.edges[].node.updatedAt] | sort | last)
}
'thread_total is the arrival detector (monotonic — resolved threads never leave). unresolved is the state detector. Compare both to the entry baseline. unresolved alone misses a swap (new thread filed in the same interval an old one closed). A reply on an existing unresolved thread changes neither count — detect it via latest_thread_timestamp. The separate latest_review_timestamp and latest_conversation_comment_timestamp detect new top-level review bodies and conversation comments even when no thread is unresolved.
captured_unresolved_count, captured_thread_total = one sentinel read at entry
last_seen_sha = HEAD_SHA
last_seen_thread_timestamp, last_seen_review_timestamp, last_seen_conversation_comment_timestamp = that same read
loop:
sleep 15–30s
run sentinel
if sentinel.sha != last_seen_sha:
# Someone else pushed. Watcher is stale.
break → Step 1
if sentinel.thread_total > captured_thread_total:
break → Step 1 # new thread (catches the swap case)
if sentinel.unresolved > captured_unresolved_count:
break → Step 1
if sentinel.latest_thread_timestamp > last_seen_thread_timestamp:
break → Step 1 # reply on an existing unresolved thread
if sentinel.latest_review_timestamp > last_seen_review_timestamp:
break → Step 1
if sentinel.latest_conversation_comment_timestamp > last_seen_conversation_comment_timestamp:
break → Step 1
# Dispatch check ONCE after the first sentinel cycle, not every tick
if not yet validated dispatch:
check_run_count = gh api "repos/$OWNER/$REPO/commits/$HEAD_SHA/check-runs" --jq '.total_count'
if check_run_count == 0: continue # no runs dispatched yet; don't false-DONE
# Full CI check every ~5 sentinel cycles (~2 min), not every tick
if cycles_since_last_ci_check >= 5:
./.claude/skills/pr-merge-ready/scripts/pr-fix-plan.sh "$PR_NUMBER" --owner "$OWNER" --repo "$REPO" --max-comments 0 --json
also run the bounded bot-thread fallback against the skip ledger (force-resolve if ≥2 polls AND ≥5 min)
if checks_summary.failed > 0:
# Do not wait for the rest of the matrix. Pull logs, fix, push.
break → Step 1 with failing checks as extra Fix items
if checks all concluded and pending == 0:
fetch reviewer state; if DONE (skip-ledger threads only after bot close or post-window force-resolve) → Step 7
if actionable comments → Step 1
if elapsed_watch_minutes > 45: surface to user, stopUse Bash Monitor or a while/sleep loop — foreground, not scheduled wake-ups. If you genuinely must yield (/loop pacing, user asked to unpin, wait outlives 45 min), follow "If you must yield" — never schedule a wake-up whose prompt re-invokes /pr-merge-ready.
/pr-merge-ready)Re-invoking the skill re-injects this whole file on every wake-up. The skill is already in context; a wake-up only needs loop state.
Resume the pr-merge-ready loop. The skill is already in context — do NOT re-invoke /pr-merge-ready.
PR #<num> <owner>/<repo>, branch <headRefName>, head SHA <sha>
Phase: <ci-wait | fixing threads | blocked-on-escalation> · Iteration <n>/5
Sentinel baseline (captured unresolved / captured total): <n> / <m>
Pending escalations: <one line each, or "none">
Declines to surface at exit: <count>
Last CI state: <pass/fail/pending counts, or "not yet dispatched for this SHA">
On wake: run the Step 1 fetch (`./.claude/skills/pr-merge-ready/scripts/pr-fix-plan.sh <num> --owner <owner> --repo <repo> --max-comments 0 --json`; `gh pr view <num> --json headRefOid,reviewDecision,reviewRequests,latestReviews`), re-evaluate DONE, then comments-first.
If the skill body is no longer in context (compacted away), Read .claude/skills/pr-merge-ready/SKILL.md once before acting.Read failing check logs for any bucket: fail rows and feed them into the next iteration as issues to fix:
gh run view <run-id> --log-failed 2>&1 | grep -E 'error|Error|##\[error\]|FAIL' | head -40Re-run pr-fix-plan once more (auto-reviewers may have posted at the end of CI).
Go back to Step 1.
When Step 1's DONE check passes against the latest head SHA:
PR #<num> ready to merge:
- Resolved threads this run: <count>
- Declined threads this run: <count>
- Escalated → user-decided this run: <count>
- CI checks: all green (<count> checks)
- Reviewers: <list>If after 5 full iterations (5 push cycles) the loop hasn't terminated, stop and surface the blocker. Don't loop forever.
| Anti-pattern | Why it bit us | Instead |
|---|---|---|
Running make check / go test ./... on every fix iteration | Whole-module tests + codegen for a few changed lines | Step 4: touched packages + planner-selected suites; CI runs the full matrix |
| Fix comment 1 → validate → commit → fix comment 2 → validate… | Multiplies local typecheck/test cost by N comments; N CI cycles (~6–7 min each) and N bot re-reviews | Triage all → edit all → validate once → one push |
Scoping $CHANGED_FILES to origin/main...HEAD | Pays lint/typecheck/tests for the whole PR on a one-line review fix | Uncommitted git diff + cached + untracked |
| Resolving Fix threads, then a push that fails | Threads closed against code that never landed | Push first, resolve human/Greptile Fixes after |
| Waiting out the full CI matrix after the first red check | Burns 5–10 min for jobs that cannot save a failed run | Abort CI wait on failed > 0, fix, re-push |
Invoking make precommit / make check from this skill | Those run the whole module matrix; meant for shipping a feature | Step 4 scoped checks |
Treating a bot COMMENTED review as "no feedback" because it has no threads | Missed a whole workweave-bot nitlist living in the review body | Split top_level_comments review bodies into one item per nit |
Rephrasing a bot's suggestion block instead of applying it | Lost the brevity the bot was asking for; subjective drift | Apply the suggested replacement verbatim |
| Committing a generator-input change without its regenerated output | CI drift check fails (internal/sqlc, AGENTS.md, policy docs) | Step 4 regenerate table |
| Falling through to another PR review workflow | This skill is the full workflow and validates once per batch | This file is the full workflow |
| Waiting for CI before fixing review comments | Reviewers blocked while the agent watches checks | Comments first |
| Sitting in CI wait without polling for new threads | Auto-reviewers post during CI | Sentinel every 15–30s |
| Polling CI immediately after push | pending=0 before dispatch → false DONE | Anchor on commits/$HEAD_SHA/check-runs count |
| Treating "0 threads at push time" as forever | Bots post minutes later | Sentinel + re-fetch after CI |
| Conflating PRs in a stack | Comments on PR #2 fixed on PR #1's branch | Verify base repo, head repo, and checked-out head SHA each iteration |
| Auto-fixing a product/architecture/scope decision | Shipped an opinionated change the author didn't want | Escalate |
| Asking with no research or options | Forces the human to do the legwork | Investigate, then 2–4 grounded options + recommendation |
Running pr-fix-plan on every sentinel tick | Wastes ~2s/tick | Cheap GraphQL sentinel; full fetch on fire or ~2 min |
Passing /pr-merge-ready as the ScheduleWakeup prompt | Re-injects the whole skill per wake-up | ~1KB state-carrying resume prompt |
| Manually resolving a Cursor/Cubic/weave-checks Fix right after push | weave-checks re-files the same nit | Skip resolve; let the bot re-scan |
| Skipping resolve on a Decline just because the bot auto-resolves Fixes | Nothing new to re-scan → thread sits open | Always resolve Declines yourself |
| Applying the bot exception to a collapsed entry that mixes a bot with a human | authors and thread_ids aren't paired — you'd strand the human thread | Only skip when authors is entirely auto-resolving bots |
| Declaring merge-ready while a skipped bot thread is still open | Bot never re-scanned | Wait the 2-poll / 5 min window; only then force-resolve and count DONE |
Sentinel comments(first: 1) | Replies on an open thread never bump counts; oldest timestamp stays put | comments(last: 1) + compare latest_thread_timestamp |
| Issue | Solution |
|---|---|
| Comment references deleted line | Check git history, apply to current location |
| File was renamed | Find new path, apply there |
| Conflicting comments | Address most recent; note the conflict in chat |
| Fix breaks scoped tests | Revert that fix, try an alternative; don't re-run the whole suite |
CI check stuck IN_PROGRESS >30min | Surface to user, stop the watcher |
| Reviewer keeps re-requesting the same point | After 2 declines on the same thread, surface to user |
| Push rejected (rebase / merged dependency) | gh stack sync or rebase onto origin/main → --force-with-lease |
pr-fix-plan (+ gh pr view for reviewer state) at the start of every iteration.© weave-os, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 1 other file (scripts) in .agents/skills/pr-merge-ready of weave-os/router.
Open the folder on GitHubat commit 37d585b
PR Merge Ready next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| PR Merge Ready this skillweave-os/router | 5.6k | — | ~10k | Automated safety check: Pass | Apache-2.0 | |
| PR Babysitteropeninterpreter/openinterpreter | 69k | 3 repos | ~4.2k | Automated safety check: Pass | Apache-2.0 | |
| WooCommerce Code Reviewwoocommerce/woocommerce | 11k | 3 repos | ~1.1k | Automated safety check: Pass | Custom licence | |
| Open Code Review CLIalibaba/open-code-review | 46k | — | ~3.1k | Automated safety check: Pass | Apache-2.0 | |
| GitHub Review Iterationprisma/orm | 48k | — | ~2.2k | Automated safety check: Pass | Apache-2.0 | |
| Understand Diff AnalysisEgonex-AI/Understand-Anything | 86k | — | ~1.4k | Automated safety check: Pass | MIT |
openinterpreter/openinterpreter
Watches an open GitHub pull request until it merges, handling review comments, diagnosing CI failures and retrying flaky checks along the way.
woocommerce/woocommerce
Reviews WooCommerce code changes against the project's standards, flagging backend PHP architecture, naming, documentation, data integrity and testing violations.
alibaba/open-code-review
Runs the ocr command-line tool to review Git changes, a commit or a branch comparison with an AI model, returning line-level comments and optionally applying fixes.
prisma/orm
Runs a loop on a GitHub pull request: fetch review state, triage comments into actions, implement them and resolve threads, repeating until nothing actionable is left.
Egonex-AI/Understand-Anything
Reads your git changes or a pull request against a prebuilt knowledge graph of the project to explain what changed, which components are affected and what is risky.
flutter/flutter
Performs a comprehensive, multi-step code review of pull requests or local code changes, using iterative refinement (generation, critique, synthesis) to ensure high-quality, actionable feedback.
weave-os/router
Stands up the Weave model router in Docker Compose and drives it with claude -p against a real or mocked upstream to reproduce and verify routing and streaming behavior.
weave-os/router
Local test harness for the Weave router: a docker compose stack plus codex exec runs that confirm how Codex requests are routed, translated and marked.
weave-os/router
Correlates a Claude Code session's local transcript with a model router's production cloud logs to explain why a specific response rendered the way it did.
weave-os/router
Installs a language server such as gopls, typescript-language-server, pyright or rust-analyzer and, with explicit confirmation, its underlying toolchain so the lsp tool can use it.
weave-os/router
Correlates a Codex CLI session's local transcript with a model router's production logs to explain why a reply rendered the way it did.
weave-os/router
Shows when to answer a code question through a real language server instead of grep, covering definitions, references, hover, outlines, and errors.
Categories
Works through every review comment on a pull request in one pass, fixes what it can, escalates human decisions and keeps CI churn to a single push. The skill follows a speed contract for getting a PR merge-ready: triage every open review thread before editing, apply all fixes in one pass grouped by file, validate once at the end scoped to the touched files, make one commit and one push per iteration, and handle comments before waiting on CI. Extra pushes cost several minutes of CI each and retrigger bot reviewers.
PR Merge Ready fits situations like: addressing all review feedback on a PR quickly; creating a PR and watching it until reviewers are satisfied and CI passes; separating review comments that need a human decision from mechanical fixes.
Run `npx skills add weave-os/router --skill pr-merge-ready -a claude-code`. Or copy the skill folder (.agents/skills/pr-merge-ready in weave-os/router) into .claude/skills/pr-merge-ready in your project. Claude Code loads it when a task matches its description.
Run `npx skills add weave-os/router --skill pr-merge-ready -a codex`. Or copy the skill folder (.agents/skills/pr-merge-ready in weave-os/router) into .agents/skills/pr-merge-ready in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add weave-os/router --skill pr-merge-ready -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/pr-merge-ready, .gemini/skills/pr-merge-ready, .github/skills/pr-merge-ready and .opencode/skills/pr-merge-ready in your project.
Going by SKILL.md and its folder, PR Merge Ready needs a shell for the scripts in its folder and the command-line tools its instructions call (git, gh, make, go, npm and python3). Our summary lists: The GitHub CLI, authenticated for the repository.
SKILL.md contains no URLs. Its commands use git, gh and npm, which can reach the network depending on how they are called. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. The check reads SKILL.md only: the scripts in the folder are not scanned, so read them before running anything.
PR Merge Ready is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 10k tokens (SKILL.md is roughly 40k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with PR Merge Ready: PR Babysitter (openinterpreter/openinterpreter, 69k stars), WooCommerce Code Review (woocommerce/woocommerce, 11k stars), Open Code Review CLI (alibaba/open-code-review, 46k stars) and GitHub Review Iteration (prisma/orm, 48k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
weave-os (a GitHub organization) maintains it in weave-os/router, which has 5,578 GitHub stars. The repository holds 19 skills in this directory. The repository was last updated on October 10, 2026.
Source: weave-os/router on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.