用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/Morrison-Lab/ai-config --skill ard命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
正在显示 SKILL.md
| name | ard |
| description | Address, rebut, or defer review items. |
| user-invocable | true |
| allowed-tools | ["Bash","Agent","Read","Edit","Write"] |
Every review comment gets exactly one disposition: A, R, D, or K. Ignoring is not an option.
A finding is anything the reviewer requests or implies a change to — including items tagged "nit", "minor", "non-blocker", "optional", or "consider". All of these require Address, Rebut, or Defer.
Pure praise or neutral observations with no requested change ("nice refactor", "TIL") still get a row — disposition Acknowledge — so the summary accounts for every comment the reviewer made and nothing reads as silently dropped.
| Code | Meaning | Action required |
|---|---|---|
| A — Address | Valid and in-scope. | Fix it in this PR/MR and commit. |
| R — Rebut | Incorrect, already handled, or a misunderstanding. | Explain why, citing concrete evidence (line, test, doc, spec). Specific enough that the reviewer can verify it without re-reading the whole PR. |
| D — Defer | Valid but out of scope (new feature, broad refactor, needs design discussion). | File a follow-up issue (CREATE_ISSUE on GitHub — gh issue create; glab issue create on GitLab), link it, and add it to the PR/MR's Deferred / Out-of-Scope section. |
| K — Acknowledge | Praise or a neutral observation with no change requested. | Give it a row so it's accounted for; no code change, no rebuttal needed. Don't stretch this to dodge a real finding. |
For anything that requests a change, choose among the first three (Acknowledge is only for no-ask comments):
@claude bot) state such claims confidently but are sometimes exactly backwards, and blindly "Addressing" a wrong one breaks the build. (Seen on rme #873 with igraph ≥ 2.x: the bot insisted bfs(mode=) was deprecated in favor of neimode=; the reverse is true — neimode is a hard deprecate_stop error, mode is current. Re-running the script revealed it instantly. The deprecation direction can differ across igraph major versions, so test on the locked version.)
The same testing discipline applies when the claim is about the PR's own state or metadata rather than code behavior — which footer/URL convention a given post uses, whether a file matches main, whether a check still exists — not just runnable code. Fetch the actual PR data (the API call, not memory) before Addressing or Rebutting. (Seen on ai-config#688: a review claimed a memory bullet's cited attribution-footer text was wrong, reasoning from the PR's own body — but the PR body and a reply comment get their footer through two different code paths that don't match. Fetching both directly via the API before responding showed the bullet was right and the reviewer had compared the wrong artifact; rebutted with that evidence instead of either implementing the suggested fix or dismissing the finding unchecked.)"You deleted/removed X" findings — check for branch-behind-main first. When a reviewer says the PR removed something you don't recall touching, run
git log HEAD..origin/mainandgit show HEAD:<file>before Addressing. Main may have added X after you branched, so X is absent only because the branch is stale — not because your diff deleted it. The fix is mergeorigin/mainin (the standing keep-synced rule), not "restore" a line you never removed. Verify the actual committed diff (git show <commit> -- <file>) rather than trusting the finding's framing. (Seen on ai-config PR #52.)
"Blocking: missing X" findings that cite a CI job or policy — check the job still exists on
mainfirst. A review can flag a CI failure (e.g. "the Require Changelog Entry job is failing, add a CHANGELOG.md entry") as blocking, but the requirement itself can be removed frommainin the time between when the review ran and when you address it — a different PR deletes the CI job and/or the file it checks. This isn't the branch-behind-main case above (nothing was deleted from this PR); the requirement stopped existing project-wide. Before Addressing, check whether the cited job/file is still onmain(git log --oneline origin/main -- <path>, orgit show origin/main:<path>). If it's gone, Rebut with the removal commit(s) as evidence rather than resurrecting a dead requirement. (Seen on ai-config PR #376: a "blocking" CHANGELOG.md finding referencedrequire-changelog.yml, which had been deleted frommainin #385/#387 by the time the fix round ran.)
Collect the full set before dispositioning, so none slips through. Pull both the summary comment and the inline review threads — READ_PR_COMMENTS and READ_PR_REVIEW_COMMENTS (abstract operation tokens; resolve to your model's tool via tool-mappings.md).
GitHub
gh pr view <N> --comments # READ_PR_COMMENTS — top-level + summary comments
gh api repos/{owner}/{repo}/pulls/<N>/comments # READ_PR_REVIEW_COMMENTS — inline review-thread comments
GitLab
glab mr view <N> --comments # discussion notes
glab api "projects/:id/merge_requests/<N>/discussions" # inline threads
Bots often post the same finding twice — once inline and once in the summary comment. Collect the union and dedupe before numbering, so one issue doesn't get two rows (or two conflicting dispositions). Then number 1..n; every number must end up with a row in the summary.
A PR can also carry more than one review surface from different reviewers: an inline code-review bot, a separate agent post-step that posts its own top-level summary, and Copilot are each distinct. gh pr view <N> --comments returns every top-level comment, so disposition each reviewer's findings — not just the inline review or the most recent comment. A whole review summary left un-dispositioned reads as ignored even when every inline thread was handled.
Apply the decision order above. For Address items, make the edits now.
git add -p # stage deliberately
git commit -m "fix: address round <k> review findings" # COMMIT
git push # PUSH
--amend the already-reviewed commits: the reviewer (and CI) ran against them and others may have pulled. A fresh commit keeps the audit trail..Rout, build artifacts); respect the repo's .gitignore.Write the summary to a file and post it from the file — never inline on GitLab, because glab mis-parses backticks (e.g. commit SHAs in code spans) as shell subcommands:
# write the summary to ard-summary.md, then:
gh pr comment <N> --body-file ard-summary.md # COMMENT_PR — GitHub
glab mr note <N> -F ard-summary.md # GitLab
End ard-summary.md with the agent-disclosure marker, on its own line after a blank line:
_Posted by Claude Code (AI agent) --- not written by a human._
This comment is posted under the account holder's own login on every round of every PR, so it is among the likeliest in the corpus to be mistaken for their own writing --- see disclose-agent-authorship.
The body arrives via --body-file, which the disclosure guard cannot read.
It still warns --- but only that the body is unreadable, never that the marker is absent, and that weaker note reads as a formality rather than a finding.
So this step states the requirement itself.
Keep the bot's trigger phrase out of the summary body. The issue_comment
trigger fires on the bare bot @-mention anywhere in a comment — even in a
sentence saying you're not re-requesting a review. 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 review. 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.
Summary format:
Addressed findings from review of <commit-or-range>:
| # | Finding | Disposition | Detail |
|---|---------|-------------|--------|
| 1 | <summary> | ✅ Address | Fixed in <commit-sha> |
| 2 | <summary> | 🔄 Rebut | <one-line reason> |
| 3 | <summary> | 📌 Defer | <issue-link> |
| 4 | <summary> | 👍 Acknowledge | <one-line thanks / note> |
### Rebuttal: Finding 2
<full explanation with evidence>
Expand each Rebut below the table. Deferred rows must carry a real issue link.
Backtick any verdict phrase you quote, including the one in the opening line. A disposition summary names the round it is disposing of, and the natural way to write that names the round's verdict too:
Addressed GitHub Claude of `9508454e` (Needs more work). Pushed `8af4edc9`.
check-pr-fully-clean.py reads that parenthetical as this comment's OWN verdict.
Because a driving session never posts a superseding clean one, the per-reviewer rule from #2274 then keeps the PR not-clean permanently, on a citation of a round that was already addressed.
Measured on ai-config#2341: that line alone froze the PR, while the disposition table and a self-imposed hold in the same comment produced no verdict at all.
The fix is one pair of backticks, because strip_cited_finding_vocab already blanks inline code spans as citation (#1202):
Addressed GitHub Claude of `9508454e` (`Needs more work`). Pushed `8af4edc9`.
Do not expect the checker to infer it from the sentence instead.
Three attempts were built and deleted on ai-config#2409 -- a driver-ledger classifier keyed on claim wording, the same classifier gated on the agent-disclosure marker, and a citation rule keyed on the disposition verb.
Each dropped or blanked a genuine reviewer's not-clean, because a reviewer rejecting a claimed fix writes the same sentence a driver does ("Addressed in abc1234 (still Needs more work)").
The disambiguation has to come from the author, and a code span is how this corpus already spells it.
The opening line is the commonest instance, not the only one. The same wire is tripped by a verdict phrase left bare anywhere in the comment --- a table cell summarizing what the reviewer said, or a paraphrase inside a Rebuttal expansion:
| 1 | Reviewer said `Needs more work` on the null check | Address | Fixed in `8af4edc9` |
Verified by execution: without those backticks, that row alone classifies the whole comment not-clean, exactly as the header line does. So sweep the finished comment for the phrase rather than backticking only the line the example shows.
(Needs more work) next to a cited SHA -- that is the exact shape that freezes the PR.(The standalone resolve-pr-threads skill
runs just the resolve half of this step on its own — useful for sweeping
already-settled threads without a full ARD pass.)
The one summary comment is not enough on its own. A reviewer who left inline comments wants a response on each thread, not just a table posted elsewhere. For every inline comment, post a short reply on its own thread with the disposition (and the commit SHA for an Address), then resolve the thread once the item is genuinely settled.
GitHub — reply on the comment's thread (REPLY_REVIEW_COMMENT), then
resolve via GraphQL (RESOLVE_REVIEW_THREAD):
# Reply on the same thread as inline comment <comment_id>:
# (quote every argument containing a <placeholder> -- bash treats a bare `<`
# as a redirection operator even mid-word, so an unquoted path like
# /tmp/reply-<comment_id>.md silently writes to the wrong file)
cat > "/tmp/reply-<comment_id>.md" <<'EOF'
✅ Addressed in `<sha>`.
_Posted by Claude Code (AI agent) --- not written by a human._
EOF
gh api "repos/{owner}/{repo}/pulls/<N>/comments" \
-F in_reply_to="<comment_id>" -F body="@/tmp/reply-<comment_id>.md" # REPLY_REVIEW_COMMENT
# List threads to get the node id, then resolve the settled one:
gh api graphql -f query='query { repository(owner:"<owner>",name:"<repo>") {
pullRequest(number:<N>) { reviewThreads(first:100) { nodes {
id isResolved comments(first:1){ nodes { databaseId body } } } } } } }'
gh api graphql -f query='mutation {
resolveReviewThread(input:{threadId:"<thread_node_id>"}) { thread { isResolved } } }' # RESOLVE_REVIEW_THREAD
In a remote/web session without gh, resolve RESOLVE_REVIEW_THREAD via
mcp__github__resolve_review_thread instead (see tool-mappings.md).
GitLab — reply to the discussion, then resolve it:
cat > "/tmp/reply-<discussion_id>.md" <<'EOF'
Addressed in `<sha>`.
_Posted by Claude Code (AI agent) --- not written by a human._
EOF
glab api -X POST "projects/:id/merge_requests/<N>/discussions/<discussion_id>/notes" \
-F body="@/tmp/reply-<discussion_id>.md"
glab api -X PUT "projects/:id/merge_requests/<N>/discussions/<discussion_id>?resolved=true"
Resolve only when the item is actually settled:
the repository owner via the request-pr-review skill (or
gh pr edit <N> --add-reviewer <reviewer>, which bypasses that skill and so
does not inherit its Lacaedemon/sparta exception --- in sparta, escalate
to the user in chat instead of requesting a reviewer) and @-mention them
with the impasse --- rather than resolving unilaterally or looping forever.
Not every re-raise is an impasse, though: if the reviewer accepts your fact
but objects that the claim is over-generalized (you verified one case and
stated a sweeping rule), narrow the claim to what you actually observed —
that's an Address, not a continued Rebut. Reserve escalation for genuine
factual disputes. (Seen on ai-config PR #183: a tested
project-root {{< include >}} behavior was correct but asserted as a
universal Quarto-projects rule; the reviewer granted the test and asked only
that it be scoped to the observed case, so the fix was to soften the wording,
not re-argue.)Don't resolve a thread you haven't replied to. Every inline comment ends with both a reply and (where appropriate) a resolution — silence on a thread reads as ignored, exactly the failure ARD exists to prevent.
End-state (fully clean): when the PR/MR is 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. A leftover open inline thread (an unaccepted rebuttal, an un-resolved Address) means you're not clean yet.
Pause point: before reporting the round done.
Do-Confirm; per
shared/workflow/skill-checklists.md.
fully-clean's criterion --- an
addressed-but-unresolved thread reads as outstanding work to every later
reviewer.
Sweep by thread id, not by finding: a re-raise usually opens a second
thread on the same line, often already marked outdated.Tell the user what you did and give a clickable URL to the PR/MR (and to the posted summary comment if available), so they can review in one click.
suggestion block is a hint, not gospel. Before committing a reviewer's suggested fix, verify it actually works and handles the general case — not just the flagged spot. A confident suggestion can overcorrect (e.g. a regex tweak that fixes one form but breaks all the others). If the correct fix differs, apply that and note the divergence in your reply, so the reviewer sees why.When dispositioning a finding (step 2) turns up something that needs
independent digging --- testing a reviewer's factual claim before Addressing
or Rebutting it (see the testing rule above), researching how a prior PR
handled the same pattern, or checking whether a cited CI job still exists on
main --- hand it to a subagent via the Agent tool rather than blocking the
rest of the round on it. Keep dispositioning the remaining findings, posting
the summary, and pushing fixes on the main thread.
Give the subagent a stronger model (e.g. model: 'opus' on the Agent tool
call) when the check is judgment-heavy --- a subtle behavioral claim, a
disputed rebuttal --- rather than leaving it at the session default.
Symmetrically, drop to a cheaper/faster tier (model: 'fable' or 'haiku')
for a mechanical check (a lookup, confirming a file/symbol still exists); see
select-model's decision tree for both
directions. For a heavy fan-out verification pass, prefer a separately-billed
provider (e.g. the codex CLI) first when available --- see
delegate-to-codex.
Inside the ardi loop (also reachable as iterate):
ardi step 2)ardi step 6) — even if this round was Rebut/Defer only, so the reviewer re-evaluates.The loop continues until the PR/MR is fully clean — zero findings, all CI
workflows green, and every inline review thread resolved (the only open
conversation being the final all-clear exchange — the reviewer's all-clear and
your reply to it). A rebuttal counts only once it convinces the reviewer; on an
impasse, escalate to a human reviewer (the repository owner; see step 4b for how).