| name | merge-prs |
| description | Merge one or more PRs with full stack-integration pipeline. Resolves branches, rebases, gates, pulls main. |
| argument-hint | [PR numbers...] [--admin] [--dry-run] |
| user-invocable | true |
| allowed-tools | Bash |
| truth_contract | {"canonical_sources":["ops/state/truth/policy.json","ops/state/truth/sources.json"],"live_load_required":["gh pr view <N> --json number,headRefName,baseRefName,mergeable,mergeStateStatus","gh api repos/InterCooperative-Network/icn/branches/main/protection --jq '.required_status_checks.contexts'"],"examples_only":[],"never_hardcode":["PR numbers or branch names","required check lists (query live or read policy.json)","sprint state"]} |
Stack-integration merge pipeline. Not a thin gh pr merge wrapper. Owns branch resolution,
merge ordering, post-merge rebases, local verification, and main sync.
For single quick merges when you already know the state is clean, this still works. For sprint
batch closes, use /integrate-pr-stack instead (same logic, more verbose output).
Step 0 — Preflight (run first, always)
REPO_ROOT="$(git rev-parse --show-toplevel)"
bash "${REPO_ROOT}/ops/scripts/what-matters-now.sh" 2>/dev/null || true
If what-matters-now.sh reports drift errors → stop and fix drift before merging.
Drift in agent files (stale paths, missing truth_contracts, hardcoded check sets) means
the tooling you're running may itself be unreliable.
Steps
-
Resolve PRs to merge:
- If
$ARGUMENTS specifies numbers, use those.
- Otherwise list open PRs:
gh pr list --json number,title,headRefName,mergeable,mergeStateStatus
- If zero open PRs, stop.
-
For each PR, resolve head branch from GitHub (never trust plan names):
gh pr view <N> --json number,title,headRefName,baseRefName,mergeable,mergeStateStatus \
--jq '{pr:.number, head:.headRefName, base:.baseRefName, mergeable:.mergeable, state:.mergeStateStatus}'
-
Classify check state (required gates only — load from policy.json, never hardcode):
REPO_ROOT="$(git rev-parse --show-toplevel)"
REQUIRED=$(python3 -c "import json; p=json.load(open('${REPO_ROOT}/ops/state/truth/policy.json')); print(','.join('\"'+c+'\"' for c in p['merge']['required_checks']))")
gh pr view <N> --json statusCheckRollup \
--jq ".statusCheckRollup[] | select(.name | IN(${REQUIRED})) | {name:.name, result:.conclusion}" 2>/dev/null
UNSTABLE mergeStateStatus + all required conclusions SUCCESS → treat as GREEN, safe to merge
- Any required conclusion empty/pending → use
--auto or wait
claude-review, Compare Against Base, Test Coverage failures → never block, ignore
-
Merge (in order provided; smallest/safest first for batch). This repo uses squash merge:
gh pr merge <N> --squash
gh pr merge <N> --squash --admin
gh pr merge <N> --auto --squash
Exception: subtree merge commits require --merge — note the reason explicitly.
-
After each merge:
git checkout main && git pull
-
Rebase remaining PR branches after each merge to keep them current:
gh pr view <remaining-N> --json headRefName --jq '.headRefName'
git checkout <branch> && git rebase origin/main
cargo clippy -p <affected-packages> --all-targets -- -D warnings
git push --force-with-lease
-
Final: confirm main state, print one-line summary per PR.
Output format
#1389 merged · rebased [feat/s22-compute-policy-config, feat/s22-obs-attestation-config]
#1391 merged · rebased [feat/s22-obs-attestation-config]
#1390 merged · rebased []
Rules
- No polling loops. Use
--auto for pending checks; use --admin for queue-stalled (not failing).
- No tabular parsing. Always
--json with jq or python3.
- UNSTABLE ≠ blocked. Required checks green +
mergeable=MERGEABLE → merge.
- Always resolve head branch from GitHub. Never use plan-doc branch names directly.
- After each merge, rebase remaining branches. Verify locally before force-push.
Required checks reference (illustrative — canonical source is policy.json)
Verify live: gh api repos/InterCooperative-Network/icn/branches/main/protection --jq '.required_status_checks.contexts'
Or read canonical: python3 -c "import json; print(json.load(open('$(git rev-parse --show-toplevel)/ops/state/truth/policy.json'))['merge']['required_checks'])"