---
name: md-pr-review
description: >-
  Review and merge open PRs in the Multideal monorepo. Run on "review open
  PRs", "review and merge PRs", "check PRs", "/md-pr-review". Triages each PR
  (dependabot/CI-bump vs real code), groups related PRs, runs a Sonnet code
  reviewer on diffs, adds security-gate + complete-mediation-scan for money
  paths (stripe/connect/affiliate/referrals/refund/returns/cases/checkout/
  auth/webhook/token/session), fixes failures via cursor-orchestrator, deploys
  each PR to preview and smokes it as a blocking pre-merge gate, then merges
  passing PRs.
---

# md-pr-review

**Audience: AI coding agents first (main-loop orchestrator, me).** Optimize for activation, not prose.

**Batch merge then final preview deploy + smoke (Step 6). Self-executed. NEVER user-gated.** Green CI is required before each merge. Final `main` deploy + smoke validates combined batch once.

Merging a PR lands code on `main` — a real act. Deploying lands code on `dev.multi.deal` — also real, and the env is shared and single.

## Step 1 — List + triage

```bash
gh pr list --state open --json number,title,headRefName,files,additions,deletions
```

Classify each PR into one bucket:

| Bucket | Criteria | Action |
|---|---|---|
| **Dependency** | dependabot/renovate PR; only `pnpm-lock.yaml` + `package.json` version bumps + `.github/workflows/*` action bumps + auto-generated files | Fast-path: verify no logic file sneaked in + CI green, then merge — no review needed |
| **Code — no money path** | Real code change; NO file matches money-path pattern | code reviewer only |
| **Code — money path** | Real code change; ≥1 file matches money-path pattern | code reviewer **AND** security-gate **AND** complete-mediation-scan |

**Money-path file pattern** (any match → money path):
- path matching: `server/payments/`, `server/payments/connect/`, `server/payments/stripe`, `server/referrals/`, `server/affiliate/`, `server/returns/`, `server/domain/refund/`, `server/domain/returns/`, `server/workflows/refund`, `server/promo/`
- path matching: `pages/api/payments/`, `pages/api/refunds/`, `pages/api/cases/`, `pages/api/returns/`, `pages/api/referrals/`, `pages/api/affiliate/`
- file name/path matching: `auth`, `webhook`, `payout`, `refund`, `transfer`, `escrow`, `charge`, `session`, `token`, `cart`, `checkout`, `ledger`, `credit`, `settlement`, `vendor-split`, `order`

**Grouping rule:** PRs touching same module family (e.g. two PRs both in `server/promo*`) → review together as one diff. Unrelated areas → review independently.

## Step 2 — Dependency PRs (fast-path)

For each Dependency-bucket PR:
1. Skim diff — confirm only lockfile, `package.json` bumps, workflow action bumps, generated files.
2. Any logic/`src` file sneaked in → reclassify as Code PR, route accordingly.
3. `gh pr checks <N>` — CI MUST be green.
4. Clean + green → Step 6 guarded merge. Fast-path skips review only.

## Step 3 — Code PRs: code reviewer

Dispatch the reviewer per PR / grouped set. **Reviewer by bucket — NEVER downgrade:**

| Bucket | Reviewer | Model/effort |
|---|---|---|
| Code — no money path | `code-reviewer` subagent | **Sonnet** |
| Code — money path | **`/ask-codex` (`cdx exec`)** | **gpt-5.6-sol/medium** |

Money-path security review runs via `/ask-codex gpt-5.6-sol/medium` (codex, `cdx exec`) — **NEVER Opus, NEVER the `security-guard` subagent** (owner ruling 2026-07-18). Money-path money bugs (auth/IDOR/idempotency/atomicity/settlement math) still get the higher-effort pass, now on codex sol/medium. Non-money code review stays on the `code-reviewer` subagent at the Sonnet floor.

Review brief (same for both — pass as the trailing positional arg to `cdx exec` for money-path, or as the subagent prompt for non-money):

```
Review the diff for PR #<N> against:
- Multideal Hard Rules (CLAUDE.md): component-only UI, no hardcoded values, RTL-first
  logical props, IS 5568/WCAG AA a11y, he+en i18n together, zod inputs, parameterized
  queries, no PII logs, secrets in CF bindings.
- The owning skill's invariants if a money path (md-stripe / md-rma / md-afill /
  md-promotions / md-dba) — read that skill BEFORE judging the diff.
- Correctness, idempotency, atomicity, object-level authorization (IDOR).

Fetch the diff with: gh pr diff <N>

Fix small problems yourself (LOP ≥ LOC rule: lines-to-fix ≤ lines-to-brief → fix in place).
Before reporting done: `pnpm --filter web typecheck` MUST exit 0; verify claims with
grep/ctx_execute/ls — never assert without evidence.
Escalate only: big refactor, total redo, or spec contradiction you cannot resolve.

Return verdict: PASS | FAIL-SMALL (fixed) | FAIL-ESCALATE (describe what).
```

## Step 4 — Money-path PRs: security-gate + complete-mediation-scan

For money-path PRs, ALSO run AFTER the code reviewer:

1. **security-gate skill** on each money-path file in the diff — validated 2-leg gate (deterministic complete-mediation oracle + k≥3 adversarial LLM) on the multideal baseline bug set:
   ```bash
   python3 ~/.claude/security-gate/gate.py <target-file> \
     --alias '@/=apps/web/src/' \
     --k 3 --depth 2 --report /tmp/security-gate-pr<N>.md
   ```
2. **complete-mediation-scan skill** — fresh adversarial pass for missing-authorization / sibling-path guard gaps (self-deal, IDOR, missing check on one party).

Read every report. Any `BLOCK`, `REPORT` (would-block under report-only), `WARN`, or `COVERAGE-INCOMPLETE` line MUST be resolved before merge — fix it, or explicitly justify benign with the why. NEVER silently bypass.

## Step 5 — Handle failures

| Verdict | Action |
|---|---|
| **PASS** | Proceed to merge (Step 6) |
| **FAIL-SMALL (fixed)** | Reviewer already committed the fix; re-run security-gate if money path; proceed |
| **FAIL-ESCALATE** | Invoke `cursor-orchestrator` with the escalation as task brief. After cursor fixes, re-dispatch the reviewer on the new diff. Loop until PASS. |
| **Security BLOCK/REPORT** | Fix the finding (self if small, cursor-orchestrator if large). Re-run security-gate. Do NOT merge until clean. |

## Step 6 — Guarded batch merge → final preview gate

Order is fixed: CI green for each PR → guarded batch merge → one final `main` deploy + smoke. Never deploy PR branches during batch review.

### 6a — Merge each approved PR

`gh pr checks <N>`. Advisory lanes (`continue-on-error: true`) do not block; state which lane is advisory and why its red is not the PR's fault. Merge through the fail-closed guard:

```bash
node scripts/safe-merge.mjs <N> squash
```

Confirm: `gh pr view <N> --json state,mergedAt`.

### 6b — Deploy and smoke final `main`

Use clean `main`. `scripts/deploy.sh` always creates a commit, so it cannot deploy clean `main`; deploy final built worker through supported package script:

```bash
cd apps/web && pnpm typecheck && pnpm build && pnpm deploy:preview
```

Deploy exit 0 is not proof worker serves. Smoke `dev.multi.deal` through `ctx_execute` with Node `fetch`; print only status, doctype presence, and body length. Require HTTP 200, `<!doctype html>`, and body length >5000. Do not use `curl` or `wget`.

`scripts/deploy.sh` performs typecheck, build, worker-config validation, commit+push, preview deploy, and scheduler-arm verification. It does not render-smoke or auto-rollback. A final deploy or smoke failure blocks release; fix `main`, then re-deploy and re-smoke.

## Step 7 — Report

After the last merge, Step 6b deploys and smokes `main` so preview == `main`.

Report concisely:
- PRs merged (numbers + one-line description)
- PRs that needed fixes (what was fixed)
- PRs blocked (why, what needs owner attention)
- Migrations applied to the shared preview DB (forward-only — name each)
- Final preview state: which commit is live on `dev.multi.deal`, smoke result.

---

## Constraints

- **NEVER merge a PR without green required PR Gate checks.** Final preview deploy + smoke is release validation, not a per-PR blocker.
- **NEVER deploy PR branches during batch review.** Deploy final `main` once after all guarded merges.
- **NEVER merge a PR with unresolved security-gate BLOCK/REPORT** — not even with a note; fix first.
- **NEVER skip the code reviewer on a code PR** — value is the independent-context pass; inline review is not equivalent.
- **Money-path security review = `/ask-codex gpt-5.6-sol/medium` (codex, `cdx exec`) — NEVER Opus, NEVER the `security-guard` subagent** (owner ruling 2026-07-18). Non-money code review = `code-reviewer` subagent at the Sonnet floor. See memory [[feedback_subagent_model_policy]].
- **NEVER `--force-push` / `--no-verify`** (CLAUDE.md Hard Rule 11). **NEVER direct-push to `main`** — land via PR merge only.
- **Judgment stays in main loop** — subagent returns evidence + fixes small things; pass/fail verdict and merge decision are mine.
- **Dependency PRs: verify before fast-merging** — a bump PR that touches `src` is a code PR.
- **Money path → read the owning skill first** (md-stripe / md-rma / md-afill / md-promotions / md-dba) — invariants live there; do not re-derive.
