resolve-feedback — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited resolve-feedback (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Fetch the unresolved review threads on an open change request, triage each one with the author, then act: change the code, reply on the thread, resolve it — in whatever combination each thread calls for. The goal is resolution: every thread ends fixed, answered, or resolved, not merely acknowledged. This closes the loop that /anchor:prepare-review opens: prepare-review routes reviewer attention out; resolve-feedback brings their findings back into the branch and drives each one to done.
CR = change request: a pull request on GitHub, a merge request on GitLab. Pick the forge tool by the origin remote.
Don't narrate your work. Every step below is an operating instruction, not a script to read aloud — follow the execute-quietly discipline: ${CLAUDE_PLUGIN_ROOT}/guides/execute-quietly.md. For this skill, the only things worth surfacing are the resolved repo and CR in one line, each thread's triage, the reply bodies awaiting the user's approval in 3c, and what changed on each thread.
%%{ init: { 'look': 'handDrawn' } }%%
flowchart TD
Start(["/resolve-feedback"]) --> Repo["Resolve repo + CR"]
subgraph "Step 1: Fetch"
Repo --> Fetch["Fetch unresolved threads"]
Fetch --> Any{Any feedback?}
Any -->|No| Stop([Nothing to address])
end
subgraph "Step 2: Triage"
Any -->|Yes| Present["Present threads + proposed dispositions"]
Present --> Confirm{Author confirms?}
Confirm -->|Adjust| Present
end
subgraph "Step 3: Act"
Confirm -->|Proceed| Fix["Apply code changes"]
Fix --> Tests["Run tests"] --> Commit["Review diff + message, then commit + push"]
Commit --> Draft["Draft the replies"]
Draft --> Approve{Author approves wording?}
Approve -->|Edit| Draft
Approve -->|Post| Reply["Post on threads"]
Reply --> Resolve["Resolve where addressed"]
end
Resolve --> Summary([Per-thread summary])Settle whether the task tools exist and whether an orchestrator owns the list as ${CLAUDE_PLUGIN_ROOT}/guides/task-tracking.md describes. With the tools and no orchestrator, enumerate:
Fetch unresolved feedbackTriage with the authorApply changes and respondIf there's no unresolved feedback, mark remaining tasks deleted.
Resolve the repo as the other anchor skills do. With a name argument, resolve it with ${CLAUDE_PLUGIN_ROOT}/scripts/resolve-target.sh <name> (see the cookbook's "Resolving a named target repo"): TARGET_VIA=resolved → use TARGET_LOCAL as the checkout — this skill writes commits, so it needs one; if TARGET_LOCAL is empty, ask where the checkout lives rather than proceeding. ambiguous → prompt with TARGET_CANDIDATES. cwd (no match) → fall back to a substring-match against repos the session has touched. With no argument, git rev-parse --show-toplevel from the working directory; ambiguous → ask. Run git with -C <repo> when the working directory isn't the target.
When the target repo isn't the working directory, the forge commands below (fetch, reply, resolve) also default to the cwd repo — retarget each:
gh pr view, glab mr view) — add -R <owner/name>.-R; it expands :fullpath from the current gitdir. Substitute the URL-encoded project path for :fullpath (e.g. group%2Fproject), and add --hostname <host> for self-hosted GitLab.
Derive owner/name and the host once from git -C <repo> remote get-url origin (or, with a CR URL argument, from the URL itself).
With a CR URL argument, derive everything from it: the forge host, the project path, and the CR number. If the repo isn't already local, ask the user where the working copy lives — this skill writes commits, so it needs one.
Resolve the open CR for the branch (when no URL was given):
# GitLab
glab mr view --output json 2>/dev/null | jq '{iid, web_url, draft, sha}'
# GitHub
gh pr view --json number,url,isDraft,headRefOid 2>/dev/nullNo open CR → say so and stop; there's nothing to address.
Confirm local state matches the CR head (same check as prepare-review): git status --porcelain clean, and local HEAD equals the CR head SHA. If they disagree, surface the mismatch and stop — replies that say "fixed in <sha>" must reference commits that actually contain the fix on top of what the reviewer saw.
Pull every unresolved, human-authored thread. Canonical invocations live in the bundled forge cookbook (${CLAUDE_PLUGIN_ROOT}/guides/forge-cookbook.md, section "List unresolved review threads"); in short:
glab api "projects/:fullpath/merge_requests/<iid>/discussions?per_page=100",filtered to non-system discussions with at least one resolvable note that isn't resolved. Keep each discussion's id, authors, bodies, and position (new_path, new_line) when line-anchored.
reviewThreads query (REST doesn't exposeresolution state), filtered to isResolved: false. Keep the thread id, path/line, and each comment's databaseId, author, body.
Also fetch top-level CR comments that ask for changes without anchoring to a line (GitLab: notes with system == false not already part of a discussion; GitHub: gh pr view --json comments,reviews review bodies with CHANGES_REQUESTED or non-empty text). Reviewers often put the biggest asks there.
If there is no unresolved feedback, report that and stop.
Present every thread in one table, ordered by file/line, each with a proposed disposition:
| # | Where | Reviewer | Ask (summarized) | Proposed |
|---|---|---|---|---|
| 1 | src/deploy.sh:42 | @reviewer | rename flag for clarity | fix + reply + resolve |
| 2 | taskdef.yml:7 | @reviewer | why not Fargate? | reply only |
Disposition vocabulary:
reply with what changed and the commit SHA, resolve the thread.
reserve resolution for the person who opened the thread — follow the project's convention; when unknown, resolving your own addressed threads is the common default on GitLab, leaving them open is safer on teams you don't know).
the asker decides whether it's settled. Never resolve a question you answered but the asker hasn't acknowledged.
where it lands (issue link, follow-up CR) — don't leave it unanswered.
Then confirm with the author before acting (use AskUserQuestion, header Triage): Proceed as proposed / Adjust (they name the thread numbers and new dispositions) / Abort. This gate settles what happens to each thread; the wording of what gets said goes to the author separately in 3c, once there are actual sentences to read.
Work the dispositions in this order — code first, then talk:
Make the edits for every fix disposition. Group related fixes into one commit; unrelated concerns get separate commits so each reply can cite a focused SHA.
Keep each fix within the changeset's existing scope — the bundled guide (${CLAUDE_PLUGIN_ROOT}/guides/changeset-scope.md) has the bar and the surface-and-confirm move.
If the author flags something in the CR description as worth keeping, fold it into the repo's docs as part of the fix commit — the bar and the adaptation rules are in the bundled guide (${CLAUDE_PLUGIN_ROOT}/guides/description-vs-docs.md).
Run the project's test suite (same detection, exit-code gate, and silence on a pass as the "Run tests" section of ${CLAUDE_PLUGIN_ROOT}/skills/commit/SKILL.md); a failing suite blocks the push, no exceptions. Then commit as new commits — never amend what the reviewer has seen: a CR with feedback on it is being read, so the "changes since you last looked" diff is load-bearing regardless of draft state. Subject names the concern, body cites each thread it addresses:
Rename --force to --skip-validation
Addresses review feedback from @reviewer on deploy.sh:42 — "force"
implied more than the flag does.Each round of feedback lands as one commit through one review, the same one /anchor:commit runs (${CLAUDE_PLUGIN_ROOT}/skills/commit/SKILL.md, from "Stage and read changes" through "Commit and push"). Its message and its code both publish under the user's name, and the Step 2 triage approved what to change, not these words or these lines:
commit-preflight.sh --path <p>....$(mktemp -u /tmp/commit-msg.XXXXXX).md.--message-file <path> --path <p>..., printing the manifest and, in diff mode, the message verbatim (${CLAUDE_PLUGIN_ROOT}/guides/execute-quietly.md`).
section does: only approved commits; no-verdict walks the fallback ladder with the message printed in the reply.
--reviewed-index <REVIEW_INDEX> --path <p>... (plain push, since the branch only gains commits). Never --force-with-lease` here.
That push starts a fresh pipeline on the fix, and telling the reviewer their feedback is addressed reads differently if it went red. Launch the watch as ${CLAUDE_PLUGIN_ROOT}/guides/watching-a-pipeline.md describes, so it polls while you carry on with 3c through 3e:
bash "${CLAUDE_PLUGIN_ROOT}/scripts/pipeline-after-push.sh" --skill resolve-feedbackIts verdict belongs in Step 4's summary; read it there.
A reply posts through the user's own token, so it lands under their name with nothing marking it as drafted by an agent. The reviewer reads it as the user talking. That makes the words the user's to approve before they go out: a fix they'd have written differently is still a fix, but a sentence they wouldn't have written is one they now have to own on a thread other people are reading.
So draft every reply, show them, and post nothing until the user says the text is right. Reply content:
addressed in <commit-url>The commit message and diff carry the detail; restating it in the thread is noise, and prose explaining why the reviewer was right reads as defensive. Add a sentence only when the fix took a different direction than the reviewer suggested.
docs should say, prefer fixing that and replying with the pointer.
Present them together, each body verbatim — the text as it will appear on the thread, not a paraphrase of it. A summary hides exactly the thing the user is being asked to approve:
| # | Where | Reply |
|---|---|---|
| 1 | src/deploy.sh:42 | addressed in <url> |
| 2 | taskdef.yml:7 | Fargate would mean rebuilding the image on every deploy — the EC2 launch type keeps the layer cache warm. |
Then ask with AskUserQuestion (header Replies): Post as drafted / Edit (they name the numbers and what to change; redraft and show the table again) / Skip replying (leave the threads for a later round). One gate for the whole set, not one per thread — the same approval asked N times reads as noise, and the user starts approving without reading.
This is not the Step 2 gate. Step 2 settles what happens to each thread while the words are still unwritten; approving a disposition is not approving a sentence. A thread that comes back in feedback with a phrasing change never returns to triage — it stays here.
Write each approved body to a unique temp file ($(mktemp -u /tmp/reply.XXXXXX).md, whose literal /tmp a caller's Edit(//tmp/**) grant reaches — ${CLAUDE_PLUGIN_ROOT}/guides/temp-paths.md) and post it into the existing thread — not as a new top-level comment (see the cookbook, "Reply to a review thread"). Post what was approved: an improvement you notice while posting goes back through 3c, because the point of the gate is that nothing reaches the thread the user hasn't read.
Resolve exactly the threads whose disposition included resolve (cookbook: "Resolve / unresolve a review thread"). Verify each resolution call returned resolved: true / isResolved: true — a silently-dropped resolution looks identical to a forgotten one.
Report one line per thread: #N <file:line> — <disposition> — <commit sha / reply posted / resolved>, plus anything deferred and where it went. If any thread was skipped, say so — the next /anchor:resolve-feedback run picks it up.
Then close with the pipeline, reading the watch launched in 3b per ${CLAUDE_PLUGIN_ROOT}/guides/watching-a-pipeline.md. If it hasn't settled yet, say the watch is still running and link the pipeline rather than waiting on it.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.