review-pr — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited review-pr (Agent Skill) and scored it 96/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 1 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
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.
Review one immutable GitHub pull-request snapshot. The workflow is read-only: it never submits a GitHub review, approval, change request, comment, Slack message, Jira mutation, checkout change, or other external action. No GitHub review is ever submitted by this skill.
/review-pr # current branch
/review-pr 123 # current-repository shorthand
/review-pr owner/repo#123 # repository-qualified
/review-pr https://github.com/owner/repo/pull/123 # PR URL
/review-pr owner/repo#123 --automation --premium-established --expected-head SHAAccept a PR URL, owner/repo#number, a numeric current-repository shorthand, or no selector. A qualified selector never derives repository identity from the current working directory.
Optional controls:
--expected-head SHA — require the selected immutable head.--checkout PATH — consider this local checkout, but use it only after exact verification.--automation — return the machine-readable contract below and never ask a question.--premium-established — assert that the caller selected the premium route before automation.--deadline-seconds N — total attended review budget; default 300 seconds. Record one absolutestop deadline at invocation, pass only the remaining seconds to each collector call, and return partial evidence when the budget expires.
This skill is model-tier: premium.
For a manual invocation below the premium tier, use AskUserQuestion once:
Skip the question when the user explicitly selected the current model.
For --automation, the skill must not prompt. Require --premium-established and confirm the current route satisfies the premium tier. If either condition fails, return status: failed, reason: premium-route-unavailable, and no verdict. Frontmatter alone is not route attestation.
Run the collector once before analysis:
~/.agents/skills/review-pr/scripts/gh-pr-snapshot.py \
'owner/repo#123' \
--expected-head HEAD_SHA_IF_SUPPLIED \
--checkout CHECKOUT_IF_SUPPLIED \
--timeout REMAINING_SECONDS \
--prettyOmit absent options. With numeric or no selector, the collector uses the current checkout only to resolve the shorthand, then passes explicit owner/repository to every remote request.
When the current checkout cannot resolve a bare number (for example a workspace root), the collector tries, in order, your open review requests with that number, then the role: primary repository in the nearest workspace.json. One match is used and recorded in target.selectorSource (review-request-number or workspace-primary-number); name the chosen repository in the report. Several matches fail with a target error of kind ambiguous listing each owner/repo#number: ask which one with AskUserQuestion and rerun qualified, or under --automation return failed with reason: ambiguous-target. No match keeps the original checkout error.
The collector returns canonical repository/PR identity, node ID, base/head refs and SHAs, bounded file patches, exact-head CI rollup, normalized feedback, a review-state key, checkout verification, limits, errors, and limitations. It disables paging and lazy Git fetching, applies one deadline and command-output cap, and rechecks base/head identity after collection.
Gate on its status:
complete with reviewReady: true — continue. Carry every limitations entry into the report.partial — name every unavailable/truncated source; do not issue a definitive verdict.stale — stop and report the expected and observed revisions; never present mixed-SHA evidence.failed — stop and report the bounded error; do not infer that missing evidence is empty.Draft or closed/merged state remains explicit in target; do not treat it as an open review.
limitations lists test, spec, fixture, and snapshot files whose patch was truncated or unavailable. These do not make the snapshot partial; truncated or unavailable source patches still do. With a verified checkout, read the full file from checkout.path instead; otherwise judge test coverage from the bounded patch and say which test files were not fully read.
A matching checkout is optional. Local repository reads are permitted only when checkout.available is true. That means the origin matches the selected repository, the working tree is clean, and local HEAD exactly matches the PR head SHA. Anchor every Read, Grep, or Glob path under checkout.path.
Remote identity uses the shared GitHub parser. SSH origins support github.com and single-label aliases such as work.github.com, in scp-style or ssh:// form. HTTP(S) requires literal github.com. This is a naming convention, not SSH configuration or DNS verification; arbitrary aliases and nested subdomains remain unsupported. Alias recognition never replaces the repository, exact-HEAD, or clean-tree checks.
When verification fails, state Local repository search unavailable with the collector's reason. Use only the bounded remote patches and metadata. Never search the workspace root or unrelated cwd, and never switch branches, fetch, reset, clean, create a worktree, or edit files.
Read evidence.feedback.records before analyzing the patches. Preserve stable identity, updateKey, stateKey, source, lifecycle, author, targets, and path/line data. Inspect evidence.feedback.partial and its errors before treating absence as none.
Build the unresolved list from:
CHANGES_REQUESTED reviews only when target.reviewDecision still reports changesrequested;
Treat approvals, dismissed/outdated/resolved records, self-authored messages, and automated status noise separately. Bot findings require the same independent validation as human findings.
Find the first Jira key in title, body, or head branch using [A-Z][A-Z0-9]{1,9}-[0-9]+.
jira.status: not-linked and continue without an AC checklist.criteria with the read-only Jira get tool.
jira.status: unavailable, include the error, and never claim requirements are satisfied.
Do not use any Jira mutation tool.
Requirements often live in Confluence pages (PRD, RFC, ADR, tracking plan) linked from the Jira description or the PR body. Collect page IDs from links shaped /wiki/spaces/<space>/pages/<id> or pageId=<id> in the Jira description (ADF link marks and inline cards) and target.body. Deduplicate and read at most 5 pages, in order of first appearance.
Read each page with the reader rules in the confluence skill, which names mcp__jira__jira_get as a verified reader:
path: /wiki/rest/api/content/<pageId>
queryParams:
expand: "body.storage,version,space"
jq: "{id: id, title: title, version: version.number, space: space.key, body: body.storage.value}"and note truncation.
read or unavailable with the reason, including short links andother unsupported link forms. Never claim a requirement met from an unread page.
unavailable; continue the review.
Use target, evidence.files, feedback, CI state, and verified local reads when available.
Before Jira lookup and before each analysis phase, check the one invocation deadline. On expiry, return partial with budget-expired; do not start another tool call. The caller should also impose its normal turn/runtime budget so interruption does not depend on model compliance.
For each changed file, assess:
If file patches, feedback, checks, Jira requirements, or repository-wide evidence needed for a claim are unavailable, make the limitation explicit. Missing evidence is never evidence of absence.
Judge the PR proportionately:
not in this PR, not fail; do not require the whole ticket to be done.
is an informational concern unless it is a real risk.
Immediately before writing any verdict, run the fast verifier using the original snapshot SHAs:
~/.agents/skills/review-pr/scripts/gh-pr-snapshot.py \
'owner/repo#123' \
--expected-head ORIGINAL_HEAD_SHA \
--expected-base ORIGINAL_BASE_SHA \
--expected-state-key ORIGINAL_STATE_KEY \
--verify-only \
--timeout REMAINING_SECONDSThe state key covers PR lifecycle, draft/review decision, exact-head CI state, and stable feedback identities/update state. If verification returns anything except complete, return stale or failed and suppress the verdict. Never reuse approval from a previous invocation or head SHA.
Every human-readable review must include this exact section before any assessment or verdict:
### Unresolved Reviewer Comments
- author — path:line — request — whether it remains valid at the reviewed headIf genuinely empty, emit:
### Unresolved Reviewer Comments
- None.## owner/repo#123 Review
**Head:** {immutable head SHA}
**Base:** {immutable base SHA}
**Snapshot:** complete
**Jira:** {key and summary | Not linked | Unavailable}
**Confluence:** {None linked | title vN read, or id unavailable (reason), per page}
**CI:** {exact-head rollup state}
**Local checkout:** {verified path | unavailable reason}
**Limitations:** {None | test files not fully read, with paths}
### Changes Overview
- ...
### Unresolved Reviewer Comments
- ...
### AC Checklist
| AC | Status | Evidence |
|----|--------|----------|
| ... | pass/fail/partial/not in this PR | ... |
### Concerns
- {blocking | informational} — ...
### Verdict
{Safe to merge | Needs changes | Needs discussion}
### Draft Comments
**Overall:** ...
- path:line — ...Omit Draft Comments when there is nothing worth saying. Drafts are never posted by this skill:
at most one combined minor line.
"Is Z intended?", not "Change X to Y").
path:line.Verdict rules:
blocking concern.
concerns alone never require discussion.
when linked, and unresolved comments are None.. Listed limitations do not block it unless an unread test region is needed to support a claim.
partial, stale, or failed snapshots.For --automation, emit one JSON object and no conversational prompt or surrounding prose:
{
"schemaVersion": "review-pr/v1",
"status": "complete|partial|stale|failed",
"reason": null,
"target": {
"repository": "owner/repo",
"number": 123,
"nodeId": "...",
"headSha": "...",
"baseSha": "...",
"stateKey": "..."
},
"changesOverview": [],
"evidence": {
"snapshotComplete": true,
"checkout": "verified|unavailable",
"checkoutReason": null,
"jira": "available|not-linked|unavailable",
"jiraKey": null,
"jiraSummary": null,
"confluence": [],
"ci": "SUCCESS|FAILURE|PENDING|UNKNOWN",
"errors": [],
"limitations": []
},
"unresolvedComments": [],
"acChecklist": [],
"concerns": [],
"verdict": "safe-to-merge|needs-changes|needs-discussion|null",
"draftComments": {"overall": null, "inline": []}
}reason is null for complete results and names the bounded failure/stale reason otherwise, including premium-route-unavailable. Before snapshot identity is available, target fields are null rather than fabricated. changesOverview contains the complete bounded changes summary used by the manual report. checkoutReason explains unavailable local evidence. limitations copies the collector's limitations, minus any test file fully read from a verified checkout. jiraKey is populated when a key is linked; jiraSummary is populated only when lookup succeeds. confluence lists each linked page as {id, title, status}, with status read or unavailable and title null when unread. Each unresolved comment, AC row, and concern retains its concise evidence so an automation caller can render the same complete report without re-running analysis. AC status is pass, fail, partial, or not-in-this-pr; each concern carries severity: blocking|informational. draftComments.overall is a string or null and each inline entry is {path, line, body}; both are empty when no draft is warranted and are never posted.
The watcher may consume a verdict only when status is complete, the final revision recheck succeeded, and verdict is non-null. This output authorizes no GitHub review or other external communication.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.