| name | merge-gate |
| description | Review inbound agent-shipped PRs at the merge gate — drift checklist, targeted live e2e, fix-in-place, review-bot iteration (Copilot + CodeRabbit) — leaving each PR merge-confident (gate-passed) or explicitly failed (gate-failed). |
| argument-hint | [pr-number] |
| disable-model-invocation | true |
| metadata | {"internal":true} |
merge-gate
Take a PR that a shipping agent left at its merge gate and make it merge-confident:
verified against the project's drift checklist, integration-tested live, scoped
gaps fixed in place, and re-reviewed — so the maintainer's merge decision is a
10-second yes. The maintainer merges; this skill never does.
Inbound PRs come from several shipping streams (cloud-ship routine, codex,
teammates' agents). Each has already run its own pipeline — tests, docs-sync,
Copilot rounds — and stopped at the merge gate. This skill is the independent
second gate: it assumes nothing the author agent claimed, and re-verifies.
Invocation
/merge-gate <n> — gate one PR.
/merge-gate — sweep: scripts/sweep-list.sh prints the queue (every
open, non-draft PR carrying neither gate-passed nor gate-failed, oldest
first); work it in order, one consolidated report at the end.
Scripts
scripts/ holds the gate's deterministic steps; each prints a one-screen
summary and its exit code is the completion criterion. When a step names a
script, run it instead of re-deriving the calls it wraps — scripts gather
evidence and encode the traps; judging the evidence stays with you.
Every comment posted to the PR starts with:
> *This was generated by AI during merge-gate review.*
Per-PR flow
1 · Preflight
- Run
scripts/gate-preflight.sh <n> — one JSON blob: PR meta + body, labels,
linked issues, CI check runs, a pushed_last_15min flag, and the CodeRabbit
threads left with no disposition (neither a fix nor a
decline-with-evidence reply).
- Skip drafts and PRs with
pushed_last_15min: true (the author agent may
still be working); note the skip.
- Read the PR comments yourself: the author agent's merge summary and its
per-reviewer dispositions (Copilot and CodeRabbit), plus the linked issue +
brief. Each undispositioned CodeRabbit thread the script surfaced is a gap
the author agent left — treat it as a review finding for step 3.
- Red CI or a merge conflict is fix item #1, not a blocker — the gate fixes
in place.
2 · Checkout
Fetch the PR branch and check it out in a fresh worktree
(git fetch origin <branch> + git worktree add); never in the shared checkout. All gate fixes are
commits on the PR's own branch. Diff base for every check below:
git diff origin/<default-branch>...HEAD.
3 · Review
Two layers, both against the diff:
- Invoke the tracked
code-review skill by name (Standards + Spec axes, its
own per-axis tiers) — the two-axis pass is the review gate. Spec source: the
linked issue + agent brief. A hand-rolled reviewer subagent is not a substitute;
if the skill errors or seems missing, fix that and re-invoke rather than
improvise around it.
- The drift checklist — the seven repo-specific gates in
reference/drift-checklist.md. Work through all
seven; each ends in an explicit pass / fail / n-a with evidence. An unchecked
item is a gap in the gate, not a pass.
Auto-triage findings with rigor: verify claims against pinned versions and current
docs, harden rather than rip out capability, reject known non-issues with
evidence. Every finding gets a one-line disposition for the verdict comment.
4 · Integration test
- D365-touching change → follow the
live-e2e skill: run the PR's new/changed
e2e tests plus the existing e2e tests for the touched command groups.
scripts/select-e2e.sh maps the diff to the group test files and prints
their @covers strings (@covers(...) stamps are a coverage registry, not
pytest markers, so -k/-m cannot select on them) — confirm the touched
verbs appear in those strings, then run
D365_E2E=1 pytest -m e2e <files> (worktree-code recipe). Run on
every live target the touched commands support — a single-target green has guessed the
wrong capability gate before. Pin --profile, confirm the org via
crm connection whoami, and quote both in the verdict. A target unreachable
(VPN down, trial expired) is an explicit skip with reason, never a silent pass.
Done = the full test_<group>*.py file(s) ran green on every supported
target — the PR's own new test node passing alone is not the integration test.
- No D365 surface (docs, local groups, offline-provable refactor) → offline
suite only; say so in the verdict.
5 · Fix in place
Fix scoped items directly on the PR branch: CI red, drift-checklist failures,
valid review findings, missing docs-sync artifacts (use the project's docs-sync
subagent for those). Re-run the project's local gate green before every push —
the ship skill's scripts/local-gate.sh (sibling skill dir) runs the full
CI-mirrored set and prints only the failing lines.
A design-level problem — wrong approach, contract change the issue never
asked for, a diff that needs re-scoping — is a finding, not a fix: record it,
label gate-failed, and escalate in the verdict. The gate polishes; it does not
redesign someone else's PR.
6 · Review-bot iteration
Only when the gate pushed commits (nothing pushed → nothing new to review; skip
— but CodeRabbit threads found undispositioned in preflight must still be
triaged (step 3), fixed where valid (step 5), replied to on-thread, and
bulk-resolved via @coderabbitai resolve before the verdict):
- CodeRabbit owns iteration — it re-reviews each push automatically, no
re-request needed (if it auto-paused,
@coderabbitai review lifts the pause).
Poll bounded, auto-triage its returned comments (same rigor as step 3), fix
valid ones, push — the next round re-triggers on its own. Reply on each
CodeRabbit thread ("fixed in <sha>" / decline + evidence); once every thread
is dispositioned, @coderabbitai resolve closes them — never earlier (it
bulk-resolves).
- Copilot is round-1-only — with one gate exception. The gate may spend
exactly one Copilot re-request, and only when it significantly rewrote
the PR (a scoped lint/format/typo fix does not qualify). Otherwise Copilot's
round-1 threads are dispositioned once, as in the ship flow. The exception is
one per PR, not per gate run. Spend it via
scripts/rerequest-copilot.sh <n> — it refuses when the re-request was
already spent or is still pending (so a re-run or sweep can't re-enter this
branch), uses the REST path with the exact bot login (the
gh pr edit --add-reviewer path and the display name "Copilot" both fail
on this repo), and verifies requested_reviewers actually populated — a
bare HTTP 201 can silently no-op.
- Converged = CodeRabbit quiet on the latest push + every Copilot thread
dispositioned (declined-with-evidence counts as dispositioned). CodeRabbit's
auto-rounds are free — drive them to quiet. If CodeRabbit stays substantive
round after round, the PR has a shape problem more rounds won't fix → stop and
fail the gate.
7 · Verdict
Post the verdict comment (reference/verdict.md) on the PR
and set exactly one label: gate-passed (converged, all checklist items
pass/n-a, live runs green where required) or gate-failed (unconverged,
design-level finding, or an unfixable checklist failure — the comment says which
and what the maintainer should decide).
8 · Issues surfaced — confirm in the verdict, then report
The gate is surface-only: it files nothing and touches no out-of-scope code
(the same discipline step 5 applies to design-level findings). Its job is to make
the issue trail explicit — so the verdict comment's Issues surfaced section,
and the session report after it, both answer two questions:
- Filed during this PR — issues the author agent's notes or the PR body say it
filed, plus any filed mid-gate. Link each by number.
- Needs filing — out-of-scope problems the review surfaced (adjacent bugs,
debt, doc gaps beyond this PR) with no issue yet: one line + a suggested action
each; the maintainer decides. State an explicit "none — nothing out-of-scope
surfaced" when the trail is clean, so an empty section never reads as unchecked.
Report the same two lists in the session after the verdict (never before); the
maintainer decides what gets filed or dispatched — file nothing yourself.
In a sweep, finish each PR's steps 1–7 before the next, then give one
consolidated report: per-PR verdict line + the combined issues list.
Model tiers
Same rule as ship: judgment on the strong tier, mechanics on the cheap one —
and no model at all where a script covers the step. Scripts gather the evidence
(preflight, genericity scan, e2e selection); judge their output inline on
the strong tier — an evidence-gathering subagent adds relay loss for nothing.
File-mapping sweeps → haiku; running live tests and mechanical fixes → sonnet;
finding triage and the drift checklist → opus (or nearest available). Tag every
subagent explicitly.
Reference files