---
name: maintainer-review-sweep
description: Use when sweeping a GitHub repo you maintain (issues + PRs) to decide what to act on and to re-review stalled change-requested pull requests before merge. Triggers — "triage the repo", "what should I pick up / clear", "re-review this PR", "is this ready to merge". Repo-aware: open-gsd/gsd-core conventions baked in but parameterized.
---

# Maintainer Review Sweep

## Advisory-only on non-Claude runtimes

PreToolUse enforcement is a Claude Code harness feature. On non-Claude runtimes (Codex, OpenCode, …) this toolkit runs **advisory-only** — the gates are not enforced; treat its guidance as advice, not a hard block. Enforcement (the fail-closed PreToolUse deny) exists ONLY on the Claude Code runtime; everywhere else this skill's gates are guidance you must follow yourself, not a hard stop.

## Overview

A maintainer-grade triage + re-review pipeline. It ranks open work by **cost-to-advance** (a re-review beats a fresh design pass), then re-reviews change-requested PRs against the blocking reviewer's findings, the governing ADRs, and software-law lenses — and stops at "clear to merge" unless explicitly authorized.

**Core principle (load-bearing):** *Exogenous, evidence-grounded verification beats endogenous self-grading.* Every verdict must be defensible by external facts — real CI conclusions, real test output, real `git diff` — and by a fresh reviewer that never saw this verdict. An agent grading its own conclusion is the documented failure mode (LLM self-attribution bias is largest exactly on incorrect outputs), not the safeguard.

## When to use

- "Triage open issues/PRs", "what's ready", "what should I clear", "re-review #N", "is #N mergeable"
- Maintainer or codeowner context on a repo where external contributors open PRs

**Not for:** authoring your own feature (use the project's normal flow); reviewing a diff you just wrote in this same session (self-bias — get a fresh context).

## Inputs (Phase 0 — Scope)

| Input | Default |
|---|---|
| `repo` | `open-gsd/gsd-core` (always pass `--repo`; clone has multiple remotes) |
| `exclude-author` | the lead maintainer (e.g. `trek-e`) — **filter for ACTION, never hide**; still show their items, especially high-severity/security |
| `target branch` | the repo **default branch** (resolve via API; here `next`) — older release lines are out of scope |
| `merge token` | none. Merge only when the invocation explicitly carries `merge=#<n>` |

## The pipeline

Run in order. **Adversarial gate between every phase:** before advancing, state the strongest reason the previous phase's output is wrong, and resolve it. Re-fetch live state (PR HEAD OID, labels, reviews) immediately before any write — never act on a stale snapshot.

0. **Scope** — resolve repo, default branch, exclude-author, merge token.
0b. **Runtime freshness (RT0, ENF-21)** — run `node <gsd-contrib-toolkit>/bin/runtime-sync.cjs check`; on any verdict other than `fresh`, run `node <gsd-contrib-toolkit>/bin/runtime-sync.cjs sync` (one command, no prompts, no decisions — do not ask, just run it). Re-reviewing a PR against a stale `~/.claude/gsd-core` yields verdicts that do not match CI: you reproduce behaviour the merge queue no longer has, and you flag as missing things `next` already ships. **This step is the ONLY runtime-freshness coverage the review path has** — the ENF-21 `PreToolUse` gate deliberately governs the *filing* surface (`gh issue create` / `gh pr create` / `git push`) and NOT the review-side verbs (`gh pr review`, `gh pr merge`), a recorded non-gate in [CTK-ADR-0007](../../docs/adr/CTK-ADR-0007-runtime-freshness-and-the-network-unavailable-severity.md). Nothing will stop you re-reviewing on a stale engine, so this is a discipline step, not a floor.
1. **Discover** — list open issues + PRs (`gh ... --json number,title,author,labels,...`). Drop excluded-author from the *action* set (keep them visible). Tag next-relevance.
1b. **Prior-adjudication recall (advisory)** — before ranking, ask what a past session already concluded about the surfaced `#numbers`: was this issue already adjudicated, was a PR on this defect already cleared or blocked, and on what evidence? Run `mempalace search "<#n> <module/symptom>" --results 5` — the CLI form, because this skill is often followed inside a subagent, where the main session's tool-call transport does not exist.
    **Search un-scoped — omit `--wing`:** one project scatters across up to three wings and the hook-captured *conversation history* lands in `sessions`, so a wing-scoped miss is **not** evidence of absence. Advisory, never a gate; if a concurrent `mine` holds the palace (`palace ... is held by PID N`), retry once then skip — it never stops the sweep. **MemPalace is an OPTIONAL third-party tool**: if `mempalace` is not on `PATH`, skip this step and rank as normal.
2. **Cross-reference in-flight work** — before ranking, map relationships across the open set: **duplicate/overlapping issues** (same defect by domain concept, not just wording), **issue ↔ PR links** (is this issue already addressed by an open PR?), and **PR ↔ PR collisions** (two PRs editing the same files/module → rebase-order or mutual-break risk). Group related `#numbers` so nothing is picked up, relabeled, or cleared in isolation. Carries into re-review.md step 7.
3. **Prioritize** — two lenses: quick-fix vs. highest user impact. Surface data-loss/security/runtime-broken first. Demote anything already covered by in-flight work (from Phase 2).
4. **State & cost-to-advance** — rank by cheapness to advance: *approved+rebase* < *brief-ready, no PR* < *re-review round* < *needs design*. A re-review ranks above a full design pass.
5. **Stranded-value sweep** — find issues with a complete brief but a missing `ready-for-agent` label; relabel them (a brief is the expensive part; the label is the only thing gating dispatch). Skip any superseded by an open PR (from Phase 2).
6. **Ball-in-court** — for change-requested PRs, anchor to the latest **`CHANGES_REQUESTED`** review (NOT the latest review of any kind). Author pushed *after* it → needs re-review (ball on reviewer). Otherwise author still owes changes.
7. **Authority reality** — read role, CODEOWNERS, and **rulesets** (note `enforcement: evaluate` = audit-only, NOT enforcing; `required_status_checks` in evaluate mode means CI is not gated — you must read real check conclusions yourself). Decide: can *you* clear it, or is a specific reviewer structurally required?
8. **Present & select (sweep mode).** Present the ranked buckets from Phases 2–7 — cost-to-advance tiers, ball-in-court, collisions, authority — and **await the user's pick** before any per-PR re-review. Skip this when invoked directly on a named PR (`re-review #N`). If persisting the report, write it to the repo's scratch location (`.planning/notes/` here — gitignored — or the repo's agreed scratch dir).
9. **Per-PR re-review** — for each *selected* PR, follow **[re-review.md](re-review.md)** (includes the ADR/source-of-truth pass and the cross-reference collision check). Produces a drafted review + verdict; submits only on human OK; merges only with the token AND a plain-`CLEAR`, evidence-backed verdict AND no other maintainer's *unresolved* change-request (a verifiably-resolved one is dismissed on human OK, not merged-over).
10. **Capture the sweep outcome (advisory)** — file the verdict and its evidence, and specifically **what the exogenous/second pass ([re-review.md](re-review.md) step 10) caught that the first pass missed**: that delta is the single most reusable thing a sweep produces, and today it evaporates with the session. Run `mempalace mine "$HOME/.gsd-contrib-memory" --wing gsd_core --dry-run` first (a short note is silently skipped), then the same command without `--dry-run`; recipe and gotchas live in the `core-contribution` skill's `reference.md`.
    **The asymmetry:** omit `--wing` to search wide, pass it to mine (the default wing is the staging directory's name). Advisory — a failed capture never fails the sweep.

## Cross-cutting rules (always on)

- **Honesty of evidence (non-negotiable).** Local-verification numbers (`npm ci`, `build:lib`, `lint:ci`, test counts) must come from commands you *actually ran* and captured. If you could not run it, write "not run" — never synthesize a plausible count. Fabricated verification is the exact dishonesty this pipeline exists to prevent.
- **Respect the intake perimeter.** The repo's GitHub Actions (auto-close-unsolicited-prs, require-issue-link, **pr-title-validator** (`type(#issue): summary`, enforced for *all* authors incl. members), dedupe, version-gate, close-draft-prs, dismiss-unauthorized-pr-approvals) are LIVE — never work around them. A conventional-title miss or a missing `#<issue>` scope is a real (Minor) gate-compliance finding, not a nit to wave through. Deliver the re-review as a **single human-submitted formal review** (Approve if CLEAR, Request-changes if a code blocker remains) — never a bot auto-approval the perimeter may dismiss, and don't double-post a standalone comment alongside it.
- **Two orthogonal isolated passes (`CI.GATE.orthogonal-review-required`).** Every PR review runs a correctness+standards pass (`/code-review`) AND a security pass (`/security-review`) as *separate* passes, plus a holistic gate/root-cause read — and carries an explicit `### Security` verdict line even when it's `PASS — no code surface`. This is trek-e's current declared convention (footer: *"Reviewed with two orthogonal isolated passes…"*); the full procedure is [re-review.md](re-review.md) step 8. Also render the **Generative-Fix-Divergence** verdict (re-review.md 4c): a fix that recreates its own root cause by hardcoding a fresh literal instead of importing the single source of truth is a Blocker/Major, not a clear.
- **Disclaimer on every tracker write:** `> *Generated by AI during triage; reviewed and posted by a human maintainer (@<you>).*` — use "submitted" for a formal review, "posted" for a comment, "dismissed" for a review dismissal.
- **Bounded comments — don't clutter the timeline.** One review body per round, ≤ ~200 visible words, verification depth in a single `<details>` fold, clean optional sections suppressed. Submit a formal review only when the verdict changes; edit a running comment for interim status. The hard template in [re-review.md](re-review.md) is the contract — post exactly it, no more.
- **Follow the `/triage` state machine** (one category role + one state role; flag conflicting states, ask before unusual transitions).
- **Handle labels exactly like the lead maintainer** — see **[labels.md](labels.md)**: remove `needs-triage` on any state transition; `confirmed-bug`(+`ready-for-agent`) is the bug fix-gate, `approved-enhancement`/`approved-feature` the code-gate; use `needs-reproduction` not generic needs-info; never hand-edit bot-managed labels (`possible-duplicate`, `needs-version`); apply the missing-label-on-PR maintainer one-click action; re-fetch labels before editing. (`version-exempt` is a maintainer opt-out the gate *reads* but is **not** bot-managed and **not** a live label — don't try to apply it; see [labels.md](labels.md).)
- **Don't clear over a lead's *unresolved* change-request.** Never merge while another maintainer has an *unresolved* `CHANGES_REQUESTED`. But if your re-review *verifiably confirms* the contributor resolved exactly what they requested, **dismiss** that stale CR (re-review.md 12a) with an evidence-cited message — fulfilling their conditions, not overriding them — rather than leaving the PR stuck awaiting a manual re-flip.
- **Delegate issue-side advancement to `/triage`.** This sweep ranks and relabels issues; for issues that need reproduction, grilling/design (`/grilling` + `/domain-modeling`), an `.out-of-scope/` prior-rejection check, or the needs-info loop, hand off to the `/triage` skill rather than reimplementing its state machine. The sweep identifies and routes; `/triage` advances.
- **Advancing an issue → follow the triage-assist order (mirror trek-e).** Issue advancement runs in sequence: (1) **settle bug-vs-by-design against the design record** (PRD/ADR/explanation docs — quote the clause; an intended-but-unimplemented behavior is still a `confirmed-bug`, #2045) *before* confirming; (2) for a confirmed bug, post the structured **Agent Brief** (Reproduction empirical → Root cause `file:line` → Fix → Acceptance → Verify → **authorization + scope fence**, #2070/#2118/#2107) — the expensive artifact `ready-for-agent` gates dispatch on; (3) for an enhancement/feature, post the **approval-conditions checklist** (hard caveat + required-before-merge boxes, #860); (4) **route** extension-point-shaped work to `capability-candidate` (deliver as an ADR-857/1239 capability, not a core patch) rather than defaulting to core. Full formats + verified examples in [triage-assist.md](triage-assist.md).
- **Initial-triage assist for an incoming issue.** For a fast, LIVE-script-backed first call on a single freshly-opened issue (duplicate signal + version-gate finding + suggested canonical role + the `needs-triage` strip), use **[triage-assist.md](triage-assist.md)** (doer: `bin/triage-assist.cjs`). It **complements** this sweep — advisory and surface-only, it mutates nothing without explicit `--apply`, and the suggested role comes ONLY from LIVE `docs/agents/triage-labels.md`. It does not replace the sweep's ranking or the re-review path.
- **Authoring intent mid-sweep → stop and route to `/gsd-submit`.** The moment this sweep stops *adjudicating* and starts *authoring* — writing a fix for a defect triage surfaced, pushing a correction to a stalled PR, filing a follow-up or spin-off issue, or opening any issue/PR on `open-gsd/gsd-core` — stop and route to **`/gsd-submit`** (the `core-contribution` skill), carrying the packet in [Authoring hand-off](#authoring-hand-off-sweep--contribution) below. Route to the SKILL, never to a bare `gh issue create`: the skill carries the KNOW-03 security routing, under which a **real exploitable vulnerability** goes to the private advisory at `/security/advisories/new` and **NOT** a public issue — a live vuln is disclosed the moment the public issue opens. This sweep ranks and adjudicates; it does not author.

## Authoring hand-off (sweep → contribution)

**Trigger:** the cross-cutting rule above — adjudicating has turned into authoring.
**Destination:** `/gsd-submit`, i.e. the `core-contribution` skill and its gated P0–P6 pipeline.

**The hand-off packet — carry every item, so the contribution path's P0/P1 does not re-derive what this sweep already established:**

1. **The `#number`.** The issue/PR this came out of, plus every related `#number` grouped by the Phase-2 cross-reference — so the contribution is not filed in isolation into a set that already holds a duplicate, a linked PR, or a colliding PR.
2. **The evidence already captured**, each item with its provenance — *which command, when*. Real check-run conclusions, `git diff`, test output. Per the honesty-of-evidence rule above, anything you did not actually capture crosses the seam as an explicit **"not run"** — never as a synthesized number.
3. **The design record you opened AND quoted** — the ADR/PRD/explanation clauses from the triage-assist bug-vs-by-design settle and from the re-review ADR/source-of-truth pass. Carry the **quotes themselves**, not "I read it": downstream, P2b must quote the clause again anyway.
4. **The RT0 verdict** — whether `bin/runtime-sync.cjs check` already ran in this session, and its literal verdict.
5. **The ball-in-court finding** — which `CHANGES_REQUESTED` review it anchored to, and who owes what.
6. **The scope fence** — any bound the confirming issue or Agent Brief imposed. The PR must stay strictly inside it; the tempting adjacent fix is its own issue, not a rider.
7. **What the recall surfaced** (step 1b) — the prior adjudications, verdicts, and falsified premises a past session already established, with their `#numbers`, so the contribution path's **P0c** inherits them instead of re-searching from scratch.

**Honest scope — this rule is MODEL-DRIVEN and advisory. It is NOT new enforcement, and it adds no hook.** The hard floor already exists and is unchanged: the ENF-21 `PreToolUse` gate DENIES `gh issue create`, `gh pr create`, and `git push` against `open-gsd/gsd-core` regardless of which skill is loaded, and it is **fail-closed**. This rule exists so you arrive at the gated pipeline *deliberately* rather than dead-ending at a deny — the gate is what actually stops a broken submission; this only tells you where to go. That split is consistent with the recorded decisions rather than a revision of them: per CTK-ADR-0001 §Decision.1 (as amended by CTK-ADR-0004 and CTK-ADR-0006) hooks lock outcomes, not steps, and CTK-ADR-0006 §Decision.4 bounds review-side gating to a fixed set of artifact-existence steps — this seam is deliberately not one of them.

## Quick reference

| Want | Do |
|---|---|
| "Triage the repo" | Phases 0–8 (rank → present → you pick) |
| "Initial-triage an incoming issue #N" | [triage-assist.md](triage-assist.md) + `bin/triage-assist.cjs` (advisory: LIVE dedupe + version-gate + role, surface-only) |
| "Re-review #N" | Phase 0 + [re-review.md](re-review.md) on #N |
| "Clear #N to merge" | re-review.md; stop at merge-readiness unless `merge=#N` given |
| "Have we adjudicated this before?" | `mempalace search "<#n> <symptom>"` — un-scoped (advisory; step 1b) |
| Ball-in-court of a PR | Phase 6 logic (anchor to latest CHANGES_REQUESTED) |
| "Found something to fix / file mid-sweep" | `/gsd-submit` with the [hand-off packet](#authoring-hand-off-sweep--contribution) — the sweep adjudicates, it does not author |

## Common mistakes (red flags — STOP)

- Comparing a commit to the *latest review* instead of the latest **CHANGES_REQUESTED** → wrong ball-in-court (a non-owner's approval does not clear an owner's change-request).
- Reporting test counts you did not run → fabrication. Run it or write "not run."
- Trusting "CI green" from the ruleset → it's in evaluate mode; read actual check-run conclusions.
- Grading your own verdict → self-bias. Use the exogenous reviewer in re-review.md.
- Dropping the excluded maintainer's high-severity/security issues from view → filter action, not visibility.
- Acting on a stale snapshot → re-fetch HEAD OID + reviews right before any relabel/post/merge.
- Posting a 2,000-line review of a 40-line fix → scope-freeze to the defect class.
- Re-reviewing the same PR on every push → dismiss-stale-on-push loop. Stop after 2 rounds without new substantive change; escalate to a human.
- Editing a generated `bin/lib/*.cjs` or trusting it as source → it's built from `src/*.cts` (ADR-457); verify the source changed and `build:lib` is clean.
- Clearing a fix that recreates its own root cause → **Generative-Fix-Divergence** (re-review.md 4c): a new hardcoded literal that duplicates a source of truth already imported. Not clear — Blocker/Major.
- Running one blended review pass → the convention is **two orthogonal isolated passes** (`/code-review` + `/security-review`) with an explicit `### Security` line every time; a missing security verdict is an incomplete review.
- Collapsing every finding into Blocker-or-Nit → use the middle tiers (**Major** / **Minor**, or High/Medium/Low). A Major that isn't a Blocker is real signal.
- Passing a test that can run its assertion **zero times** → the vacuous/pass-always trap (re-review.md 4b); demand a `> 0` count assertion.
- Posting a long multi-section review wall → use the hard size-bounded template (re-review.md); depth goes in the one `<details>`, never clobber the PR/issue history with repeated full dumps.
- Authoring a fix, pushing a correction, or filing an issue/PR **straight out of the sweep** instead of routing through `/gsd-submit` → the sweep ranks and adjudicates; it does not author. You will hit the fail-closed deny anyway — arrive there on purpose, carrying the [hand-off packet](#authoring-hand-off-sweep--contribution).

## Validation

Live-validated 2026-06-20 (RED→GREEN→REFACTOR per `superpowers:writing-skills`): on **#1409** the CI-attribution heuristic produced a wrong APPROVE (a coverage-table misread) — caught when the contributor's rebase falsified the "stale-base, rebase clears it" claim, corrected to Request-changes, and step 4a hardened to demand the literal failing assertion. On **#1418** it correctly Request-changed a changeset `product-name-purity` blocker that the prior reviewer's review predated. Both runs exercised the discover→re-review path end-to-end.
