Skip to content

Add /swarm:review --pr: review a PR diff and post the result#29

Merged
gering merged 5 commits into
mainfrom
task/swarm-post-findings-to-pr
Jul 13, 2026
Merged

Add /swarm:review --pr: review a PR diff and post the result#29
gering merged 5 commits into
mainfrom
task/swarm-post-findings-to-pr

Conversation

@gering

@gering gering commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Summary

  • New opt-in --pr [<number>] flag on /swarm:review: run the local mixture-of-agents ensemble against a GitHub PR's diff and post the surviving findings as a PR comment — the codex/grok/Claude review voice on GitHub with no CI setup, repo secrets, or API-token cost.
  • The pipeline is reused unchanged; only the diff source (step 1) and a new confirm-then-publish step (step 5) are added — the workflow script is untouched.
  • Posting is outward-facing: the rendered comment is shown and confirmed once before it goes out.

Changes

  • plugins/swarm/skills/review/SKILL.md--pr flag parsing (read-only, mutually exclusive with --fix/--loop); a gh-backed diff-source block in step 1 (resolve PR via gh pr view, diff via gh pr diff, graceful SWARM_PR_ERR stops); step-3 routing + PR header; new step 5 (build body from output-gated findings only, ## 🐝 Swarm review marker header, confirm once, post via gh pr comment, graceful failure, pr-flow compatibility note).
  • Version — swarm 0.3.10.4.0 (minor); plugin.json + marketplace.json in sync.
  • READMEs — top-level + swarm plugin README document the new flag.
  • Knowledgeswarm-review-pipeline.md + index capture the design.

Readiness

  • ✅ Uncommitted: clean
  • ✅ README: top-level + swarm updated
  • ✅ Version: swarm 0.4.0 (marketplace in sync)
  • ➖ Changelog: N/A (no changelog in repo)
  • ✅ Knowledge: documented
  • ✅ Tests: check-structure.py green (0 errors)
  • ➖ Lint / Build: N/A (markdown-only)

Test plan

  • /swarm:review --pr <n> against a real PR resolves the diff and runs the ensemble
  • Rendered comment is shown and only posts after explicit confirmation
  • Posted comment carries the ## 🐝 Swarm review header and is not picked up by pr-flow's claude-author polling
  • --pr --fix/--loop is refused with a clear message
  • gh missing/unauthenticated, no PR, or post failure each report clearly without losing the review

🤖 Generated with Claude Code

gering and others added 5 commits July 13, 2026 13:17
Opt-in flag to run the local swarm ensemble against a GitHub PR's diff
and publish the output-gated result as a PR comment, with no CI setup,
repo secrets, or API-token cost.

- --pr [<number>]: resolve the PR (explicit number or the current
  branch via `gh pr view`), take `gh pr diff` as the review input
- Reuse the pipeline unchanged; only the diff source (step 1) and a new
  publish step (step 5) differ — the workflow is untouched
- Confirm once before posting; post via `gh pr comment` under the
  user's own identity with a `## 🐝 Swarm review` marker header
- Only output-gated findings are ever posted
- Read-only; mutually exclusive with --fix/--loop (documented)
- pr-flow compatible: posted as non-`claude` author, so
  claude-review.sh polling ignores it
- Graceful failure on missing/unauth gh, no PR, or post failure
- Bump swarm 0.3.0 -> 0.4.0; update READMEs

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GHHArYXzAy4DVYyFu15bpU
Document the PR-review-and-post feature in swarm-review-pipeline.md: the
workflow-untouched design (diff still arrives via a temp-file path, --pr
only swaps how it is filled), the pr-flow compatibility design point
(posted under the user's own gh identity, so claude-review.sh's
claude-author poll ignores it), output-gated-only posting with a single
confirm, and the --fix/--loop mutual exclusion. Update the index line.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GHHArYXzAy4DVYyFu15bpU
Fold the swarm ensemble's confirmed findings on the --pr diff:

- Restructure step 1 into ONE block with an `if REVIEW_PR` branch instead
  of a second self-contained `set -euo pipefail` script that read
  $TMPD/$DIFF from the first (would abort under set -u if run as its own
  Bash call). This also: routes --pr through the shared empty-diff guard,
  makes INCLUDE_UNTRACKED=0 actually consumed, cleans up $TMPD on every
  SWARM_PR_ERR exit, and replaces the literal PR_ARG placeholder with a
  default-safe empty string.
- Sync plugins/swarm/.claude-plugin/plugin.json description with
  marketplace.json (both advertise --pr, previously byte-identical).
- Defer step-3 $TMPD cleanup to step 5 for --pr (step 5 needs the temp
  dir for the comment body) and correct the "reuse $TMPD" wording.
- Mark the PR title as untrusted display data; harden the step-5 confirm
  gate to scan the body for injected/echoed diff content before posting.
- Fold the PR-specific report header into the step-3 render spec.
- Standardize the --pr placeholder to `[<number>]` in the top README.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GHHArYXzAy4DVYyFu15bpU
Deeper findings from the deepest-effort ensemble pass, several of them
consequences of the first review's own fixes:

- Fix REVIEW_PR/PR_ARG clobber: the block's hardcoded defaults overwrote
  the caller-set --pr mode, silently reverting to a local review. Read
  via ${REVIEW_PR:-0}/${PR_ARG:-} so a same-call prefix survives; reword
  the instruction to prefix, not edit-in-place.
- Deterministically sanitize finding text before it enters the posted
  Markdown (escape pipes/newlines/backticks, neutralize @-mentions and
  raw HTML, inert links) — the human confirm scan alone is not enough for
  content leaving the sandbox to a public comment under the user's identity.
- Pin the reviewed revision: capture headRefOid, show `reviewed at <sha>`
  in the report header + comment body, optionally re-check the head at
  post time so a mid-window push can't make a stale comment look current.
- Warn (SWARM_PR_WARN) when the local checkout isn't the PR head, since
  the in-session verifier reads file:line from local disk, not the PR head.
- Validate PR_ARG as a bare number so gh can't be pointed at another repo.
- Clean up $TMPD on every step-5 exit (decline / post-failure), not just
  the success path — the deferred cleanup otherwise leaks the PR diff.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GHHArYXzAy4DVYyFu15bpU
Two external-only review rounds (codex/grok/composer, no opus finders);
applied the confirmed correctness/security fixes, several of which were
regressions introduced by the earlier fix rounds:

R0:
- Hard-stop (not soft-warn) when the local checkout isn't the PR head —
  the in-session verifier reads file:line from disk, so a divergent tree
  silently mis-judges solo findings and gates the posted output on that.
- Capture gh stderr into SWARM_PR_ERR instead of discarding it.
- Undo the cross-turn $TMPD defer (step 3 cleans it; step 5 uses its own
  temp) so a decline/error can't strand the fetched PR diff on disk.
- Deterministic guard rejecting --pr combined with --fix/--loop.
- Tighten finding-text sanitization (neutralize @ anywhere, not just
  leading).

R1 (fixing R0-introduced regressions):
- Make the stale-head check a real gate that stops before posting, not an
  advisory echo followed by an unconditional gh pr comment.
- Validate the --pr value to ^[0-9]+$ in the model layer before building
  the shell prefix, closing a PR_ARG assignment-time injection.
- Extract headRefOid via gh --jq (no python3 dep) and treat an empty OID
  as a hard error instead of silently skipping the head guard.
- Also require a clean working tree at the PR head (not just HEAD==oid).
- Write the comment body with the Write tool, never a fixed-delimiter
  heredoc a steered finding line could forge.
- Restore composer in the skill description.

Loop stopped by judgment: it diverged (8 -> 13 findings), and the
recurring class (whole publish path is model-interpreted prose) is a
named residual — extract it into a testable scripts/ helper as a
follow-up rather than bolting on more inline shell.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GHHArYXzAy4DVYyFu15bpU
@gering gering merged commit 9fd980c into main Jul 13, 2026
1 check passed
@gering gering deleted the task/swarm-post-findings-to-pr branch July 13, 2026 16:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant