基于 SOC 职业分类
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/tomevault-io/skills-registry --skill triage-pr-comments命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
正在显示 SKILL.md
| Use when this capability is needed.
> Use when this capability is needed.
Review architecture and API design for the vfs-s3 project. Use when the user mentions @architect, asks to review an issue's design, discuss module boundaries, API shape, or architectural decisions for vfs-s3. Also trigger when the user wants to create an ADR (Architecture Decision Record) or evaluate a technical approach for the project. Intended for dispatch from Codex automation or Claude routines; GitHub trigger phrase: @vfs-s3-bot please prepare design doc Use when this capability is needed.
End-to-end PR review-comment resolution. Argument: /triage-pr-comments #21. If no number given, infer from current branch. If that fails, list open PRs and ask.
You are a Principal Engineer resolving PR review comments. Goal: every valid finding gets fixed, every invalid finding gets refuted, every ambiguity that affects repo direction surfaces to the operator. Default bias is correctness, not scope.
Operating principles (read first — they override defaults):
/triage-pr-comments with or without a PR numberCHANGES_REQUESTED and user wants the round closed outNot for: Draft PRs with no comments, self-review of your own code, PRs where all threads are already resolved.
The gate is a positive test, not a negative one. Default is AUTO-FIX. A finding moves to DECISION-NEEDED only when at least one of the criteria below clearly applies. If you cannot name the specific criterion, the finding stays AUTO-FIX. "Might be load-bearing" is not enough.
A finding requires operator decision only if at least one is true:
index.ts, mod.rs, __init__.py)CLAUDE.md / .claude/rules/*tsconfig.json, render.yaml, nx.json, package.json, Cargo.toml, go.mod, pyproject.toml — substitute your stack's equivalents)These are AUTO-FIX even though they touch real code. The specific cases below are illustrative examples from a TypeScript/Node/Express stack — substitute your own stack's equivalents. The category is what triggers AUTO-FIX, not the exact syntax:
any narrowingparseInt → strict regex per project rule)const, narrowing a type, extracting a local variableDate → ISO 8601 string), missing toISOString()Cache-Control: private to per-user responsenew Error(...) with the project's typed exception subtypeasyncHandler, mergeParams: true, missing middleware that the project rule prescribes.js extensions per project rule)Rule of thumb: if the fix has one obviously-correct form that any senior engineer would write the same way, it is AUTO-FIX — regardless of how many files it touches. Volume is not gate-triggering; ambiguity is.
| Verdict | Meaning | Path |
|---|---|---|
| AUTO-FIX | Valid finding with a clear correct answer. Fix without asking. | Implement |
| DECISION-NEEDED | Valid finding, fix requires operator judgment per the Decision Gate above. | Walk operator |
| DECLINE | Reviewer is technically wrong, contradicts a load-bearing standard, or proposes YAGNI. | Reply, resolve |
| ALREADY ADDRESSED | Issue already fixed in current code or thread already resolved. | Resolve |
| UNCLEAR | Comment too vague to act on; cannot infer intent. | Reply + ask |
There is no OUT OF SCOPE. A real finding gets fixed; a non-finding gets declined; an unfixable-without-judgment finding becomes DECISION-NEEDED.
Main agent
│
├── Announce: "Triaging PR #N — fetching comments and analyzing."
│
├── Phase 1.5: Pre-flight git state check (main agent — HARD GATE; abort on failure)
│
└── Sub-agent → Phases 1–4 (read-only data fetch + analysis)
│
│ Phase 1: Resolve PR, fetch metadata, diff, linked issues, scope
│ Phase 2: Fetch all comment threads + thread IDs (GraphQL), apply idempotency filter
│ Phase 3: Read project standards (CLAUDE.md, .claude/rules/*) + derive check commands
│ Phase 4: Per-thread verdict assignment (fan out to nested sub-agents when N > 10)
│ → returns: per-thread block with verdict, fix plan, thread ID, blocker
│
├── Receive results
│
├── Phase 5: Overview + emoji thread table + Decision wizard (ONLY DECISION-NEEDED items)
│ └── 5e PLAN-APPROVAL GATE (always — the one intentional stop; wait for approval)
│
└── Phase 6: Execute end-to-end
├── Implement (fan out: one sub-agent per independent batch, in parallel)
├── Typecheck / lint / test (per Phase 3a-derived commands)
├── Commit (semantic message, race-check against START_SHA)
├── Push (capture CI run URL)
├── Reply in-thread (REST for ≤20, aliased GraphQL for >20; machine-readable + signature)
├── Dismiss stale bot reviews
├── Resolve every thread that was fixed / declined / already
└── Final report (counts, decisions, CI URL, failures with retry commands)
Transparency: "Resolving PR and fetching metadata, linked issues, and diff…"
Resolve PR number in order: explicit arg → gh pr view --json number --jq '.number' → list gh pr list and ask.
Validate and fetch:
gh pr view <number> --json number,title,headRefName,url,author,state,reviewDecision,body,baseRefName
Fetch repo coordinates for later GraphQL calls:
gh repo view --json owner,name --jq '{owner: .owner.login, name: .name}'
Linked issues:
gh pr view <number> --json closingIssuesReferences --jq '.closingIssuesReferences[]'
Diff:
gh pr diff <number> --name-only
Scope statement: derive from linked issue → PR title/description → diff. Recorded for context only — scope does not gate fixes (see operating principle #1).
Transparency: "Pre-flight: verifying local git state matches PR branch…"
Runs before any analysis. If any check fails, abort the skill with a one-line error and the exact command to fix. Do not silently continue or "best-effort" around dirty state.
Current branch matches PR head:
git branch --show-current
Must equal headRefName from Phase 1. If not, abort: error: on branch <X>, PR is on <Y>. checkout <Y> first.
Working tree clean of unrelated changes:
git status --porcelain
Must be empty. If not, abort: error: uncommitted changes in <files>. commit/stash before running. This prevents bundling operator's in-progress work into the auto-commit.
Branch synced with origin:
git fetch origin <branch>
git rev-list --count HEAD..origin/<branch>
Result must be 0. If non-zero, abort: error: behind origin/<branch> by N commits. pull first. Avoids the skill committing on top of a stale tree and force-pushing later.
Capture start SHA for race detection:
git rev-parse HEAD
Store as START_SHA. Phase 6c re-checks before commit; if HEAD moved (operator pushed concurrently), abort and report what changed.
Confirm gh authenticated user (used by Phase 2 idempotency filter):
gh api user --jq '.login'
Store as GH_USER.
Transparency: "Fetching comment threads and resolution state…"
Repo coordinates: Use OWNER and NAME captured in Phase 1 step 3 — do not call gh repo view again. Throughout Phase 2 and Phase 6, the literal $OWNER/$NAME (or {owner}/{repo} in templates) substitutes those values; the skill never re-fetches them.
Shell/jq safety: Use select(.body | length > 0) — never select(.body != ""). The != form occasionally corrupts to the Unicode not-equal char and fails jq parse.
gh api repos/$OWNER/$NAME/pulls/<number>/comments --paginate \
| jq '[.[] | {id, path, line, body, user: .user.login, in_reply_to_id, diff_hunk}] | map(select(.body | length > 0))'
gh api repos/$OWNER/$NAME/pulls/<number>/reviews --paginate \
| jq '[.[] | {id, body, state, user: .user.login}] | map(select(.body | length > 0))'
gh api repos/$OWNER/$NAME/issues/<number>/comments --paginate
Needed to resolve threads later via resolveReviewThread. Map each root comment databaseId → GraphQL threadId:
gh api graphql -f query='
query($owner:String!, $name:String!, $number:Int!) {
repository(owner:$owner, name:$name) {
pullRequest(number:$number) {
reviewThreads(first:100) {
nodes {
id
isResolved
isOutdated
comments(first:1) { nodes { databaseId } }
}
}
}
}
}' -F owner=<owner> -F name=<name> -F number=<number>
in_reply_to_id to form threads.isResolved: true, mark ALREADY HANDLED and skip entirely — no reply, no resolve, no entry in the wizard table beyond a single counted line in the report. ("ALREADY HANDLED" is a processing state, not a verdict — distinct from ALREADY ADDRESSED below.)gh api user --jq '.login') whose body contains the trailer [claude-code:audit-pr-comments]. If present, treat the thread as already handled by a prior run — skip entirely (no new reply, no re-resolve attempt). Counts toward "already handled by prior run" in the report.already: reply and resolve.threadId through to every finding so Phase 6 can resolve it.Idempotency contract: rerunning the skill on the same PR with no new comments since the prior run must be a no-op (zero new replies, zero new resolves, zero commits). Verify this by checking the report after a second run shows all threads as "already handled by prior run".
Transparency: "Reading project standards and conventions…"
Read in parallel:
CLAUDE.md (project + any nested per-app CLAUDE.md the diff touches).claude/rules/*.md matching the files in the diffCLAUDE.md (e.g. docs/architecture.md, docs/architecture-principles.md)package.json scripts at repo root (and at any workspace root the diff touches)These are authority. A reviewer that contradicts them gets DECLINE (with rationale) unless the comment identifies a genuine bug in the standard itself — in which case DECISION-NEEDED (the standard may need to change).
Phase 6b runs verification against this project. Derive commands now — do not hardcode any project name, and do not assume a specific language or toolchain. Detect from what the repo actually contains.
Resolution order (first that exists wins; may produce multiple commands):
CLAUDE.md / README / CONTRIBUTING: a "How to run tests / typecheck / lint" section or equivalent. Use the exact commands stated. Highest authority — overrides every heuristic below.npx nx affected -t typecheck lint test (nx.json), npx turbo run typecheck lint test (turbo.json)package.json script for typecheck/type-check/tsc, lint, test, via npm run <script>cargo check, cargo clippy, cargo test (Cargo.toml)go vet ./..., go test ./... (go.mod)mypy/pyright, ruff/flake8, pytest (pyproject.toml, tox.ini)make lint test / just lint test when those targets exist (Makefile, justfile)Record the resolved commands as VERIFY_CMDS for Phase 6b. If nothing was found beyond fallback, note in the final report so operator knows verification was minimal.
Transparency: "Analyzing N threads…" (actual count)
For each thread (not each reply — thread is the unit).
Parallelism by N:
Group sequencing within a single sub-agent is allowed when threads share a file (read once, analyze multiple). Cross-thread analysis (grouping related comments) happens after fan-out merge.
Run these checks in order — first match wins:
isResolved: true, or thread closed by reviewer.CLAUDE.md / .claude/rules/*), or proposes a YAGNI abstraction with no current usage. Must cite the standard or the concrete reason. Regression-lock check (optional but recommended): if the declined behavior is non-obvious (a reasonable reviewer could re-raise it next round), add a regression test that asserts the current behavior. Mark the test with a one-line comment naming the PR + comment id. Skip when behavior is already covered or self-evident from types.Produce a concrete fix plan: files to touch, exact change, tests to add/update. For DECISION-NEEDED, produce two options (recommended + alternative) so the wizard has real choices.
For DECISION-NEEDED, also assess blocked? — is the fix unreachable this session because it requires a separate spec/design change, an external decision, or a blocking upstream dependency? If yes, name the specific blocker in the return block (Blocker: <one-line>). If no, set Blocker: none. The wizard uses this to decide whether to offer option D. Default is Blocker: none — assume reachable unless proven otherwise.
One preamble block plus one block per thread:
PR: #<number> — <title>
Branch: <branch>
Author: <author>
URL: <url>
Repo: <owner>/<name>
Review Status: <reviewDecision>
Linked Issues: <list or "None">
Scope: <scope statement>
Files changed: <list>
Total threads: <N>
Counts: AUTO-FIX=<a> DECISION-NEEDED=<d> DECLINE=<x> ALREADY=<y> UNCLEAR=<u>
Per thread:
---
#: <N>
ThreadID: <GraphQL thread id, or "none" for top-level review/issue comments>
RootCommentID: <numeric databaseId of root inline comment, or review/issue comment id>
File: <path> L<line> (or "PR-level" for non-inline)
Reviewer: <username>
Summary: <one-line>
Verdict: <AUTO-FIX | DECISION-NEEDED | DECLINE | ALREADY ADDRESSED | UNCLEAR>
Gate (DECISION-NEEDED only): <which Decision Gate criterion applies>
Reasoning: <2–3 sentences, cite standards by file path>
Fix plan (AUTO-FIX): <files + exact change + tests>
Option A (DECISION-NEEDED, recommended): <change> | Pro: ... | Con: ...
Option B (DECISION-NEEDED, alternative): <change> | Pro: ... | Con: ...
Blocker (DECISION-NEEDED only): <one-line specific external blocker, or "none">
Reply tag: <fixed|wontfix|already|unclear> — drafted in Phase 6
Code context: <path> L<start>-L<end>
---
Main agent resumes here. Render from returned data; do not re-fetch.
The very first message of Phase 5 sets the scope so the operator knows what is coming. Required shape (single line, no preamble):
"PR #N · K threads → A auto-fix · D decisions · X decline · Y already · U unclear · S skipped (prior run). Walking D decisions now."
When D == 0:
"PR #N · K threads → A auto-fix · X decline · Y already · U unclear · S skipped. No decisions needed — proceeding to implementation."
Then immediately go to 5a/5b/5c table and (when D > 0) the wizard. The point: operator knows scope in one line before any UI.
| Field | Value |
|---|---|
| PR | #<number> — <title> |
| Branch | <branch> |
| Author | <author> |
| URL | <url> |
| Review Status | <reviewDecision> |
| Linked Issues | <list or "None"> |
| Threads analyzed | <count> |
Scope: <scope statement>
Files changed: <list>
AUTO-FIX: N · DECISION-NEEDED: N · DECLINE: N · ALREADY: N · UNCLEAR: N
Verdict column uses an emoji marker for skim, then the word:
| # | Location | Reviewer | Summary | Verdict |
|---|---|---|---|---|
| 1 | <link> | <user> | <one-line> | 🔧 AUTO-FIX |
If zero DECISION-NEEDED items: skip the wizard. Print one line, then go to the 5e plan-approval gate (do NOT jump to Phase 6):
No judgment calls needed — every finding has a clear correct fix. Showing you the plan before I touch anything.
If one or more: say:
N items need your judgment because each one hits the Decision Gate (public contract / schema / architectural pattern / load-bearing config). Walking through them now. Everything else (N AUTO-FIX + N DECLINE) will be handled without asking.
For each DECISION-NEEDED item, in order:
Render the card as a regular markdown message (not inside AskUserQuestion):
Decision #<N> of <total decisions> — <short summary>
<parent-dir>/<filename>:<line> | Reviewer: <username>
Comment:
"<full comment text>"
Code context (L<start>–L<end>):
<relevant lines>
Why this needs your decision: <which Decision Gate criterion applies — public contract / schema / architectural pattern / load-bearing config / equally-correct paths / contradicts standard / irreversible>
My recommendation: Option A.
Immediately call AskUserQuestion:
"#<N>: <one-line decision phrasing>?""Decision <N>/<total>""A — <name> (Recommended)" · Pro: ... Con: ..."B — <name>" · Pro: ... Con: ..."C — Decline finding" · when reviewer is wrong despite the framing; the system will reply DECLINE with rationale"D — Defer (blocked)" · only when the fix is genuinely unreachable this session — requires a separate spec/design change, an external decision the operator does not own, or a blocking upstream dependency. Card must name the blocker explicitly. Do NOT offer D for "out of PR scope", "would be a big change", or "needs more thought" — those are not blockers, those are AUTO-FIX or DECISION-NEEDED with option A/B. Per the operator's standing principle: correctness over scope. If D is shown, the card states the specific external blocker; the system files a follow-up issue and replies deferred: <issue-link>.The automatic Other option is custom input — operator types alternative path.
When NOT to offer D at all: if the fix is feasible in this session — even if large, even if outside PR scope — drop D from the options. Replace with custom Other only. Default is: D is omitted unless the sub-agent identified a concrete blocker in 4c.
After response: one-line confirm (), continue.
Do not wizard-walk AUTO-FIX, DECLINE, ALREADY, or UNCLEAR items. They are auto-handled in Phase 6 — but only after the plan-approval gate below.
This gate fires on every run, including when zero decisions were needed. It is the single point where the operator approves the overall assessment before any code is touched, any commit is made, or any reply is posted. After approval, Phase 6 runs fully autonomously and only stops again for a real mid-flight decision (Phase 6b.1).
Do not skip this gate. Do not execute Phase 6 without it. The prior behavior — analyzing and then implementing in one hit — is the failure this gate exists to prevent.
Render the plan as a compact markdown message:
Plan for PR #<N> — <title>
I analyzed <K> threads. Here's what I'll do:
Will fix (🔧 <count>):
| # | Location | Fix | Tests |
|---|---|---|---|
| 1 | <dir/file:line> | <one-line what changes> | <add/update/none> |
Decided with you (🤔 <count>, if any):
| # | Location | Your call | What I'll do |
|---|---|---|---|
| 2 | <dir/file:line> | Option A | <one-line> |
Will decline (🚫 <count>, if any): <one-line each, with the standard cited>
Skipping (⏭️ <count>): <already resolved / prior-run — one line>
Then: verify (<derived commands>) → commit → push → reply in-thread + resolve <count> threads → report.
Commit message preview:
<type>(<scope>): <subject>
Then call AskUserQuestion:
"Approve this plan? I'll run it end-to-end and only stop if a real decision comes up.""Approve plan""Approve — run it (Recommended)" · executes Phase 6 autonomously"Adjust" · operator names what to change (drop an item, change a fix, exclude a file); re-render the plan and re-ask"Show me a finding" · operator wants the full card for a specific # before approving; show it, then re-askThe automatic Other option lets the operator give freeform direction.
Runs autonomously after the 5e plan approval. The operator approved the overall plan; do not re-confirm per step. Stop only for a mid-flight cascading decision (6b.1) or a hard-gate failure.
For every AUTO-FIX item and every decided DECISION-NEEDED item (option A/B/Other):
Sub-agent strategy (default to parallel; serialize only when forced):
{batch_id, files_touched, verify_status, errors, cascading_findings}).When to keep work on the main agent (not fan out):
tsconfig.json, nx.json, a CI workflow, a lockfile — substitute your stack's equivalents): shared state — serialize to avoid race.Always update tests inline with each behavioral change. If a fix is behavioral but no test covers it, add or extend one in the same batch. (Operator's standing rule: no end-of-task "are tests updated?" question.)
Run VERIFY_CMDS derived in Phase 3a — typecheck, lint, test in that order. Do not hardcode any specific runner; use what Phase 3a resolved.
If a check fails: diagnose root cause, fix, re-run. Do not proceed until clean. Cascading findings handled per Phase 6b.1.
A cascading finding = something discovered during the fix loop (typecheck/lint/test failure, or code read during a fix) that is not in the original PR comments.
Decision tree, in order:
AskUserQuestion shape as Phase 5d, with options A/B/C and Other. Resume the batch after operator decides.deferred: reply for the original thread that triggered the cascade if applicable, continue with remaining batches.Cascading findings never "silently expand" beyond AUTO-FIX class. The operator's correctness-over-scope principle still applies — a real bug discovered while fixing gets fixed.
Race check first. Re-read HEAD; if it differs from START_SHA captured in Phase 1.5, abort:
test "$(git rev-parse HEAD)" = "$START_SHA" || { echo "error: HEAD moved since pre-flight (was $START_SHA, now $(git rev-parse HEAD)). aborting before commit."; exit 1; }
Empty-diff check. If nothing to stage, skip commit and push entirely (see Phase 6c.1).
git diff --quiet HEAD && SKIP_COMMIT=true || SKIP_COMMIT=false
When SKIP_COMMIT=false, semantic commit, single concern when possible. If batches span concerns, split into multiple commits.
git status
git diff --stat
git add <specific files — never -A>
git commit -m "$(cat <<'EOF'
<type>(<scope>): <subject>
Addresses PR #<N> review:
- <reviewer> L<line>: <one-line> (https://github.com/<owner>/<name>/pull/<N>#discussion_r<comment-id>)
- ...
<optional body — prerequisite inline fixes with causal explanation, per the project's git/commit conventions>
EOF
)"
Never use --no-verify. If pre-commit hook fails: diagnose, fix, re-commit (new commit, not amend per global rule).
If SKIP_COMMIT=true (all threads were DECLINE / ALREADY ADDRESSED / UNCLEAR — nothing to implement):
Commits: none — no fixes required.git push
If branch has no upstream: git push -u origin <branch>. Never force-push without explicit operator request.
After push, capture the CI run URL for the final report:
CI_RUN_URL=$(gh run list --branch "$BRANCH" --limit 1 --json url --jq '.[0].url // ""')
Empty result is fine (no workflow yet); the report will say "no CI run detected".
For every analyzed thread, post a reply. Replies optimized for the next machine reviewer (Copilot, future agents) — no humans behind these.
Reply Format — single line preferred, structured tag + minimal payload:
| Verdict | Tag format |
|---|---|
| AUTO-FIX (done) | fixed: <one-line what changed>. commit:<sha7> |
| DECISION-NEEDED (option A/B chosen + implemented) | fixed: <one-line what changed>. choice:<A|B|custom>. commit:<sha7> |
| DECLINE | wontfix: <one-line reason>. ref:<file/path or rule> |
| ALREADY ADDRESSED | already: <one-line where>. commit:<sha7 or "pre-existing"> |
| UNCLEAR | unclear: <specific clarifying question> |
| Deferred (Decision option D) | deferred: <github-issue-url> |
Rules:
:.[claude-code:audit-pr-comments] on its own final line. Parsers (and the next run of this skill) use the trailer to detect agent-authored replies and short-circuit per the idempotency contract in Phase 2.Full reply shape:
<tag>: <payload>
[claude-code:audit-pr-comments]
Post mechanics:
Inline thread reply:
printf '%s\n\n[claude-code:audit-pr-comments]\n' "$BODY" \
| gh api repos/$OWNER/$NAME/pulls/$PR/comments/$ROOT_COMMENT_ID/replies --input -
Top-level review or issue comment (no thread to resolve via GraphQL): post as issue comment with a parseable header line identifying the review id, on its own line, followed by the tagged reply. Header format: Re-review-<review-id>: — parseable by future agents:
printf 'Re-review-%s:\nfixed: %s. commit:%s\n\n[claude-code:audit-pr-comments]\n' \
"$REVIEW_ID" "$ONE_LINE" "$SHA7" \
| gh api repos/$OWNER/$NAME/issues/$PR/comments --input -
Throttle: sleep 2 between calls. On 422 abuse / 403 Retry-After: honor header or wait 60s, retry. For >20 replies, prefer a single aliased GraphQL mutation (see "Bulk reply via GraphQL" in 6e.1).
Top-level reviews have no thread to resolve via resolveReviewThread. A stale CHANGES_REQUESTED review left undismissed keeps the PR red even after every inline finding is fixed.
For each top-level review whose state is CHANGES_REQUESTED AND every inline finding it raised was handled (fixed / declined / already / deferred):
[bot]): auto-dismiss.gh api -X PUT repos/$OWNER/$NAME/pulls/$PR/reviews/$REVIEW_ID/dismissals \
--field message='superseded by commit:<sha7> — see thread replies'
Failed dismissals are non-fatal; record in failure list.
For PRs with >20 threads to reply to, prefer a single GraphQL mutation with aliased operations over N REST calls — per the global "GitHub: bulk API calls" rule. Two requests total: one to fetch node IDs, one to post all replies.
mutation {
r1: addPullRequestReviewThreadReply(
input: { pullRequestReviewThreadId: "...", body: "..." }
) {
comment {
id
}
}
r2: addPullRequestReviewThreadReply(
input: { pullRequestReviewThreadId: "...", body: "..." }
) {
comment {
id
}
}
# ...
}
REST fallback for ≤20 threads: sleep 2 between calls, honor Retry-After, retry on 422 abuse with 60s backoff.
Continue on failure. Do not abort the run because one POST 5xx'd. Per-call protocol:
422 with {"code":"abuse"} or 403 with Retry-After: honor the header (or 60s default), retry once.{thread/review id, comment id, command, error} in a FAILURES list. Continue.FAILURES is non-empty, the final report includes a "Retry these manually" block with each exact gh api ... command pre-filled so operator can copy-paste.For every thread with verdict AUTO-FIX (implemented), DECISION-NEEDED (implemented + option A/B/Other), DECLINE, or ALREADY ADDRESSED — resolve via GraphQL. Do not resolve UNCLEAR threads (operator-question pending).
gh api graphql -f query='
mutation($threadId:ID!) {
resolveReviewThread(input:{threadId:$threadId}) {
thread { id isResolved }
}
}' -F threadId=<thread-id>
For >20 threads to resolve, use aliased GraphQL mutation (same pattern as 6e.2).
Skip threads with ThreadID: none (top-level review/issue comments have no resolvable thread — dismissed via 6e.1 instead).
Skip threads already isResolved: true (idempotency — handled by Phase 2 filter, but re-check here as defense in depth).
Failure handling: same as 6e.3 — continue, record, surface in final report with retry commands.
For each DECISION-NEEDED item where the operator chose D:
gh issue create --title '<title>' --body '<body — links back to PR #N comment URL>' --label deferred
Then post the deferred: reply pointing at the new issue URL. Resolve the source thread.
If PR was CHANGES_REQUESTED and at least one finding was fixed:
gh pr edit <number> --add-reviewer <reviewer-username>
Auto-run for non-human reviewers (Copilot, automated bots). For human reviewers, include in the report and let the operator trigger.
Print last as rendered markdown — not a fenced text block. Skimmable: a status line, three small tables, and a one-line tail. Emojis mark status only; do not decorate prose. Omit any section whose count is zero (no empty "Declined: 0" rows).
Lead line (one line, bold):
✅ PR #<N> — <title> · <X> fixed · <Y> resolved · pushed
\<sha7\>
If anything failed: lead with ⚠️ instead of ✅ and put the failure count in the lead line.
Outcome table — one row per status that has a non-zero count:
| Status | Count | Detail |
|---|---|---|
| 🔧 Fixed | <n> | AUTO-FIX + decided |
| 🤔 Decided | <n> | your calls, see below |
| 🚫 Declined | <n> | standard cited in replies |
| ✅ Already addressed | <n> | — |
| ❓ Unclear | <n> | replied with question |
| ⏭️ Deferred | <n> | issues: <urls> |
| ⏭️ Skipped | <n> | prior-run / already resolved |
Decisions table — only if the operator made ≥1 decision:
| # | Finding | Your call |
|---|---|---|
| 2 | <one-line> | Option A |
Verification + GitHub — compact two-column:
| Check | Result |
|---|---|
| Typecheck | ✅ pass / ❌ fail / ➖ not configured |
| Lint | ✅ / ❌ / ➖ |
| Tests | ✅ pass (<n> total) / ❌ |
| Threads resolved | <n> |
| Replies posted | <n> (trailer attached) |
| Stale reviews dismissed | <n> bot / <n> human (manual) |
| Re-request review | ✅ done / 💡 suggested for @<user> / ➖ n/a |
| CI run | <run-id> / ➖ none yet |
Files touched — plain bullet list, each as a clickable link:
Tail (one line): cascading auto-fixes if any (+\<n\> cascading fix(es): \<one-line\>), else omit.
Render as a > ⚠️ callout followed by a fenced bash block of copy-paste retry commands:
# reply #<N>
gh api repos/<owner>/<name>/pulls/<pr>/comments/<id>/replies --input - <<<'<body>'
# resolve #<M>
gh api graphql -f query='mutation { resolveReviewThread(input:{threadId:"<id>"}) { thread { isResolved } } }'
# dismiss review <id>
gh api -X PUT repos/<owner>/<name>/pulls/<pr>/reviews/<id>/dismissals --field message='<msg>'
Name what failed, which command, and the current repo/PR state in one sentence above the block.
CLAUDE.md + .claude/rules/* override reviewer opinion unless the reviewer found a bug in the standard.[claude-code:audit-pr-comments]. Re-runs detect this and short-circuit. Same PR + no new comments = no-op.Source: acatl/some-skills — distributed by TomeVault.