Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions .mergify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Mergify merge queue — WS3.4/D5 of the v3.8.49 quality/velocity master plan.
#
# WHY: ~85-100 active PR authors/month and 300+ PRs/week peaks, all merged by ONE
# identity. The manual merge-train validated batches by hand; this queue automates
# it with batching + automatic batch bisection (a red batch of N costs ~log2(N)
# revalidations instead of N). Mergify Open Source plan: free, unlimited, public repo.
#
# GOVERNANCE (non-negotiable, mirrors CLAUDE.md Hard Rules #21/#22 + the owner's
# pre-merge ⭐ gate):
# • A PR enters the queue ONLY via the `queue` label — applied by the owner (or a
# session acting for the owner) AFTER the pre-merge ⭐ report/decision. The label
# IS the merge approval; Mergify only executes it.
# • During a release-freeze (open issue labeled `release-freeze`), do NOT label PRs
# targeting the frozen branch — the freeze is a human-honored coordination signal
# the queue cannot see. Retarget to the active release/vX+1 first (Hard Rule #21).
# • Never label a PR another session is actively working (Hard Rule #22b).
# • Fallback path if Mergify misbehaves or the OSS plan changes: the manual
# merge-train runbook (docs/ops/MERGE_TRAIN.md) — remove labels, proceed by hand.

queue_rules:
- name: release
# Any current or future release branch — the reason GitHub's native queue was
# rejected (no wildcard support on personal-account repos).
queue_conditions:
- base~=^release/v\d+\.\d+\.\d+$
- label=queue
- -draft
- -conflict
# "Everything that ran is green, nothing still running, AND the always-on
# anchor check succeeded" — robust to the path-filtered fast-gates (docs-only
# PRs skip code jobs; matrix shard names vary) while never fail-open: a PR with
# zero checks cannot vacuously merge, because `Merge integrity` runs on EVERY
# non-draft PR (quality.yml) and must be an affirmative success. Review approval
# is intentionally NOT a condition here: the owner-applied `queue` label IS the
# approval in this repo's single-maintainer model (see governance header).
merge_conditions:
- "#check-failure=0"
- "#check-pending=0"
- "#check-success>=1"
- check-success=Merge integrity (changelog + generated skills)
# Batching: validate up to 10 queued PRs together (the manual train's sweet spot);
# don't hold a lone PR hostage waiting for siblings.
batch_size: 10
batch_max_wait_time: 5 min
# Squash keeps the one-commit-per-PR history the CHANGELOG reconciliation expects.
merge_method: squash

pull_request_rules:
- name: clean up the queue label after merge
conditions:
- merged
actions:
label:
remove:
- queue
1 change: 1 addition & 0 deletions changelog.d/maintenance/mergify-queue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- **Merge queue (D5)**: reviewed PRs now merge through the Mergify queue (`.mergify.yml`, Open Source plan) — entry is the owner-applied `queue` label AFTER the pre-merge ⭐ gate; batches of up to 10 validate together with automatic bisection of red batches (~log2(N) instead of N revalidations); the manual merge-train is codified as the fallback runbook in `docs/ops/MERGE_TRAIN.md` with the freeze/cross-session guardrails
63 changes: 63 additions & 0 deletions docs/ops/MERGE_TRAIN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
title: Merge Queue & Manual Merge-Train Runbook
---

# Merge Queue & Manual Merge-Train Runbook

Since v3.8.49 (WS3.2/WS3.4 of the quality/velocity plan) the default merge path for
reviewed PRs into `release/vX.Y.Z` is the **Mergify merge queue** (`.mergify.yml`);
the **manual merge-train** documented below is the FALLBACK — used during incidents,
release freezes, or if the Mergify Open Source plan ever changes.

## Default path: the Mergify queue

1. PR is reviewed/greened by the campaigns and approved by the owner's pre-merge ⭐
gate (the report + per-item decision — see `/merge-prs` Step 0.75).
2. The owner (or the session acting on the owner's decision) applies the **`queue`**
label. The label IS the merge approval; Mergify only executes it.
3. Mergify batches up to 10 queued PRs, validates the batch against the fast-gates,
and merges (squash). A red batch is **bisected automatically** — the offending PR
is isolated in ~log2(N) revalidations and unqueued; the rest proceed.
4. Post-merge, the continuous release-green workflow validates the new tip on push
and opens an attribution issue if the combination regressed (never auto-revert).

Guardrails (mirror `CLAUDE.md` Hard Rules #21/#22):

- **Release freeze open** → do NOT label PRs targeting the frozen branch; retarget to
the active `release/vX+1` first.
- **Another session's in-flight PR** → never label it; only the owning session queues
its own work.
- Tests-only diffs and `hotfix`-labeled PRs already run reduced CI (see
`RELEASE_CHECKLIST.md` → Hotfix Fast-Lane); the queue conditions accept whatever
check set actually ran (`#check-failure=0` + `#check-pending=0`).

## Fallback: the manual merge-train

Used when the queue is unavailable. This codifies the practice that drained 33 PRs in
one day during the v3.8.47 cycle:

1. **Assemble the batch** (~10–30 reviewed+approved PRs). Check `linked:` collisions
(same `tap.testFiles`, same CHANGELOG hunks) and serialize those.
2. **Validate ONCE**: in an isolated worktree off the release tip, merge all batch
heads locally, then run the release-equivalent suite
(`npm run check:release-green`, add `--with-build` before a release).
3. **Green** → merge the PRs in sequence (re-checking `state,headRefOid` before each —
a PR whose head moved re-enters review). Prove the net diff of each merge is the
PR's own change (no auto-resolve reverts: audit `git diff --stat` for
out-of-scope deletions).
4. **Red** → bisect the batch by halves (validate each half) instead of re-validating
one-by-one; drop the offending PR back to the review queue with the evidence.
5. **Never**: merge during a freeze into the frozen branch; `git stash` anywhere;
blanket-rerun CI hoping a red goes away (rule: a red is information).

## Tiering (why the queue is safe with fast-gates only)

- **Per PR** (quality.yml fast-gates): TIA-impacted tests + full unit 4-shard +
vitest + lint bag + typecheck + docs/changelog integrity.
- **Per batch/tip** (continuous release-green): `--quick` HARD gates on every push to
the release branch; full `--with-build --full-ci` sweeps 3×/day.
- **Per release** (ci.yml on the release PR): the complete matrix incl. E2E ×9,
package-artifact + tarball boot-smoke, coverage/ratchets.

Nothing is validated less than before — the heavy surface just runs per batch/tip
instead of per PR, which is what removes the O(N) round-trips.
Loading