review-agent-feedback
Use when an existing GitHub pull request or GitLab merge request already carries review comments from an automated reviewer — Kody/Kodus, CodeRabbit, Copilot, Gemini Code Assist, PR Review, or any oth
SKILL.mdskills/review-agent-feedback/SKILL.md
GitHubReview Agent Feedback
A bot's finding is a claim, not a fact — and a reply is not a resolution. Every automated review thread gets one verdict grounded in the code as it exists at the PR/MR head, one in-thread reply that states that verdict with evidence, one reaction the reviewer can learn from, and one resolution — then the thread is re-read and the report says what the remote actually shows.
Works on GitHub (gh) and GitLab (glab), self-hosted included; provider is
auto-detected from origin or the URL. Canonical location:
issueops/skills/review-agent-feedback (home skill paths are symlinks installed
by issueops update). agents/openai.yaml exposes it as $review-agent-feedback
on Codex. Call the script as <skill>/scripts/review_threads.py, where <skill> is
this directory.
Pipeline
1 List review_threads.py list → bot threads, normalised, with already_handled
2 Verify you, per thread → verdict + evidence (code, diff, tests, rules)
3 Plan plan.json → verdict / reply / reaction / resolve per thread
4 Apply review_threads.py apply --dry-run, then apply → verified ledger
5 Report ledger table → what the remote shows, not what was sent
1. List
out=<repo>/.issueops/issues/<issue-number>/review/<provider>-<number>; mkdir -p "$out" # no linked issue: <repo>/.issueops/tmp/review-agent-feedback/<number>
python3 <skill>/scripts/review_threads.py list --pr <number|!n|#n|URL> --repo-dir <repo> > "$out/threads.json"
threads.json, plan.json, and the ledger live under that $out — the per-issue
artifact folder's ignored review/ area (same rule as pr-review); they are working files,
not commits. The issue number comes from the PR/MR body's issue link or the IssueOps record.
Read threads.json. It contains only bot-authored threads (--all adds humans);
each has reviewer, mention, resolvable, resolved, path, line,
already_handled. Skip threads whose already_handled equals the verdict you would
give now — they were finished by an earlier run. Threads with resolvable: false
(summary / status notes) are not part of the job unless the user names them. If
state is not open, say so before doing anything. If the list is empty after those
filters, report "처리할 봇 스레드 없음" with the counts and stop.
With a full URL, --repo-dir is only needed for the verify step (a clone at the
PR/MR head); provider, host, and project come from the URL. With a bare number the
origin remote of --repo-dir decides them.
For GitLab hosts whose token lives in a profile-scoped MCP server rather than the
CLI, reproduce the calls in references/providers.md through that glab_api tool —
the paths and fields are identical; the gitlab-usecase skill governs profile choice.
2. Verify (you)
For every thread, before writing a word of reply:
- Open the file at the PR/MR head (
git fetch+ checkout orgit show <head_sha>:<path>), not the diff hunk alone; follow the symbol one hop upstream (where the value is produced/validated) and one hop downstream (where it is consumed). - Decide whether the change under review introduced the issue or it is pre-existing
(
git blame/base branch), and whether a cited project rule really names it. - Run the fastest relevant check when the claim is about behaviour (the test, a
typecheck, a reproduction). Where the claim cannot be checked, the verdict is
hold, notvalid. - Verdict:
valid·partial·invalid·out_of_scope·hold(definitions and default reaction/resolve inreferences/providers.md).
3. Plan
Write plan.json (schema in the script docstring). Each reply is Korean prose in this
shape; the script prepends the reviewer's mention and appends the idempotency marker:
**판정: 타당** (또는 부분 타당 / 타당하지 않음 / 범위 밖 / 보류)
근거: <파일:라인 · 호출 경로 · 명령 결과>. <봇의 전제 중 맞는 것/틀린 것>.
조치: <적용한 수정과 커밋 · 별도 이슈 · 현재 코드가 안전한 이유 · 보류 사유>
Identifiers, paths, and commands stay verbatim; never quote secret values. A reply must contain the evidence that decided the verdict — "확인했습니다" alone is not a reply.
Before posting plan.json, invoke the fluent-korean skill and apply it to every
reply. These replies are published to a remote thread a human reads, so the polish
pass is a precondition for posting, not an option.
Rules for the plan:
holdthreads (waiting on a human, a follow-up promised in this PR/MR that is not pushed yet, or the user said leave it open) keepresolve: falseand areason_open; every other verdict resolves, whether accepted or rejected.- Reaction rates the finding's usefulness for this change: 👍 correct and actionable here, 👎 false, misleading, over-severe, or not actionable here.
- The user's overrides ("resolve 하지 마", "반응 달지 마") go into
reaction/resolvefields, not into skipped steps.
4. Apply
python3 <skill>/scripts/review_threads.py apply --pr <ref> --repo-dir <repo> --plan plan.json --dry-run # preview reply bodies and planned actions
python3 <skill>/scripts/review_threads.py apply --pr <ref> --repo-dir <repo> --plan plan.json # post, react, resolve, then re-read
The script replies to the original thread, adds the reaction on the reviewer's note,
resolves the thread, then re-fetches it and marks each row ok, unverified, or
error. It never duplicates a reply or reaction on re-run and refuses to resolve
resolvable: false threads. Single primitives (reply, react, resolve,
unresolve, verify) exist for partial redo.
5. Report
Present the ledger as a table — one row per thread: file:line · reviewer · verdict ·
reply id · reaction · resolved (and reason_open when not) · status. A row that is
unverified or error is reported as not done, with the observed state. Then, if any
verdict changed code, point at the commit; if any is hold, list what the human must
decide.
Hard rules
- Verify before replying; a verdict without opened code is
hold. - Reply, react, resolve, read back — for every handled thread, in that order. Missing
any one of the four means the thread is not handled. A
holdthread is the one exception: reply and read back, reactionnone, no resolve,reason_openin the report. - Never resolve a thread you did not reply to, and never resolve a
hold. - Never post through a second path (web UI, another tool) to "fix" a failed row — re-run the script or the primitive so the ledger stays true.
- Never print tokens; the script masks known token shapes in output and errors.
| Rationalisation | Reality |
|---|---|
| "The bot is usually right, I'll accept it" | The 2026 !5581 findings looked right and were blocked upstream by validation. Open the code. |
| "I replied, that's enough" | An open thread is open review debt; the MR still shows it as blocking. Resolve or say why not. |
| "It's out of scope, so I'll just resolve it silently" | The reviewer learns nothing and the author cannot see why. Reply with the scope reason, then resolve. |
| "The POST returned 201" | The ledger's observed block is the deliverable. Read back or report unverified. |
| "This reviewer doesn't learn from reactions" | The reaction is also the author's and the next reviewer's signal. Add it unless the user opts out. |