Skip to content
Back to skills

Pr Review

ASecurity

Use to review or comment on a GitHub PR or GitLab MR by number, URL, or branch, including a second review after an automated reviewer. Replies to existing bot threads: review-agent-feedback. Not for uncommitted local diffs.

  • 6 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 7, 2026
ai-agentspythongobashgitapisecurityperformance

Works with

  • claude code
  • api

Security analysis

A100/100

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

Scanned November 7, 2026

npx -y skills add m16khb-org/issueops --skill pr-review --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Pr Review?

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

Security grade badge for Pr Review
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/m16khb-org-pr-review/badge)](https://www.skillsdirectory.com/skills/m16khb-org-pr-review)

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: pr-review
description: 'Use to review or comment on a GitHub PR or GitLab MR by number, URL, or branch, including a second review after an automated reviewer. Replies to existing bot threads: review-agent-feedback. Not for uncommitted local diffs.'
---

# PR Review

## Activation and Scope

Review a GitHub PR or GitLab MR by number, URL, or branch, not uncommitted diffs.
Ask for a missing target; do not invent one. Existing bot-thread replies belong
to `review-agent-feedback` when available, not this review/posting workflow.
Detect the provider from `origin`: GitLab uses `glab`, GitHub uses `gh`.

This body owns the actor contract, including isolated/body-only use. Resolve
`<skill>` and optional references from the real, symlink-resolved skill directory,
not cwd. No sibling skill or evaluator bundle is required. Missing executable
assets, provider access, or host capabilities are explicit blockers/degradation,
not permission to fabricate a clean review or install unrelated tooling.

Every defect needs opened definitions, an upstream validation hop, a downstream
consumer hop, and a concrete failure on the cumulative diff. Only deterministic
gate measurements are exempt. `max` adds blind refutation and reproduction;
cheaper levels retain evidence requirements and disclose the missing adversarial stage.

## Effort levels

`--level` on preflight picks how much is spent. **Only `max` spawns agents.** Every level below
it runs the same lenses inline in your own context — no finder sub-agents, no skeptics — which
is why its cost does not scale with the number of units. The default is `high`.

| level | gate | find | verify | candidates per lens |
|---|---|---|---|---|
| `low` | 생략 | `logic`+`boundary`만, 팩 1패스 | prescreen | 렌즈당 4 |
| `medium` | ✅ | 적용 렌즈 전부, 인라인 순차 (정밀도 편향) | prescreen | 렌즈당 3 |
| `high` (기본) | ✅ | 적용 렌즈 전부, 인라인 순차 (재현율 편향) | prescreen | 렌즈당 4 |
| `xhigh` | ✅ | 전부 + sweep 패스 | prescreen | 렌즈당 6 |
| `max` | ✅ | workflow.js 팬아웃 | prescreen + blind tracer + reproducer | 규모 기반 (기존) |

`summary.md` prints the resolved plan as a `level=` line; `context.json → level` drives the
disclosure `post_review.py` puts in the posted body. Candidate confidence uses
the screening scale (≥80 strong, 60–79 summary, <60 dropped); posting uses the
severity-weighted bars in step 4. Do not impose a final finding-count cap:
`--max-inline` limits inline placement, not the findings disclosed.

**Pick `max` when the review is the gate** — a release branch, a change to money/auth/data, or
any MR whose findings you intend to state as verified. Pick `high` (or below) for a running
review of ordinary work. Say which level ran when you report in chat; never describe an inline
level's output as verified.

### 0. Preflight

```bash
python3 <skill>/scripts/mr_context.py --mr <ref> --repo-dir <repo> --worktree --history 5 --level high
```

Read `<out_dir>/summary.md`. Stop and say so when `eligible=false` (closed/merged/draft, or a
review by this skill already exists for this head) unless the user explicitly asked to review it
anyway. When `large=true` (> 40 files or > 2000 added lines), tell the user the scale and
ask whether to narrow (directories or lenses) before spending agents.

`--worktree` guarantees safe isolation: reuse a clean primary/linked checkout
at `head_sha` (`context.checkout`, `worktree=null`); only a dirty or different-head
checkout needs `<out_dir>/worktree`. Never create a redundant detached worktree.

`workflow_args.json` already contains the finder units, the checkout path, the candidate caps,
the hunk ranges and prior lessons for the prescreen — never hand-build it. A unit is one
lens bundle × one shard of the changed files with one self-contained `pack/<unit>.md`
([unit and pack layout](references/workflow-details.md)).

### 1. Gate

```bash
python3 <skill>/scripts/quality_gate.py --context <out_dir>/context.json
```

Run except at low: lint, scoped typecheck, targeted tests, LOC, longest touched
function, approximate complexity and test/source ratio against `base_sha`.
Only `gate.json → candidates` (`G1..`; failure 95, new breach 90, pre-existing 60,
unanchorable 50) are hop-exempt. All review numbers come from the gate, never estimates.

### 2. Find — inline (`low` / `medium` / `high` / `xhigh`)

**Do not dispatch sub-agents at these levels.** You are the finder. Work through the units in
`units.json` yourself, in this context, in sequence:

1. Read `pack/<unit>.md` whole, in one call — it already holds that slice's diff, the
   definitions and one-hop neighbours, matching rules, threads and prior lessons. Do not grep
   around it; that is what the pack exists to prevent.
2. Apply each of the unit's lenses to that pack **separately**, and tag every candidate with its
   `lens`. Do not let one lens's conclusion suppress another's.
3. Emit the finder schema below, at most `perLensCap` candidates and 3 `verified_ok`
   entries per lens. Attest every assigned file exactly once in `reviewed_files`.
4. `xhigh` only: after every unit, take one more pass over the diff as a fresh reviewer holding
   the current list, looking **only** for defects not already on it (examples in
   [workflow-details](references/workflow-details.md)). Return nothing rather than padding.

State missing hops and cap confidence at 50. Low/medium favor actionable precision;
high/xhigh favor recall with a nameable failure scenario, then confidence screening.

### Finder contract (all levels)

Apply only the generated unit's effort-selected lenses independently; never share finder conclusions.
Low is limited to `logic` and `boundary`. The five-lens base below applies to other
levels when selected by the generator; topic matches do not add undisclosed lenses.
`rules` requires explicit rule text, matching glob, changed line, and cited rule; `intent`,
when selected, maps each claim, criterion, checklist/table row to code/tests and quotes
unmet issue lines. Per-lens scope and applicability triggers: [lenses](references/lenses.md).

```json
{"lens":"<id>","reviewed_files":["<each assigned path once>"],"inspected":["<opened files/symbols>"],
 "candidates":[{"path":"...","new_line":1,"end_line":null,"severity":"critical|high|medium|low",
 "category":"bug|security|performance|business-logic|data|api-contract|test|rule|scope",
 "title":"<Korean>","what":"<Korean>","why":"<Korean failure scenario>","how":"<Korean fix>",
 "evidence":["path:line — proof"],"upstream":"<observed>","downstream":"<observed>",
 "suggestion":null,"rule":null,"confidence":80,"newly_reachable":false}],
 "verified_ok":[{"concern":"...","why_ok":"<evidence file:line>","loc":"file:line","thread":null}]}
```

Severity: critical = loss/corruption, auth bypass, secrets, payment/quota error;
high = reachable wrong behavior, partial write or crash; medium = degradation,
missing timeout/cleanup or consumer-visible drift; low = rare but concrete risk.
Exclude style/naming, speculative refactors, unsupported "consider adding", and
linter/typechecker/CI findings (the gate owns those). A call site is not a definition.
Require `suggestion` for API-contract and one-line decorator/description/config fixes;
apply and check it before shipping, or strip the code while retaining the finding.

### 2b. Screen — inline levels

Write the candidates to `<out_dir>/candidates.json` and run the deterministic stage:

```bash
python3 <skill>/scripts/prescreen.py --args <out_dir>/workflow_args.json \
  --candidates <out_dir>/candidates.json --out <out_dir>/prescreened.json
```

Merge `prescreened.json → candidates`. Dedup, off-hunk checks and committed memory
match max; security/data retain their exemption. Dropped reasons go in chat, not the MR.

### 2 (max). Find + Verify — fan-out

Claude Code: `Workflow({scriptPath: "<skill>/references/workflow.js", args: <contents of
workflow_args.json>})`. Omo native: **do not execute `workflow.js` through the current Omo
session**. Run the pinned adapter instead:

```bash
python3 <skill>/scripts/omo_driver.py --args <out_dir>/workflow_args.json \
  --profile omo-flash --provider zai --model glm-5.3-flash
```

The Omo adapter pins `zai/glm-5.3-flash` and never silently switches providers.
Missing/duplicate `reviewed_files`, failed children, or abstention mean
non-zero/degraded, never a clean review. Read [max-fanout](references/max-fanout.md)
before running the adapter or recovering a failed run: permissions, budgets, temporary
input cleanup, retained diagnostics (`failure_counts`), and recovery commands.

Other hosts use current sub-agent tools with `workflow.js`'s finder/skeptic
prompts (or the contracts in this body when the asset is absent): one finder per unit,
then tracer and only if needed reproducer per candidate, with the finder and skeptic
message budgets in [max-fanout](references/max-fanout.md). Use the session model
unless `args.models = {finder, tracer, reproducer}` overrides it.
Save `<out_dir>/workflow-result.json` before reading; quote its `cost` block
(agents, prescreened, reproducers skipped, output tokens) in chat.

### Max verdict contract

Prescreen deduplicates, rejects non-changed files/off-hunk lines unless proven
newly reachable, and matches prior lessons/refutation memory at similarity ≥0.5;
security/data never lose the learned-suppression exemption, including after dedup.
Skeptics start by reading candidate hunks and relevant defs together. The tracer
sees no finder evidence/upstream/downstream and independently opens the validation
and consumer hops, including linked intent; the reproducer sees the whole candidate
and tracer verdict, attempts a throwaway test, targeted check or executable scenario,
and records command/output, then deletes throwaway files.

Prescreen refusal or `refuted=true` at confidence ≥70 kills; a tracer kill skips
reproduction. Inability to verify is not refutation: return `refuted=false`,
confidence ≤40, reason prefixed `미확인:`. Exclude those verdicts; fewer than two
usable verdicts leaves an abstain ≤50, summary-only. Otherwise both must fail to
refute; final confidence is the minimum of finder and non-refuting skeptics.
Partial refutations with a standing skeptic ≥70 become open questions.
Severity changes require evidence and rationale, at most one step; use corrected
line/suggestion values. Scores: 0–25 inferred/unverified, 50 definition-proven rare
case, 75 real reachable path, 90–100 reproduction or definitions with no escape.
No reproduction and no proof from definitions can refute a scenario; tool inability
alone cannot. Intent alone is not correctness.

Missing finders or incomplete/duplicate coverage block verify, and no tracers run until
coverage is complete. Recover only failed units, or rerun only candidates kept as
`skeptics unavailable (abstain)`, with the `--retry-failed-units` and
`--retry-degraded-from` commands in [max-fanout](references/max-fanout.md); never
combine the two retries.

### 3. Merge (you are the moderator)

Write `<out_dir>/findings.json` (schema: `post_review.py` docstring):

- Start from `gate.json → candidates` (keep their `source`/`metrics`/`pre_existing`/`minor`
  fields), then add the findings with ids `F1..` — at `max` from `workflow-result.json`
  (keep `skeptics_passed`, `upstream`, `downstream`); at the inline levels from
  `prescreened.json → candidates` (keep `upstream`/`downstream`; there is no
  `skeptics_passed`, and never invent one — `post_review.py` reads it as "both skeptics failed
  to refute this" and lowers the inline bar to 50 on the strength of it).
- `partial` refutations become `open_questions` (inline levels list the hops you could not
  complete instead); `verified_ok` keeps `{concern, why_ok, loc, thread}` objects, at most 8,
  preferring ones that contradict an existing bot thread. Selection rules:
  [merge-details](references/merge-details.md).
- Drop a finding whose line already has a bot/human thread unless it contradicts that thread
  (then say so in `what`). Never repeat a claim in `prior_review_lessons`.
- Re-review of a moved head: resolved earlier threads in `context.json → prior_review_threads`
  go to `verified_ok` as "이전 지적 Fn 해결 확인", unresolved ones are referenced, not
  re-posted. `rule_candidates` are the refutations that hinged on a project fact, one Korean
  sentence each naming the fact and the proving file.
- Verdict: `request_changes` if any critical/high; `approve` if nothing ≥ medium and the
  tests lens found the changed behavior covered; else `comment`.
- Korean prose; identifiers, paths, commands verbatim. Never quote secret values.
- Before posting, use `fluent-korean` when installed and polish every Korean prose
  field the agents produced (`summary`, `verified_ok`, `open_questions`,
  `rule_candidates`, each finding's `title`/`what`/`why`/`how`). The polish pass is
  required even in an isolated copy; wording rules in
  [merge-details](references/merge-details.md).

### 4. Post

```bash
python3 <skill>/scripts/post_review.py --context <out_dir>/context.json --findings <out_dir>/findings.json --gate <out_dir>/gate.json          # dry run
python3 <skill>/scripts/post_review.py ... --post                                                                                             # only when asked
```

Inline bar is severity-weighted: critical/high ≥ 50, medium ≥ 65, low ≥ 80, and any
finding both skeptics failed to refute (`skeptics_passed`) qualifies at ≥ 50. At most 8
inline, agent findings first; deterministic gate findings get one inline slot (the worst by
complexity) and the rest a table. Medium+ findings under the bar are shown in "저자 확인
요청" with their `what`, never folded away. Pre-existing and minor (≤ 10 LOC over, complexity
≤ 3) breaches are table-only.
Findings on non-diff lines are demoted to the summary with a warning (`--strict` to fail
instead). Secrets are masked. GitHub `approve` posts as `COMMENT` unless `--allow-approve`.
The script refuses a moved head, a closed MR, and a second post for the same head. Report
the read-back table it prints, not the POST responses.

When the user did not ask to post, the deliverable in chat is: the level that ran, verdict, the
"지적" table, "저자 확인 요청", the 자동 검사 table, "검토했으나 문제 없음", rule proposals, and one
line per refuted candidate (title + winning skeptic reason; at the inline levels the prescreen
reason from `prescreened.json`). Do not paste the inline bodies. The
summary's first paragraph must name only defects that appear in the tables or in "저자 확인
요청" — never mention a finding the reader cannot find below.

### 4b. Remember refutations and 4c. Re-review

After a `max` run, `scripts/record_refuted.py` appends evidence-backed refutations to
`<repo>/.issueops/pr-review/refuted.jsonl`; `security`/`data` candidates are never suppressed.
For a moved head, run preflight with `--incremental`: only units whose files changed are
re-inspected and findings on untouched files are carried. Commands and rules:
[workflow-details](references/workflow-details.md).

### 5. Clean up

Read `context.json` before cleanup. Remove a worktree only when its non-null `worktree`
field resolves exactly to `<out_dir>/worktree`; a null field means preflight reused the
caller's checkout and it must never be removed. Remove an isolated review worktree with
`git worktree remove --force <out_dir>/worktree` on every exit path, including
`eligible=false` and validation failures. Delete throwaway specs the reproducer left.

## Scope and Disclosure

Only changed/newly reachable lines qualify; pre-existing debt needs explicit
evidence that this change worsened it. Name unread units and unverified
("probably/may/could") claims rather than presenting a complete/verified review.
Never approve through another path or post twice for one head.

## Stop and Optional Background

Done after the requested chat report or authorized post readback, plus cleanup.
No findings is valid with traced `verified_ok`; never pad. Missing evidence,
failed agents, or incomplete coverage must remain visible. Do not resolve
threads, merge, silently upgrade effort, or add a posting approval the user did
not give. In an IssueOps cycle return evidence to its owning stage for authenticated
recording and preserve its lifecycle/actor/generation/cwd and publication gates.

Optional detail in `references/`: `lenses.md`, `verification.md`, `max-fanout.md`,
`merge-details.md`, `workflow-details.md`. This body's effort, output, and posting rules govern.

Files in this skill

  • SKILL.md16.3 KB
  • agents/openai.yaml349 B
  • extensions/structured_output.js3.7 KB
  • references/lenses.md8.6 KB
  • references/max-fanout.md2.6 KB
  • references/merge-details.md1.5 KB
  • references/verification.md6.5 KB
  • references/workflow-details.md2.2 KB
  • references/workflow.js24 KB
  • scripts/mr_context.py60 KB
  • scripts/omo_driver.py69.4 KB
  • scripts/post_review.py22.9 KB
  • scripts/prescreen.py6 KB
  • scripts/quality_gate.py21.6 KB
  • scripts/record_refuted.py2 KB
  • tests/test_context_pack.py18.5 KB
  • tests/test_omo_driver.py54.6 KB
  • tests/test_prescreen.py6.9 KB

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.