Implements review-comment suggestions across one or more PRs. Multi-PR mode (default when $ARGUMENTS holds PR URLs; empty $ARGUMENTS auto-detects the active PR) resolves a worktree per PR, fetches every actionable comment from human teammates AND AI review bots (claude[bot], coderabbitai[bot], …), validates each through /critical + /confidence, builds a structured suggestion-pack, and dispatches a worker subagent that applies each approved change as its own commit, pushes to the existing branch, and resolves the addressed thread — so every handled comment ends resolved and the PR is left clean. Free-text mode applies a single pasted suggestion in place. --watch re-applies on one PR until the reviewers go quiet (max 5 iterations); --resolve-all also replies to and resolves every non-fix thread it can honestly close. Triggers on "implement suggestion", "apply review comments", "/implement-suggestion".
66
83%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Low
Low-risk findings worth noting
Take reviewer suggestions on one or more pull requests, validate each through
adversarial review (/critical) and a confidence gate (/confidence), then
hand off a structured suggestion-pack to a worker subagent that applies
the approved changes inside the PR's worktree and pushes them — without
opening a new PR.
This skill is a thin orchestrator. The heavy reasoning lives in
/critical and /confidence. Per-PR worktree isolation comes from gw.
The mechanical apply / commit / push runs inside one dispatched worker per PR.
Source of truth. This
SKILL.mdis a thin index. Detailed procedures live inrules/*.md, literal artefacts intemplates/*.md. Load only what the current phase asks for.
Parse $ARGUMENTS once, in this order. First match wins.
| # | Signal in $ARGUMENTS | Mode |
|---|---|---|
| 1 | One or more github.com/<owner>/<repo>/pull/<n> URLs (with or without #discussion_r…) | multi-pr |
| 2 | One bare PR number (#123) and the current directory is a PR worktree | multi-pr (n=1) |
| 3 | Free-text prose, pasted comment body, or non-PR URL | free-text |
| 4 | Empty and the current branch has an open PR (auto-detected via gh pr view) | multi-pr (active PR) |
| 5 | Empty and no active PR for the current branch | Prompt the user |
Active PR auto-detection (rule #4): when $ARGUMENTS is empty, run
gh pr view --json number,url,state,headRefName,headRefOid,isDraft (no number =
current branch). If it returns a PR in state OPEN, treat as multi-pr with
that single PR. Print one line before continuing:
Mode: multi-pr Active PR: dash0/console#1234 (current branch)If the detection finds a MERGED or CLOSED PR, refuse to proceed and ask
the user to confirm explicitly.
--watch modifier (orthogonal to mode): if $ARGUMENTS contains --watch,
the run loops the single-pass on one PR until the review bots go quiet (max 5
iterations). It requires exactly one PR (multi-pr with n=1, or the active PR).
Refuse --watch with more than one PR or in free-text mode. Full procedure in
rules/watch-mode.md — load it now when --watch is set.
--resolve-all modifier (orthogonal to mode): default is off — every
caller keeps today's behaviour (non-fix comments are surfaced with their threads
left open) unless this flag is present. review-loop passes it so the loop can
converge a PR to zero open threads. When set, in addition to applying fixes the
worker gives every fetched thread an explicit disposition and closes the
ones it can honestly close:
| Disposition | Thread action |
|---|---|
Fix applied (apply) | reply Addressed in <sha> + resolve (unchanged) |
question | reply with the answer + resolve |
discussion, or an actionable/nit the gates declined | reply with the rationale (a decline) + resolve |
praise | drop silently (no thread action) |
Genuine human-judgment flag — a real potential issue the agent will not auto-apply and cannot honestly decline (e.g. a /critical Must-fix surface, or a surface-band actionable) | leave open + surface, with a reply noting why it is flagged |
The last row is the safety valve: it never force-resolves a live finding, so
this flag cannot green-wash a PR. It composes with --watch. Free-text mode
ignores it (no threads to resolve).
Full parsing rules live in rules/input-parsing.md.
State the detected mode and inputs in one line before continuing. Example:
Mode: multi-pr PRs: dash0/console#1234, dash0/console#1278Phase 0: Input parse → PR tuples (or free-text string)
Phase 1: Worktree resolution → gw checkout <pr> per PR; verify clean state
Phase 2: Comment fetch → per-PR ledger (parallel across PRs)
Phase 3: Classify → actionable / nit / discussion / praise
Phase 4: Two-gate validation → /critical → /confidence per actionable comment
Phase 5: Build suggestion-pack → .agent/<branch>/suggestion-pack.md per PR
Phase 6: Handoff → one worker per PR: commit-per-comment, full check, push, resolve thread
Phase 7: Report → per-PR table: applied / surfaced / skipped / resolvedPer-PR work in Phases 1–6 runs in parallel across PRs (one message,
multiple Agent / Bash dispatches). Per-comment work in Phase 4 runs
sequentially within a PR so /critical and /confidence see consistent
state.
Apply rules/input-parsing.md. Output a
deduplicated list of {owner, repo, prNumber, commentFilter} tuples. Validate
each via gh pr view --json number,state,headRefName,headRefOid,isDraft and
refuse to proceed for any PR in state MERGED or CLOSED.
Apply rules/worktree-resolution.md. For
each PR:
gw checkout <pr-url-or-number> (preferred).git status --porcelain is empty and HEAD == headRefOid.Hard rule: never auto-stash, never auto-rebase, never operate in the user's main worktree.
Apply rules/comment-fetching.md. Per PR,
fetch in parallel:
gh api repos/<owner>/<repo>/pulls/<n>/reviews
gh api repos/<owner>/<repo>/pulls/<n>/comments
gh api repos/<owner>/<repo>/issues/<n>/commentsBuild one ledger per PR. Include comments from both human teammates and
AI code-review bots (claude[bot], coderabbitai[bot], etc.) — only the
current user's own comments and noise bots (dependabot, github-actions)
are filtered. Exclude resolved threads. Honor commentFilter from Phase 0
if present.
A review body carrying <!-- PR_REVIEWER_REPORT --> is handled specially: it is
never self-filtered (the reviewer and this skill often share one GitHub App
identity; the same carve-out covers a pr-reviewer inline finding, recognised by
its attribution footer), and it is expanded into one ledger entry per finding — deferred
findings, gate findings, and optimality proposals all live only in that body.
The parse grammar is shared with pr-reviewer itself
(agents/shared/rules/reviewer-report-ingest.md);
the expansion contract is in
rules/comment-fetching.md § Reviewer-report expansion.
Before tagging — read prior lessons. Load the
implement-suggestion-lessons lessons from LoreKit so accumulated
misclassifications and gate mis-calibrations bias this run before they repeat.
Full contract in rules/self-improvement-loop.md#read-lessons-phase-3:
# Narrow-to-broad; silent no-op if memory.* not connected.
memory.list { scope: "repo::{owner}/{repo}", tags: ["loop::implement-suggestion-lessons"], limit: 50 }
memory.list { scope: "global", tags: ["loop::implement-suggestion-lessons"], limit: 50 }Match each lesson's Applies when line (reviewer source + topic) against the ledger. Matches are advisory inputs to Phase 3 tagging and the Phase 4 gates — they never relax the two-gate requirement or a hard rule.
Tag every comment per rules/comment-classification.md:
| Tag | Treatment (default) | Treatment under --resolve-all |
|---|---|---|
actionable | Carries to Phase 4 (validation gates). | Same; a declined one is reply-and-resolved in Phase 6 (rationale). |
nit | Carries to Phase 4; higher confidence bar applies. | Same; a declined one is reply-and-resolved in Phase 6 (rationale). |
discussion | Skipped; surfaced in Phase 7 report. | Reply with the agent's take + resolve (Phase 6). |
question | Skipped; surfaced. | Reply with the answer + resolve (Phase 6). |
praise | Dropped silently. | Dropped silently. |
Under --resolve-all, discussion / question comments are not skipped —
they are carried into Phase 5's pack as reply-only entries (no code change) so
the Phase 6 worker can answer and resolve their threads.
For each actionable / nit comment, run gates in this order — full
procedure in rules/validation-gates.md:
Skill("critical", "<mode>") — code if the comment proposes a
specific edit (suggestion block, file:line reference), analysis
otherwise. Capture findings (hidden assumptions, blast radius, steelman).Skill("confidence", "analysis") — score the change in context of the
comment, surrounding code, and /critical's findings.Decision matrix:
/confidence score | nit comment | actionable comment |
|---|---|---|
| ≥ 90% | apply | apply |
| 80%–89% | surface | apply |
| 70%–79% | surface | surface |
| < 70% | skip | skip |
A /critical finding tagged Must-fix overrides the matrix and
forces surface, even at ≥ 90%. This is non-removable — /critical's
Must-fix calls are designed to catch what /confidence cannot.
(/critical emits Must-fix / Should-fix / Nice-to-have; only Must-fix
overrides — the lower buckets are recorded in the pack.)
Write .agent/<branch>/suggestion-pack.md per PR using
templates/suggestion-pack.md. The pack is
the contract handed to the worker — it lists every apply-tagged change
with file:line, the proposed edit, the comment author and ID, and the
/critical + /confidence evidence.
The pack is plan.md-shaped intentionally: it carries an Acceptance Criteria section (one criterion per applied change) and a "Mode: existing-pr" header that signals to consumers "commit and push to the existing branch; do not open a new PR".
When --resolve-all is set, also set resolve-all: true in the pack frontmatter
and add the ## Reply-only section: one entry per question / discussion /
declined comment with its disposition (answer / discussion / decline /
flag), thread ID, and the exact reply text the worker will post. The worker's
step-6 pass consumes this section.
Once the pack names its files, read the shared codebase-knowledge signal for
exactly those paths and fold the matches into the pack as apply-time constraints (a
known hotspot → tighter change + more coverage; a recorded invariant / consumer
count → preserve it). This is the cross-bucket read specified in
rules/self-improvement-loop.md § Cross-bucket read — codebase-knowledge;
it is read-only, structural, and bounded to the pack — the worker reads no other
memory, so the pack is where these constraints reach it.
There is one lane. Every PR's pack goes to one worker — no planner in between
(rules/handoff.md § Why there is no planner lane).
Before dispatching, the pack must already order dependent comments (B builds on
A ⇒ A first); two apply comments that contradict each other are both moved to
surface, because no ordering makes both true.
For each PR, dispatch the worker subagent (one message, parallel across PRs):
Agent(
description: "Apply suggestion-pack to PR #<n>",
subagent_type: "general-purpose", # "general" on OpenCode / Agent0 — rules/handoff.md#generic-sub-agent-type
prompt: <the "Worker prompt template" from rules/handoff.md, filled in — the template is inline in that file; no external prompt file exists>
)Full dispatch contract and prompt template:
rules/handoff.md#worker-prompt-template.
The worker makes one commit per applied comment (each message cites that
comment's @author + URL), pushes once after all commits, then resolves
each addressed review thread — posting a brief Addressed in <sha> reply,
then resolveReviewThread. This leaves a clean one-to-one trail (commit →
resolved comment) and a PR where every handled comment is resolved; only
surface / skip comments stay open. issues / review-summary comments
have no resolvable thread and are reported as such. Full contract in
rules/handoff.md#worker-prompt-template.
Under --resolve-all, the worker runs one extra pass after the commit/push
pass: for every reply-only entry (question / discussion, or an actionable /
nit the gates declined) it posts a reply — the answer for a question, the
agent's take for a discussion, the decline rationale for a gated-out change —
then resolveReviewThread. Reply-only entries have no commit and are pushed
nothing. A genuine human-judgment flag is the one exception: the worker posts a
reply noting why it is flagged and leaves the thread open. This pass is
resolve-side only, so its failures are non-fatal exactly like the fix-thread
resolves. Full contract in
rules/handoff.md#worker-prompt-template.
The main agent does not edit files in Phase 6. All applies / commits / pushes / thread resolutions happen inside the worker subagent so the loud loop (test runs, push retries) stays out of the main context.
Emit one summary table:
## Implement-Suggestion Results
| PR | Branch | Applied | Surfaced | Skipped | Commits | Pushed | Resolved |
|----|--------|---------|----------|---------|---------|--------|----------|
| dash0/console#1234 | fix/foo | 3 | 1 | 2 | abc1234, def5678, 9a0bcde | ✓ | 3/3 |
| dash0/console#1278 | feat/bar | 0 | 2 | 1 | — | — | 0/0 |Commits lists one SHA per applied comment (commit-per-comment). Resolved
is <threads-resolved>/<applied-with-a-thread> — comments whose fix landed and
whose thread was resolved, over the applied comments that had a resolvable
thread (issues / review-summary comments have none and are excluded from
the denominator).
Under --resolve-all, add an Answered column: <threads-answered-and-resolved>
— question / discussion / declined comments closed with a reply (no commit).
Surfaced then counts only the human-judgment flags left open on purpose; if
Surfaced is 0, the PR has zero open threads and the caller (review-loop) can
exit. Report each still-open flag on its own line so it is never silently dropped.
Then per PR list:
/critical finding if any.
Thread left open.
After the report — write lessons. Run the retrospective and capture any
durable lesson from the run (a Phase 3 misclassification, a Phase 4 gate
mis-score, a Phase 6 apply that rippled past its own files, an apply that needed a scoped-check fix).
Full contract, tier classification, and the applied-lesson UPDATE rule in
rules/self-improvement-loop.md#write-lessons:
# Classify scope (universal → global; project-bound → repo::{owner}/{repo}), dedup, then write.
# Silent no-op if memory.* not connected.
memory.search { q: "<lesson keywords>", scopes: ["repo::{owner}/{repo}", "global"], limit: 10 }
memory.write { scope: "<global | repo::{owner}/{repo}>", key: "implement-suggestion-lessons::<slug>", value: "<body>", tags: ["loop::implement-suggestion-lessons", "source::<trigger>"], source_agent: "implement-suggestion", trigger: "<end-of-run | watch-reflag | user-override>", ttl_days: 90 }A lesson reaching seen_count >= 3 is promotion-eligible — surface the
scope-appropriate one-liner (/create-skill diagnose implement-suggestion for
global; a repo rule via docs for repo::). Never promote silently.
See rules/self-improvement-loop.md#lesson-promotion.
After writing lessons — emit outcome records to review-outcomes AND write
comment-relevance memories. These are two separate, parallel writes per processed
comment.
Write 1 — review-outcomes bus (existing): appends to the review-outcomes LoreKit bus
(tag loop::review-outcomes) — one fingerprinted outcome record per comment.
This feeds the shared candidate/outcome bus consumed by
agents/shared/rules/outcome-learning.md at promotion time.
Write 2 — reviewer-comment-relevance memory (new): writes a relevance signal
to the reviewer-comment-relevance LoreKit bucket (tag
loop::reviewer-comment-relevance) for each processed comment.
This is the primary write path that makes reviewer and pr-reviewer continuously
better on this specific repository — each resolved or dismissed comment updates
the per-repo signal so future reviews suppress recurring noise and reinforce
reliably-resolved patterns.
See agents/shared/rules/comment-relevance-memory.md § Write
for the full record schema and scope-classification rules.
Reuse the per-comment /critical + /confidence result already in context — do not recompute.
Derive verdict / relevance from the Phase 4 decision matrix:
| Phase 4 outcome | verdict (review-outcomes) | relevance (comment-relevance) | resolution_method |
|---|---|---|---|
| Gate cleared, patch landed | applied | relevant | fixed |
/critical Must-fix raised OR /confidence below threshold | rejected-at-validation | not-relevant | wont-fix |
| Gate cleared but scoped out / deferred | deferred | weak-not-relevant | ignored-at-merge |
| Patch landed then reverted after CI failure | reverted-after-ci | not-relevant | wont-fix |
For applied verdicts, also check the comment thread for explicit "won't fix"
language from the author (won't fix, by design, intentional, nwf, n/a,
👎 reaction):
gh api repos/$REPO/pulls/$PR_NUMBER/comments \
--jq ".[] | select(.in_reply_to_id == $COMMENT_ID) | .body" \
| grep -iE "(won.?t fix|wont fix|by design|intentional|nwf|not going to|n/a)"If a decline phrase is found, override relevance: not-relevant, resolution_method: wont-fix.
Infer source from the comment author login per the heuristic in review-outcomes.md.
# Write 1 — review-outcomes bus (unchanged).
# Append-only, non-blocking. Silent no-op if LoreKit not connected.
memory.write { scope: "<global | repo::{owner}/{repo}>", key: "review-outcomes::<fingerprint-slug>", value: "<outcome record>", tags: ["loop::review-outcomes", "source::<verdict>"], source_agent: "implement-suggestion", trigger: "outcome-emit" }
# Write 2 — comment-relevance memory (new).
# Deduplicate first; UPDATE seen_count if exists, ADD otherwise.
# Scope: almost always repo::{owner}/{repo}; global only for universal patterns.
memory.search { q: "<fingerprint slug>", scopes: ["repo::{owner}/{repo}", "global"], limit: 5 }
# The key is EXACTLY `<category>:<claim-gist>` — never a pr#, comment id, SHA, or file:line.
# A coordinate key is unique per occurrence, so seen_count never accumulates and the signal
# is inert. Put coordinates in the record's `examples` field. Self-check: comment-relevance-memory.md § Key format.
memory.write { scope: "repo::{owner}/{repo}", key: "reviewer-comment-relevance::<category>:<claim-gist>", value: "<relevance record>", tags: ["loop::reviewer-comment-relevance", "source::<resolution_method>"], source_agent: "implement-suggestion", trigger: "outcome-emit" }LoreKit owns storage server-side and dedups on write.
The reviewer-comment-relevance bucket has a 60-day default TTL, refreshed on each sighting.
The review-outcomes bucket has a 30-day volatile TTL.
Both writes are append-only and non-blocking — they MUST NOT gate or delay the Phase 7 report.
If LoreKit's memory.* tools are not connected, both steps are silent no-ops; the apply flow is unaffected.
--watch)A loop wrapper around the multi-PR single-pass, scoped to one PR. Each iteration:
waits for new review activity, runs Phases 1–7 over comments newer than the last
processed timestamp, advances the baseline, and repeats until the reviewers go
quiet, CI goes red, or the iteration cap (default 5) is hit. Run it on purpose for
reviewers who comment after the automated passes; create-pr no longer backgrounds it.
The wait is the shared review-activity poll, never restated here.
CI is a stop reason, never a fix. After each iteration's push the loop reads
check state once; a failing check stops it with ci red — <check names> and names
the handoff. It dispatches no ci-auto-fix and spends none of the per-PR handoff
budget. Getting a PR both comment-clean and green is review-loop.
Full loop, the poll call, parameters (--max-iters, --interval), the CI-state
table, the per-iteration report, and watch-specific hard rules live in
rules/watch-mode.md.
Inside each --watch iteration, after the per-iteration Phase 7 report:
run the outcome-emit step (see above) for every comment processed in that iteration.
This includes both the review-outcomes bus write AND the reviewer-comment-relevance memory write.
This ensures that reverted-after-ci verdicts and relevance signals are captured at the end of the
iteration where CI failure is detected.
Both emits are append-only and non-blocking in each iteration.
When $ARGUMENTS is prose, a pasted comment, or a single comment permalink
without a PR worktree context:
Free-text mode preserves v1 behaviour: a quick "implement this colleague suggestion I just pasted" path with no PR plumbing.
/implement-suggestion gets better across runs through a two-tier lessons loop
(fast episodic tier + gated promotion), identical in shape to
autonomous-workflow and fix-bug. It reads implement-suggestion-lessons at Phase 3
and writes at Phase 7 (and on a --watch re-flag), keyed by reviewer source
seen_count >= 3) is promotion-eligible to a permanent skill guard
via /create-skill diagnose implement-suggestion.The loop owns implement-suggestion's decision phases and the worker's apply
outcomes; there is no planner dispatch contributing elsewhere.
LoreKit (the lorekit-memory skill's memory.* tools) is an optional
companion: if those tools are not connected the whole loop is a silent no-op.
Full contract:
rules/self-improvement-loop.md.
In addition to writing implement-suggestion-lessons, this skill is a producer of two LoreKit buckets:
review-outcomes shared candidate/outcome bus (see agents/shared/rules/review-outcomes.md) — volatile 30-day TTL; consumed by outcome-learning.md at promotion time only.reviewer-comment-relevance memory bucket (see agents/shared/rules/comment-relevance-memory.md) — durable 60-day TTL; consumed by reviewer and pr-reviewer at the start of every review run (Step 0.7 / Step 1.0) to suppress recurring noise and reinforce reliably-resolved patterns. This is the primary mechanism by which the reviewer pipeline continuously improves on a specific repository.At Phase 7 (and per-iteration inside --watch), one fingerprinted record is written to each bucket
per processed comment.
Both writes are append-only and non-blocking.
The reviewers consume review-outcomes only at promotion/consolidation time; they consume reviewer-comment-relevance on every review run.
--force or --force-with-lease without explicit user approval.--no-verify or bypass hooks./critical review surfaced a Must-fix finding without surfacing first.git log maps one-to-one to resolved threads.resolveReviewThread. surface / skip comments — and any comment whose
commit did not land — stay open. Never resolve a thread whose fix is not on the remote.--resolve-all never green-washes. The extra reply-and-resolve pass closes
only threads it can honestly close (fix landed, question answered, discussion
taken, change explicitly declined with a rationale). A real potential issue the
agent will not auto-apply and cannot honestly decline stays open and is
surfaced — the same invariant as thread-resolution.md's "never resolve a
persisting/unaddressed finding". Resolving to make a loop terminate is forbidden.gw worktree.gh pr create.| Dependency | Purpose | Required? |
|---|---|---|
gh CLI (authenticated) | Fetch comments, resolve PR metadata | Yes for multi-PR |
gw CLI | Worktree creation and reuse per PR | Strongly recommended |
git | Commit + push | Yes |
/critical skill | Adversarial pre-mortem per comment | Yes |
/confidence skill | Gate scoring per comment | Yes |
lorekit-memory skill (LoreKit memory.* tools) | implement-suggestion-lessons self-improvement loop (read Phase 3, write Phase 7 / watch re-flag); reviewer-comment-relevance per-repo relevance memory (write Phase 7 / watch); review-outcomes bus (write Phase 7 / watch) | Optional — all three loops are silent no-ops if not connected |
If gh is missing in multi-PR mode, stop and tell the user to install it.
| Rule | When it loads |
|---|---|
input-parsing | Phase 0 |
worktree-resolution | Phase 1 |
comment-fetching | Phase 2 |
comment-classification | Phase 3 |
validation-gates | Phase 4 |
handoff | Phase 6 — worker dispatch + prompt |
watch-mode | When --watch is set — the post-push feedback loop |
self-improvement-loop | Cross-cutting — implement-suggestion-lessons fast tier (read Phase 3 / write Phase 7 + watch re-flag) + promotion to diagnose |
Templates:
suggestion-pack.md — the per-PR pack written in Phase 5.| Skill | Relationship |
|---|---|
ci-auto-fix | Owns red CI. This skill never fixes it. The worker's pre-push checks (Phase 6) are local and gate the push; post-push check state is only ever read as a stop reason under --watch. No ci-auto-fix dispatch happens here, and none of its 2-handoff budget is spent. |
review-loop | The composition point. "Apply the review comments and get CI green" is review-loop (or polish), which sequences pr-reviewer → this skill → polish simplify → ci-auto-fix. Invoked standalone, this skill leaves red CI to the caller — deliberately. |
pr-reviewer | Upstream producer of the findings this skill consumes; read-only, never invoked from here. |
/critical + /confidence call.
Workers only apply pre-validated changes./critical runs first so its findings feed /confidence./critical + /confidence;
the worker's unscoped pre-push check catches the cross-file ripple a planner was meant to
foresee. See rules/handoff.md § Why there is no planner lane.implement-suggestion-lessons (read Phase 3, write Phase 7)
biases classification, gate calibration, and pack ordering from prior runs — but a lesson
never relaxes a gate or a hard rule. Only a recurrence-proven lesson (seen_count >= 3) earns
a confidence-gated, user-approved change to the skill's source.pr-reviewer body carries findings that exist
nowhere else — gate findings have no inline anchor, optimality proposals are never posted
inline, and deferred findings only ever appear in the body. Phase 2 expands it into one entry
per finding and never self-filters it. Everything expanded still runs the full pipeline: the
reviewer's own confidence score is evidence for Phase 4, never a way around it.39b3f44
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.