Skip to content
Back to skills

Workflow Address Feedback

ASecurity

Use when a PR owner wants to address review feedback — fetches outstanding threads and general comments, presents a per-item triage (ADDRESSED / CLARIFIED / DEFERRED), applies fixes via the Edit tool, commits via workflow-commit-and-pr, resolves review threads via GraphQL resolveReviewThread (those without a thread are never resolved), and syncs stale PR metadata when drift is detected.

  • 3 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 28, 2026
developmentgobashnodegitapidatabase

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned October 5, 2026

npx -y skills add lugassawan/swe-workbench --skill workflow-address-feedback --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Workflow Address Feedback?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Workflow Address Feedback
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/lugassawan-workflow-address-feedback/badge)](https://www.skillsdirectory.com/skills/lugassawan-workflow-address-feedback)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: workflow-address-feedback
description: Use when a PR owner wants to address review feedback — fetches outstanding threads and general comments, presents a per-item triage (ADDRESSED / CLARIFIED / DEFERRED), applies fixes via the Edit tool, commits via workflow-commit-and-pr, resolves review threads via GraphQL resolveReviewThread (those without a thread are never resolved), and syncs stale PR metadata when drift is detected.
orchestrator: true
---

# Workflow: Address Feedback

**Announce at start:** "I'm using the workflow-address-feedback skill to address review feedback on PR #N."

## When to invoke

- The user runs `/swe-workbench:address-feedback <N>`.
- A PR owner wants to systematically work through review threads.
- Phrases: "address the feedback on PR 123", "help me resolve review comments", "triage and fix the review threads on #456".

## When NOT to invoke

- The reviewer side of the loop → use `swe-workbench:workflow-pr-review` (first-pass or followup mode).
- The user just wants to reply to a single comment without the full triage flow.
- The PR is closed/merged.

## Composition

This skill orchestrates:
- `swe-workbench:ticket-context` — prepended context when PR references a ticket.
- `swe-workbench:workflow-commit-and-pr` — invoked after all ADDRESSED fixes are applied to commit and push.

## Phase flow
### Phase 1 — Pre-flight + fetch
```bash
command -v swe-workbench-address-feedback-fetch >/dev/null 2>&1 || {
  echo "swe-workbench runtime commands not on PATH — reinstall or update the swe-workbench plugin." >&2
  exit 1
}
RESULT=$(swe-workbench-address-feedback-fetch --pr "$PR" \
  | swe-workbench-result-check swb.address-feedback-fetch/1) || exit 1
STATE=$(printf '%s' "$RESULT" | jq -r '.data.state')
JSON=$(printf '%s' "$RESULT" | jq -r '.data.pr_json_path')
[ "$STATE" = "OPEN" ] || { swe-workbench-clean-state-files "$JSON"; echo "PR #$PR is $STATE — address-feedback only applies to open PRs."; exit 1; }
OWNER=$(printf '%s' "$RESULT" | jq -r '.data.owner')
REPO=$(printf '%s' "$RESULT" | jq -r '.data.repo')
AUTHOR_LOGIN=$(printf '%s' "$RESULT" | jq -r '.data.author_login')
CURRENT_USER=$(printf '%s' "$RESULT" | jq -r '.data.current_user')
PR_BRANCH=$(printf '%s' "$RESULT" | jq -r '.data.pr_branch')
THREADS_PATH=$(printf '%s' "$RESULT" | jq -r '.data.threads_path')
PR_COMMENTS_PATH=$(printf '%s' "$RESULT" | jq -r '.data.pr_comments_path')
TRIAGE_PATH=$(printf '%s' "$RESULT" | jq -r '.data.triage_path')
RESUME_TRIAGE_PATH=$(printf '%s' "$RESULT" | jq -r '.data.resume_triage_path')
ELIGIBLE_THREADS=$(printf '%s' "$RESULT" | jq -r '.data.eligible_threads')
SKIPPED_THREADS_CLARIFIED=$(printf '%s' "$RESULT" | jq -r '.data.skipped_threads_clarified')
ELIGIBLE_PR_COMMENTS=$(printf '%s' "$RESULT" | jq -r '.data.eligible_pr_comments')
SKIPPED_PR_COMMENTS=$(printf '%s' "$RESULT" | jq -r '.data.skipped_pr_comments')
RUN_DIR=$(swe-workbench-new-run-dir address-feedback "$PR")
```
`swe-workbench-address-feedback-fetch` handles `gh auth status`, fetches the PR JSON to `$JSON` (via `swe-workbench-preflight-pr`), and — when the PR is OPEN — paginates review threads and PR-level conversation comments, projecting `eligible`/`skip_reason` onto each entry (resolved/already-clarified for threads; bot/owner/marker/manual-reply exclusion for PR comments) before writing them to `$THREADS_PATH`/`$PR_COMMENTS_PATH`. The `[ "$STATE" = "OPEN" ]` gate runs immediately after the fetch and before `$RUN_DIR` is allocated, so a rejected PR reaps `$JSON` inline via `swe-workbench-clean-state-files` rather than leaking it — `$RUN_DIR` never exists on this path, so there is nothing else to reap. `new-run-dir.sh` allocates `$RUN_DIR` — a mode-0700 scratch directory under `/tmp/swe-workbench-run/` for this run's own ad-hoc bash artifacts, distinct from the deliberate PR-keyed state files above (including `$TRIAGE_PATH`, which is a cross-invocation resume point and must never move here). All four state paths are repo-scoped: the fetch resolves the owner-repo slug itself — explicit `--repo` when the invocation carried a full PR URL, else the checkout's origin remote, else legacy un-scoped names — so same-numbered PRs in different repositories never collide.

If `CURRENT_USER != AUTHOR_LOGIN`, warn:
> "You are not the PR author (PR author: @AUTHOR_LOGIN, you: @CURRENT_USER). Address-feedback flows are typically owner-side. Continue anyway? Reply `yes` to proceed."

Wait for confirmation before continuing. If the user declines, run **Phase 7 — Cleanup** and exit.

If `$ELIGIBLE_THREADS` and `$ELIGIBLE_PR_COMMENTS` are both zero, nothing is left to triage — one merged check, replacing two separate early-exits the pre-runtime-command version had (this now runs before Phase 2 ever spins up a worktree, unlike before):
- `$SKIPPED_THREADS_CLARIFIED` or `$SKIPPED_PR_COMMENTS` is non-zero: print "No new items to triage — N already clarified/handled." (`N` = their sum).
- Otherwise, when some threads existed but were all resolved (nothing was skipped as already-clarified/-handled): print "No new items to triage."
- Otherwise, when `$ELIGIBLE_PR_COMMENTS` is zero and no threads existed at all (`jq 'length' "$THREADS_PATH"` is zero): print "No open threads — nothing to address."

Then run **Phase 7 — Cleanup** and exit.

If the envelope's `resume_available` is true, offer to resume — reading the saved decisions from `$RESUME_TRIAGE_PATH`, which the fetch resolves to wherever the data actually lives (the scoped `$TRIAGE_PATH`, or a pre-upgrade session's legacy un-scoped `/tmp/swe-workbench-address-feedback/${PR}-triage.json`, dual-read). New saves always go to `$TRIAGE_PATH`, so a resumed session migrates to the scoped spelling on its next save.

### Phase 2 — Worktree

Acquire the worktree via the runtime command — it owns the reuse-current / reuse-existing / create-via-rimba / create-via-git decision, the `origin/$PR_BRANCH` fetch, the fast-forward-or-diverged-warn reconcile, and `rimba deps install`:
```bash
RESULT=$(swe-workbench-address-feedback-worktree acquire --pr "$PR" --branch "$PR_BRANCH" \
  | swe-workbench-result-check swb.address-feedback-worktree-acquire/1) || exit 1
WT=$(printf '%s' "$RESULT" | jq -r '.data.path')
CREATED_WT=$(printf '%s' "$RESULT" | jq -r 'if .data.reused then "false" else "true" end')
printf '%s' "$RESULT" | jq -r '
  if .data.reused then "Reusing worktree (" + .data.reuse_reason + ") at " + .data.path
  else "Created worktree at " + .data.path end'
[ "$(printf '%s' "$RESULT" | jq -r '.data.dirty')" = "true" ] && \
  echo "Note: working tree has uncommitted changes; the user may stash before Phase 4 commits to avoid sweeping unrelated edits into the feedback commit."
[ "$(printf '%s' "$RESULT" | jq -r '.status')" = "partial" ] && \
  printf '%s' "$RESULT" | jq -r '.warnings[] | "⚠ " + .message'
```

This worktree is **disposable but sits on the PR branch itself** — Phase 4 commits and pushes from it update the PR directly, so the work lives on the PR branch, not a throwaway task branch. Phase 7 removes the worktree on every exit (success, Q-quit, or error) but **keeps the local `$PR_BRANCH`**: it is the owner's actual PR head branch, and keeping it preserves unpushed commits if a prior run crashed mid-Phase-4 (the next run's `acquire` checks that branch out instead of re-creating it). Never delete `$PR_BRANCH` (e.g. via `git branch -D`) — `swe-workbench-address-feedback-worktree release` never issues one. If the skill exits with an unrecoverable error at any point after this phase, run Phase 7 before stopping.

### Phase 3 — Triage digest

Read `$THREADS_PATH` and `$PR_COMMENTS_PATH` (`jq '[.[] | select(.eligible)]'` on each) and render only the `eligible == true` entries, one by one — the fetch command already applied the resolved/already-clarified exclusion for threads and the bot/owner/marker/manual-reply exclusion for PR comments, so Phase 3 never re-implements those rules itself.

If `$SKIPPED_THREADS_CLARIFIED` or `$SKIPPED_PR_COMMENTS` is non-zero, print transparency notes before the digest — this dedup is lossy by construction, so a transparency note replaces silently dropping:
> "(N thread(s) skipped — already clarified.)"
> "(N PR comment(s) skipped — already handled.)"

For each remaining thread:
```
─────────────────────────────────────────────────
Thread #ID — {path}:{line}  by @{author}  [{Severity if parseable}]
─────────────────────────────────────────────────
> {first 200 chars of comment body}

[A]ddressed — fix + commit + reply + resolve
[C]larified — reply + resolve
[D]eferred — reply + resolve (acknowledged, not fixed now)
[Q]uit — save progress and exit
```
Parse severity from the comment's leading `**<Severity>**` headline (the layout `swe-workbench-pr-review-submit` renders) or a legacy `Severity: <level>` prefix in the comment body if present; otherwise label `Unknown`.

Capture: `triage[<thread_id>] = A|C|D`.

For each remaining PR comment (no `path:line`, no resolve state), key as `triage["prcomment:<comment.id>"]` (namespaced against review-thread node IDs in the same flat map; carries the id needed for the Phase 5 marker; both key kinds round-trip through Q-quit save/resume unchanged):
```
PR comment by @{author}
> {first 200 chars of comment body}

[A]ddressed — fix + commit + reply (PR comments have no thread to resolve)
[C]larified — reply only
[D]eferred — skip this comment
[Q]uit — save progress and exit
```
If the owner replies `Q` at any point in either loop, save triage state to `$TRIAGE_PATH`, then run **Phase 7 — Cleanup**, and exit. Re-invocation resumes from this file (Phase 2 re-creates the worktree).

### Phase 4 — Implement + commit

For each `ADDRESSED` review thread or PR comment (in order — both sources share this loop, since the commit step is source-agnostic):

1. Show the finding and the relevant file/line context (PR comments have no `path:line`; show the comment body instead).
2. Ask the owner for the fix approach (free-text). If the comment already carries a suggested fix — a `**Suggested fix:**` paragraph (the layout `swe-workbench-pr-review-submit` renders; the fix is the rest of that paragraph — or, when a fenced block opens the value, the paragraph after the label — code fences included, up to the next label or the end of the comment) or a legacy `### Suggested fix` block — offer to apply it automatically via the Edit tool.
3. Apply edits using the Edit tool.

After all `ADDRESSED` fixes are applied, invoke `swe-workbench:workflow-commit-and-pr` with the prompt:
> "commit and push these fixes addressing review feedback on PR #N"

This reuses the existing `[type]` commit format, branch-naming check, and push logic. After the skill returns, capture the resulting commit SHA:
```bash
FIX_SHA=$(git -C "$WT" rev-parse HEAD)
```

### Phase 5 — Reply + resolve

For each **ADDRESSED**, **CLARIFIED**, or **DEFERRED** review thread, post a reply via REST then resolve via GraphQL `resolveReviewThread` by calling `swe-workbench-reply-and-resolve` with the triage-mapped args — all three dispositions now resolve the thread, since an open thread blocks `/swe-workbench:review`'s approval gate regardless of *why* it was left open. For each **ADDRESSED** or **CLARIFIED** PR comment, post a reply via REST — PR comments have no thread, so resolve is always suppressed and `KIND=issue` is passed explicitly, with a hidden `swe-workbench:handled:{id}` marker embedded in the reply body for Phase 1's re-run dedup; DEFERRED PR comments are skipped entirely, unchanged. Reply targets the thread root comment (`comments.nodes[0].databaseId`), never a subsequent reply. Full reply-body templates, exact invocation args, and the PR-comment quoting/escaping caveat live in `reference/resolve-review-threads.md`.

After all replies and resolutions land, emit the follow-up CTA:

> "Want me to ping the reviewer to re-check? Reply `yes` to run `/swe-workbench:review --check-followup <N>`."

On the Phase 5 success path, delete the triage resume-point file. The three run-scoped state files (`$JSON`, `$THREADS_PATH`, `$PR_COMMENTS_PATH` — repo-scoped per) are reaped by **Phase 7** on every exit instead — `$TRIAGE_PATH` is reaped here specifically, because completion is what makes the resume point spent, and Phase 7 must never touch it (see Phase 7). The reap runs foreground; failures surface (no `2>/dev/null`):
```bash
swe-workbench-clean-state-files "$TRIAGE_PATH"
[ -e "$TRIAGE_PATH" ] \
  && echo "⚠ state file NOT reaped: $TRIAGE_PATH" >&2 \
  || echo "✓ state file reaped: $TRIAGE_PATH"
```
Then run **Phase 6 — Sync PR metadata**.

### Phase 6 — Sync PR metadata (when fixes were committed)

Skipped when `$FIX_SHA` is unset (no fixes committed in Phase 4). Otherwise fetches the live PR title/body, commit subjects, and diff stat, judges drift against the title and `## Summary` section — the title side of that judgment starts from a mechanical scope-tag check (`swe-workbench-pr-title-drift`; a `pure_extra`/`missing`/`mixed` verdict is confirmed drift whose `suggested_title` seeds the revision) — and — on detected drift, after a `Reply \`yes\`` preview gate — completely re-writes/rephrases the PR title (if needed) and the `## Summary` section to keep them clean (instead of just appending updates) via `swe-workbench-sync-pr-metadata` while preserving `## Test Plan`, the `Closes #`/`Fixes #`/`Issue: N/A` trailer, and all other sections. Full fetch commands, drift-judging criteria, and apply mechanics live in `reference/sync-pr-metadata.md`. Then run **Phase 7 — Cleanup**.

### Phase 7 — Cleanup (always)

Run on every exit after the Phase 1 preflight line. Skip the worktree-removal block on Phase 1 early-exits (before any worktree exists) — `$WT` is unset there since Phase 2 never ran. Otherwise call `release`, which itself no-ops when `$CREATED_WT` is `"false"` (a reused worktree was never created, so there is nothing for it to remove) — the runtime command's own `--created` flag carries that distinction now, replacing the old skill-level `REUSED_WT` variable.
```bash
if [ -z "${WT:-}" ]; then
  echo "No worktree was created this run — skipping worktree cleanup."
else
  RELEASE_RESULT=$(swe-workbench-address-feedback-worktree release \
    --pr "$PR" --path "$WT" --branch "$PR_BRANCH" --created "$CREATED_WT" \
    | swe-workbench-result-check swb.address-feedback-worktree-release/1) || RELEASE_RESULT=""
  if [ -z "$RELEASE_RESULT" ]; then
    echo "⚠ swe-workbench-address-feedback-worktree release failed — worktree at $WT may need manual cleanup."
  elif [ "$(printf '%s' "$RELEASE_RESULT" | jq -r '.data.removed')" = "true" ]; then
    echo "Cleaned up worktree at $WT (local branch kept)."
  elif [ "$(printf '%s' "$RELEASE_RESULT" | jq -r '.status')" = "ok" ]; then
    echo "Reused existing worktree at $WT — skipping cleanup (nothing was created)."
  else
    echo "⚠ release did not remove $WT — see warnings below."
    printf '%s' "$RELEASE_RESULT" | jq -r '.warnings[] | "⚠ " + .message'
  fi
fi
[ -n "${RUN_DIR:-}" ] && { swe-workbench-reap-run-dir "$RUN_DIR"; [ -e "$RUN_DIR" ] && echo "⚠ run dir NOT reaped: $RUN_DIR" >&2 || echo "✓ run dir reaped: $RUN_DIR"; }
swe-workbench-clean-state-files "$JSON" "$THREADS_PATH" "$PR_COMMENTS_PATH"
for f in "$JSON" "$THREADS_PATH" "$PR_COMMENTS_PATH"; do
  [ -e "$f" ] && echo "⚠ state file NOT reaped: $f" >&2 || echo "✓ state file reaped: $f"
done
```
Cleanup is **failure-tolerant**: `release` always exits 0, and a genuine removal failure surfaces as a warning rather than blocking completion. `release` is path-keyed and never issues a branch-deleting command, so `$PR_BRANCH` — the owner's actual PR head branch — can never be destroyed by this step. `$RUN_DIR` is reaped unconditionally whenever it exists, independent of whether Phase 2 reused an existing worktree. It is guarded with `${RUN_DIR:-}` because the Phase 1 `STATE`-gate rejection is the one exit that precedes `$RUN_DIR`'s allocation entirely — that path reaps `$JSON` inline instead of routing through this phase (see Phase 1) and never reaches this guard. `$JSON`/`$THREADS_PATH`/`$PR_COMMENTS_PATH` are the paths the Phase 1 fetch envelope named — reaping whatever the manifest names, rather than re-hardcoding the three literals, keeps this list and `swe-workbench-address-feedback-fetch`'s own output in permanent sync. These are the state files whose lifetime ends with the run itself; the resume-point file created on Q-quit is reaped separately, on completion, in Phase 5 — never here, since a Q-quit into this phase must leave it intact for the next invocation to resume from.

## Failure modes

| Failure | Signal | Action |
|---|---|---|
| `gh auth` fails | Non-zero exit | Abort. |
| PR not found | `gh pr view` fails | Abort. |
| `CURRENT_USER != AUTHOR_LOGIN` | JSON mismatch | Warn + ask to continue. |
| No outstanding threads | GraphQL returns 0 unresolved | Print "No open threads — nothing to address." Exit. |
| Owner picks Q mid-triage | Loop exit | Save triage state to `$TRIAGE_PATH`, run Phase 7 cleanup, then exit. |
| Worktree removal fails | `release`'s `data.removed` is `false` | `release` still exits 0 with `status: "partial"` and a `warnings` entry — log it; do not block. |
| Reply REST fails (404 — comment deleted) | HTTP 404 | Skip that thread, log "skipped (comment deleted)". |
| Resolve mutation fails | GraphQL error | Reply already posted — log "reply posted but resolve failed". Continue. Do not roll back the reply. |
| rimba re-prefixes a non-conventional PR branch name (worktree on `feature/<name>` ≠ `$PR_BRANCH`) | `acquire`'s post-create branch verification mismatch | Handled internally by `acquire`: removes the mis-branched worktree and falls back to a plain `git worktree add`. |
| Fork PR — branch exists only on the fork remote | `git fetch origin "$PR_BRANCH"` fails / `origin/$PR_BRANCH` missing | Out of scope: add the fork remote manually (`git remote add gh-fork-<owner> …`) and check out the PR head by hand, then re-run. |

## Common mistakes

| Mistake | Fix |
|---|---|
| Create a new worktree when already on the PR branch or when one already exists | `acquire` runs the reuse-current and reuse-existing checks internally before ever creating anything — Phase 2 never needs to reimplement them. |
| Re-implement `--skip-deps`/`--skip-hooks` handling in Phase 2 | `acquire` never exposes either flag — it always installs deps so the worktree is fully initialized with no separate bootstrap step. |
| Create the worktree on a throwaway task branch | `acquire` always creates on `$PR_BRANCH` itself, never a throwaway task branch — Phase 4 pushes update the PR directly. |
| Leave the worktree behind after skill exits | Phase 7 runs on every exit past the Phase 1 preflight line — including exits before Phase 2 ever ran — but its worktree-removal block only fires when `$WT` is actually set; a Phase 1 early exit (`$WT` unset) skips removal, and `release --created "$CREATED_WT"` itself no-ops for a reused worktree. |
| Deleting `$PR_BRANCH` directly in Phase 7 cleanup | `release` is path-keyed and never issues a branch-deleting command — `$PR_BRANCH` is the real PR head branch and can never be destroyed by this step. |
| Post the reply before the commit | Always commit first (Phase 4) so `$FIX_SHA` is available for the ADDRESSED reply template. |
| Leave a CLARIFIED/DEFERRED **thread** open after reply | All three review-thread dispositions now resolve — an open thread blocks `/swe-workbench:review`'s approval gate. PR comments are unaffected (no thread to resolve). |
| Try to resolve via REST | Thread resolution is GraphQL-only (`resolveReviewThread` mutation). REST has no equivalent endpoint. |
| Re-present a thread the owner already clarified | On re-runs, skip *unresolved* threads that already have a comment authored by `$CURRENT_USER`. Detect via `comments.nodes[*].author.login`. |

Files in this skill

  • SKILL.md13.4 KB
  • triggers.txt303 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.