用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/Morrison-Lab/ai-config --skill ardi命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
正在显示 SKILL.md
| name | ardi |
| description | Drive one PR to clean. |
| user-invocable | true |
| allowed-tools | ["Bash","Agent","Read","Edit","Write"] |
Drive one PR/MR to a clean review verdict by looping: read every review → ARD every
finding from every reviewer → push → post summary → re-request review from those
reviewers → repeat until every reviewer's latest verdict is clean.
A later all-clear from one reviewer does not clear another reviewer's standing
not-clean, even with mwc active (ai-config#2274).
COMMENT_PR) so a parallel @claude CI run or another person doesn't start a colliding session.
The body carries a real blank line before the disclosure marker --- \n inside a bash double-quoted string is the two characters, not a newline:gh pr comment <N> --body "Driving this PR to clean --- please hold off until done.
_Posted by Claude Code (AI agent) --- not written by a human._" # COMMENT_PR
Skip if your most recent comment already says so and is still live --- claims expire 2 hours after the most recent push or comment, and an expired one needs reasserting, per claim-pr.
(COMMENT_PR and the other bracketed tokens below are abstract operation tokens --- resolve to your model's tool via tool-mappings.md.)
Read every review, not only the latest.
Pull the most recent reviewer comment --- the @claude bot's, or a human's ---
and every other review that still has a standing verdict.
Don't trust earlier cached verdicts --- actively poll until a review appears that references the commit you just pushed, then read that one.
If one review is all-clear and another raises findings or nits, the findings
win: ARD the union, then request fresh reviews.
Do not merge on the all-clear, even with mwc (ai-config#2274).
gh pr checks (PR_CHECKS) / glab ci list going green is about CI state, not the review verdict --- always parse the latest review body for findings.
A user question about this PR that is not the word "status" still requires
this fetch (see pr-status).
Don't answer the chat question from session memory while a review comment
sits unread.
When the user provides a specific review link/ID (e.g. #pullrequestreview-4761444085): Fetch that review directly via the GitHub API using its ID.
Many bot reviews have a generic overview body but the actual findings live in inline comments on specific lines --- don't rely on the top-level review body alone.
Fetch both the review overview and its inline comments:
gh api "repos/<owner>/<repo>/pulls/<N>/reviews/<review-id>" --jq '{state, body}'
gh api "repos/<owner>/<repo>/pulls/<N>/comments" --paginate --jq '.[] | select(.pull_request_review_id == <review-id>) | {line: (.line // .original_line), body}'
The comments endpoint returns pages oldest-first -- without --paginate
a later review's inline comments can sit past the first page and never
reach the filter, making a review with real findings look empty.
GitHub:
Filter on the body marker, not on an author login.
Do not take | last as the only review to ARD
(ai-config#2274).
Claude, Antigravity, and skip notices can all post as github-actions[bot],
so a login filter silently drops a standing not-clean.
gh api repos/<owner>/<repo>/issues/<N>/comments --paginate \
| jq -s '[.[][] | select(.body | test("\\*\\*Claude finished|### Verdict|Antigravity Agent Report"; "i"))] | .[] | {created: .created_at, user: .user.login, body: .body}' # READ_PR_COMMENTS
Completed Claude runs start the body with **Claude finished.
Read every matching comment, not only the newest.
A later all-clear from a different reviewer does not clear another
reviewer's standing not-clean.
For a reviewer (any login), also gather comments with the
skill's step 1 ( plus the inline-thread API).
Pause point: before advancing to the next round.
Do-Confirm; per
shared/workflow/skill-checklists.md.
started_at against your last reply and the
head SHA.If the PR's CI checks are failing (not just the review), investigate and fix them as part of the ARDI loop --- don't declare "clean" with red CI. This includes:
sup skill, then either pin a working version or apply a local
workaround until the upstream fix lands.The goal is green CI + clean review, not just clean review.
Some steps benefit from a subagent rather than blocking the round on the main
thread --- investigating a CI failure whose cause isn't obvious (see above),
verifying a reviewer's factual claim before Addressing/Rebutting it, or
checking a sibling PR for a merge conflict during the opportunistic sweep.
Delegate that via the Agent tool and keep driving the round itself (ARD,
push, post summary, re-request review) on the main thread.
For a judgment-heavy sidecar task (a subtle root-cause hunt, adjudicating a deadlocked rebuttal before escalating to a human), give the subagent a stronger model via the Agent tool's model parameter (e.g. model: 'opus').
Symmetrically, drop to a cheaper/faster tier (model: 'fable' or 'haiku') for a mechanical sidecar task --- see select-model's decision tree for both directions.
For a heavy fan-out investigation/verification pass, prefer a separately-billed provider (e.g. the codex CLI) first when available --- see delegate-to-codex.
The loop ends only at fully clean, which means both:
shared/workflow/fully-clean.md for the check-run-vs-workflow-run and
API-casing gotchas).mwc (ai-config#2274).
That review must be a genuine posted verdict at the current head, from an external reviewer if one is reachable -- check availability again right before declaring clean, not just at the round where self-review first started; an inferred "probably clean" from green CI and resolved threads does not satisfy this.Threads: at fully-clean, every inline review thread is resolved, and
the only conversation left open is the final all-clear exchange --- the
reviewer's all-clear comment (usually a top-level PR comment, not an inline
thread) and your reply to it. (Thread mechanics live in the ard skill, step
4b.)
Pause point: before declaring "clean" or reporting the PR ready.
Do-Confirm; per
shared/workflow/skill-checklists.md.
python3 scripts/check-pr-fully-clean.py --quorum <number-of-reachable-providers> <pr-number> returned exit code 0 (confirming all CI check runs completed with success AND clean review comments for current HEAD SHA have been posted).status before conclusion.adversarial-reviewer subagent rather than inline, and its findings were dispositioned (adversarial-self-review).There is no round limit. Always request another review. The loop on a single PR ends on exactly three things:
Nothing else. Not a round count, not a sense that findings are getting smaller, not a judgment that the reviewer is nitpicking.
Deadlock is per-item, and it does not stop the loop.
If you and the reviewer can't reach consensus on one finding (your rebuttal didn't convince them, and their re-raise didn't convince you), escalate that item to a human reviewer rather than looping on it or unilaterally overriding.
Request the repository owner via the request-pr-review skill, @-mention them in a comment summarizing the impasse, and surface the open item to the user.
The raw gh pr edit <N> --add-reviewer <reviewer> form bypasses that skill and so does not inherit its Lacaedemon/sparta exception.
In sparta, escalate to the user in chat rather than requesting a reviewer at all.
Then keep driving the PR: address every other finding, push, and request the next review.
Only when every remaining item is an escalated deadlock does condition 2 above fire, and even then the loop resumes the moment the human rules.
ardia and gia drive many PRs.
When one of those is waiting on a human --- a deadlocked item, a blocked dependency, an unresolvable conflict --- the sweep records it and moves to the next PR so the batch keeps moving.
That is scheduling, not a stopping condition for the loop: the sweep returns when the human rules, and nothing about it licenses accepting unaddressed findings on the PR itself.
This skill used to carry a guard saying that after 3-4 rounds of new nits you should surface the pattern and ask whether to continue. That guard is removed, and reasoning of that shape must not be reintroduced. It fails three ways:
address-every-comment already forbids for individual findings.
The guard reintroduced at loop scale the thing that fragment bans at item scale.The tell is any sentence of the form "the reviewer keeps finding things, so maybe we should stop." Replace it with another review request.
Two things that are not this anti-pattern and stay:
(ai-config#1029 is the case record: six rounds, 23 findings, all Addressed, with rounds 2-6 each finding real bugs in earlier rounds' own fixes --- including CRLF silently disabling a failure path repo-wide, a --compare reporting a zero delta, and a traversal-order-dependent measurement.
The loop stopped to ask twice under the old guard; both times the answer was to keep going, and the next round found four more real bugs.)
Post an unclaim comment (COMMENT_PR) to unblock any parallel sessions that backed off in step 1:
gh pr comment <N> --body "Done --- PR is free.
_Posted by Claude Code (AI agent) --- not written by a human._" # COMMENT_PR
Then run ums, before reporting ready.
The clean verdict is still a proactive-UMS checkpoint for this PR, not the
merge, but it is not the first one: run a pass when you read the review,
including a Rebut/Defer round, rather than holding everything for the
verdict.
See CLAUDE.md's "Run UMS proactively, as learnings accumulate".
The loop's whole point is that it ends here and hands the merge to a human,
so a pass deferred to the merge is deferred to a moment this session may
never see.
Everything the review lifecycle taught -- recurring findings, corrections,
guidance given along the way -- is complete as of the verdict.
Always provide a clickable link to the MR/PR in the final message.
Report the final verdict and round count. Don't merge unless asked.
ardgh pr view <N> --commentsCopilot code review doesn't post as a PR comment at all -- it's a
formal GitHub review, invisible to the command above. Request it
(REQUEST_COPILOT_REVIEW -- abstract operation token; resolve to your
model's tool via tool-mappings.md) and check
whether it posted a verdict at the current head. Finding a review
object at the right commit_id only proves Copilot looked -- it says
nothing about whether that review is clean. Fetch the matched review's
own overview and its inline comments (same two-call shape as the
review-link case above; the reviews list itself is READ_PR_REVIEWS)
before treating it as an all-clear:
gh api's own --jq flag has no --arg/--argjson (see
memories/gh-cli.md's gh api/jq note) --
pipe the raw paginated output into standalone jq -s instead, which
supports both:
set -o pipefail
head="$(gh pr view "<N>" --json headRefOid -q .headRefOid)"
review_id="$(gh api "repos/<owner>/<repo>/pulls/<N>/reviews" --paginate \
| jq -s --arg head "$head" \
'[.[][] | select(.user.login=="copilot-pull-request-reviewer[bot]" and .commit_id==$head)] | last | .id')"
if [ -n "$review_id" ] && [ "$review_id" != "null" ]; then
gh api "repos/<owner>/<repo>/pulls/<N>/reviews/$review_id" --jq '{state, body}'
gh api "repos/<owner>/<repo>/pulls/<N>/comments" --paginate \
| jq -s --arg rid "$review_id" \
'[.[][] | select(.pull_request_review_id == ($rid | tonumber))] | .[] | {line: (.line // .original_line), body}'
else
echo "no fresh review yet -- wait or re-request"
fi
A genuine clean Copilot overview is not an empty string -- it reads something like "Copilot reviewed N files and generated no new comments."
Don't require a literally empty body; parse the overview for a zero-new-findings phrasing and confirm zero matched inline comments -- both, not either alone, since zero inline comments with no affirmative zero-findings overview doesn't rule out a non-verdict formal review.
A "no new comments" overview can still carry real findings in a collapsed suppression block -- these are genuine flagged items under the fully-clean rule (address every finding regardless of confidence label), even though they never become formal inline comment objects the /comments endpoint returns (verified: PR #660's review 4767752501 read "generated no new comments" in its overview while its full body carried 3 suppressed findings; PR #1029 repeated the shape from round 3 onward).
A third condition is required: the raw review body must not contain a suppression block at all.
Match the suppression block inside its <summary> heading, case-insensitively on suppressed -- not on either exact phrase, and not anywhere in the body.
GitHub changed the wording and dropped the reason: PR #660 emits <summary>Comments suppressed due to low confidence (3)</summary>, while PRs #1029 and #1031 emit <summary>Suppressed comments (4)</summary>.
So a literal grep for Comments suppressed returns zero against a current body that plainly has the block, which produced a real false negative during the ai-config#1029 loop.
A body-wide match over-corrects, though, and would keep a clean PR permanently non-clean: ordinary overview prose contains the word, verified on review 4837572117, whose summary table reads "suppressed Copilot findings" outside any collapsed block.
Scope the match to <summary> elements (or parse the <details> block), and accept both headings.
The stakes are why this matters: from round 3 of ai-config#1029 onward every substantive finding arrived suppressed, under a "generated no new comments" overview with zero inline comments -- including CRLF silently disabling a failure path repo-wide.
So a suppressed finding is not a lower-value one, on the evidence available here --- which is a run of valuable suppressed findings, not a measured correlation.
GitHub's own docs do not document suppression at all, so expect the label to keep moving and key on the stable token within that scope.
And dispositioning a finding-bearing review's comments yourself does not make that same review the all-clear -- the fully-clean bar needs a later review, at the still-current head, that doesn't re-raise them.
So the all-clear is either (a) a review with a zero-new-findings overview, zero inline comments, and no suppressed- findings block, or (b) a later review at the same head as a finding-bearing one, confirming nothing remains -- never the finding-bearing review itself, however thoroughly you addressed its findings.
A review object existing at the current commit_id with unresolved findings inside it is not clean, it's just current.
A stub-like non-answer ("ineligible", "reached their quota limit") is also not a verdict -- treat it the same as a skipped/stub @claude run (see the "Do the review yourself" fallback in CLAUDE.md) and retry later or fall back accordingly.
GitLab: poll the MR notes (sort=desc) for a review note that
references your latest short SHA before proceeding; if none has appeared,
wait and retry rather than reading a stale verdict.
If the latest review is a cancellation, the live verdict is stale --- don't re-do already-applied fixes.
A cancel-in-progress cancellation (on setups that cancel superseded review runs) means the last complete review's findings may already have been fixed by a commit that landed after it, with the confirming re-review killed before it could post.
Before treating those findings as outstanding work, diff the current code against each one to see what's already addressed --- then push only what's genuinely needed and let a fresh review confirm.
Re-applying fixes that are already in the tree wastes a round and muddies the diff.
If nothing remains outstanding (every finding is already applied), don't push an empty commit --- skip to step 6 and re-request the review directly.
Execute the sequential multi-provider review loop defined in shared/workflow/adversarial-self-review.md.
You must pin all available providers (including external reviewers and the local adversarial-reviewer subagent).
You must query them sequentially, one at a time.
Do not request them in parallel.
When the loop reaches the local self-review step, don't perform it.
Hand the review to a separate adversarial-reviewer subagent (foreground, read-only), briefed with the base ref, the paths, and the standards that apply --- never with your rationale for the change, which is what makes a reviewer agree with you.
The session that wrote the diff knows what it was meant to say, so an inline pass reads the artifact and recovers the intent: confirmation rather than review, and indistinguishable from the real thing in the output (see adversarial-self-review).
Its brief covers what an inline pass would have done.
This includes the current PR diff against its base, each changed call path and edge case, the focused tests, and the relevant lint/documentation checks.
You must Address, Rebut, or Defer every finding it returns.
If a provider skips or cannot produce a verdict (quota, offline), note the skip in your ARD summary comment.
Re-check reviewer availability every round, not just once:
A reviewer that was unavailable a few pushes ago can become available mid-session.
A skipped review is never a clean external verdict on its own and does not authorize marking the PR as approved.
See The bar: "fully clean".
It requires clean verdicts at the current head from all reachable providers in your pinned quorum, not just a self-review.
ARD every finding --- regardless of severity label. "Not a blocker", "minor", "nit", "optional", "consider", "if you want" are for the user's prioritization, not a pass for the implementer. For each flagged item, choose exactly one:
defer-issue skill).Push fixes (if any). If main moved ahead of the branch, sync it in before you push, so the next review evaluates against current main:
git fetch origin main
git log --oneline ..origin/main | head # any commits? merge them in
git merge origin/main
Resolve conflicts, run the repo's pre-commit checks, then re-scan the PR's touched files with whitespace-normalizing search for merge-status hedges that main may have falsified (still open, not yet merged, once that merges, as of, will live at, proposed in) before pushing.
Do not use line-oriented literal grep; semantic line breaks can split the phrase this check needs to find.
Don't rebase/squash a published branch -- a merge commit matches GitHub's "Update branch" button. (The sync-pr-branch skill does exactly this.)
Resolve inline threads as you go --- including outdated ones.
After pushing fixes for a round, resolve the corresponding inline review threads immediately (RESOLVE_REVIEW_THREAD) via mcp__github__pull_request_review_write with method: resolve_thread and the threadId (returned by READ_PR_REVIEW_COMMENTS --- mcp__github__pull_request_read with method: get_review_comments).
Don't wait until fully-clean to do thread housekeeping.
For threads marked outdated in GitHub (the underlying code changed), confirm the fix is in the current tree, then resolve.
Threads whose fixes are already in the tree but were never resolved still block the "fully clean" check --- clear them as soon as you confirm the code is right.
Opportunistic conflict sweep. After pushing (or after any round where all findings were Rebutted/Deferred with no push), scan other open PRs in the same repo for merge conflicts:
gh pr list --state open --json number,title,headRefName,mergeable,mergeStateStatus,comments # LIST_PRS
For each PR where mergeable == "CONFLICTING" or "UNKNOWN" (see resolve-conflicts, "Verify before you act" --- UNKNOWN can mean GitHub hasn't finished computing yet, not that there's no conflict), verify with git merge-tree --write-tree origin/main origin/<branch> (git ≥ 2.38) before acting, then check claim status (most recent comment) and fix unclaimed ones --- same cascade procedure as post-merge step 1.5 (claim → isolated worktree → fetch main → merge → resolve-conflicts skill → push → unclaim).
A merge to main during your ARDI loop can create new conflicts in sibling PRs; clearing them while waiting for the next verdict is better than letting them pile up.
Post the ARD summary as a comment on the MR/PR (table format per the ARD skill).
Re-request review --- but don't double-trigger.
How depends on the repo's review trigger first, and on whether this round pushed code second.
Read the review workflow's on: block once per repo (the first time you push to a PR there) before applying the branches below --- the first one is wrong for a repo that has no push-based trigger, and nothing about the PR's appearance will tell you.
pull_request: with synchronize): the push already triggers the review.
Do NOT also post "@claude review again".
On workflows with concurrency: cancel-in-progress, the push-triggered and mention-triggered runs cancel each other, leaving the latest commit with a canceled, never-posted verdict.
Just wait for the push-triggered review.pull_request: trigger is absent or commented out, leaving workflow_dispatch (and perhaps issue_comment), which is how a repo disables automatic review on PR activity.
The push fires nothing, so you must dispatch explicitly, after the round's last push rather than once when the PR opened: gh workflow run <review-workflow>.yml -R <owner>/<repo> --ref <PR-branch> -f pr_number=<N>, taking the input's name from that workflow's own file.
There IS a cancel-in-progress race here, and it is one you create yourself --- dispatching after each push means each dispatch cancels the last, since the group is keyed on the PR number rather than on the trigger.
Finish pushing, then dispatch once.
Pass --ref as well, or the cancelled run's failing review gate attaches to the default branch and never appears on the PR.
See ardi's "Dispatch once, after the round's LAST push".
This is the branch that fails silently: CI still goes green on each push, so watching CI to green feels like watching the PR, and a verdict from an earlier head stands unchallenged for as long as you keep pushing.
(UCD-SERG/serocalculator, 2026-08-07: claude-code-review.yml has its pull_request: trigger commented out with the note "reviews are on request only".
Six pushes across several hours were each followed by watching CI to green.
No review was ever dispatched, and a verdict from roughly 20 hours earlier stood until the user asked whether the PR had a clean review.)@claude review,
or the forge's equivalent). This is the only case where you post the
mention.claude-code-review.yml on pull_request (opened, synchronize, ready_for_review, reopened) and workflow_dispatch (input pr_number), not on an @claude comment.
A new push auto-fires it.
To force a fresh review on an existing PR without a new commit, prefer workflow_dispatch: gh workflow run claude-code-review.yml --ref <PR-branch> -f pr_number=<N>.
Without gh, use the REST .../actions/workflows/claude-code-review.yml/dispatches endpoint, or your GitHub MCP workflow-dispatch tool.
Closing+reopening the PR also works (fires reopened) but adds timeline noise.
See memories/claude-review-dispatch.md.pr-on-claim for the diagnosis and the gh run rerun remedy.concurrency: cancel-in-progress a fresh dispatch is what cancelled the old run, so a retry cancels the new one --- possibly a review a human just asked for, since claude-bot.yml's review-workflow-file re-dispatches this very workflow into the same per-PR group.
Attribute each in-flight run to a PR from its own gather-context log, since gh run list's branch column reads main for all of them, and wait rather than retry if one is running.
Only when nothing is running, trigger one cleanly via gh workflow run claude-review.yml --ref <PR-branch> -f pr_number=<N> (input is pr_number) and don't push/comment again until it posts.
Pass --ref rather than omitting it: without it the dispatch runs against the default branch, so the run's check runs attach to main's tip instead of the PR's head commit, leaving the PR's own review check stale and making a check-runs query at that head useless as the in-flight pre-check above.
See review-verdict-pitfalls's "A cancelled review is the one case where retrying is the cause rather than the remedy", and its follow-on on why the cheap PR-side pre-check needs that ref.
Note: a review run on a bot-pushed commit may show as action_required (gated) and never run --- the explicit workflow_dispatch bypasses that.Don't let the trigger phrase leak into prose.
The issue_comment trigger fires on the bare bot @-mention anywhere in a comment body --- even inside a sentence saying you're not triggering a review.
In ARD summaries and status comments, refer to it obliquely ("re-request review", "the review-trigger mention") or split the tokens (e.g. @ claude, with a space, so the raw body never contains the contiguous handle); paste the literal @-mention only when you actually intend to dispatch.
A stray mention spawns a run that cancels the push-triggered review on cancel-in-progress setups.
On some mention-bot setups it also starts a session whose residual-commit sweep can churn the branch.
Then wait for the new verdict and for CI.
A CI-fix push is not handled when the first few jobs go green.
Read the full rollup: pending, queued, and in-progress jobs are still running;
a cancelled job has finished but is not clean and still needs investigation or a rerun before the rollup counts as done.
Arm wait-for-results (or the timer
below) instead of returning to chat with a partial gh pr checks read.
(gha#511, 2026-08-18: lint/link went green, a sibling job sat in
setup then cancelled, and the session left without waiting; the user
asked why.)
Set a timer when ending a turn waiting for AI reviews.
Whenever ending a turn while waiting for AI reviews or CI completion after pushing code, launch a schedule timer (e.g. 120s) and follow the check-and-reschedule procedure in shared/workflow/ardi.md when it fires.
While waiting, keep checking for merge conflicts.
Other PRs in this repo can become conflicting at any time (someone merges to main while the review runs).
Poll every few minutes with /loop or a manual re-check:
gh pr list --state open --json number,title,headRefName,mergeable,mergeStateStatus,comments \
--jq '.[] | select(.mergeable == "CONFLICTING" or .mergeable == "UNKNOWN")' # LIST_PRS
Verify each candidate with git merge-tree --write-tree origin/main origin/<branch> (git ≥ 2.38; see resolve-conflicts, "Verify before you act") before
claiming --- UNKNOWN isn't proof of a real conflict, and CONFLICTING can
be stale if a sibling PR merged since GitHub last computed it. Claim and
fix confirmed conflicts using the cascade procedure in post-merge step
1.5. Re-check after each resolution --- new ones can appear at any time.
This turns idle wait time into productive conflict prevention.