ci(mergify): add Phase 1 serial merge queue (.mergify.yml)

Implements issue #409: a serial Mergify merge queue that supersedes the
hand-rolled poor-man's queue (autoupdate.yml + ci-trigger.yml +
traffic-control.yml, all already disabled_manually).

- queue_rules "default": batch_size 1 (serial, no batching), merge_method
  merge (merge commits, never squash/rebase), merge_conditions gate on
  check-success = "CI passed" + -draft + -conflict + label != broken.
- merge_queue.max_parallel_checks 1 (true serial; unambiguously
  require-up-to-date-compatible).
- priority_rules map P0..P9 labels (P0 = 10000 highest .. P9 = 1000).
- pull_request_rules queue action triggers auto-queueing (queue_conditions
  alone do NOT auto-queue per Mergify lifecycle docs).

require-up-to-date STAYS ON (Phase 1 is the only trilemma combo that keeps
the checkbox literally enabled AND preserves merge commits). No batching
(that is Phase 2 / #410). Single required gate stays "CI passed".

Validated: YAML parses and conforms to Mergify's published JSON schema
(negative-control confirmed). Merging this activates Mergify, so NO
auto-merge — must be reviewed first.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-06 22:28:14 -05:00
co-authored by Claude Opus 4.8
parent 594c6167d3
commit 40b14d51cc
+138
View File
@@ -0,0 +1,138 @@
# SPDX-License-Identifier: GPL-3.0-or-later
#
# ============================================================================
# Mergify configuration — PHASE 1: serial merge queue (issue #409).
#
# Spec: docs/ci/mergify-integration-spec.md + docs/ci/mergify.yml.proposed
# (issues #407 / #408).
# Schema: https://docs.mergify.com/configuration/file-format/
# Verified against the LIVE Mergify docs on 2026-07-06 (queue rules,
# the queue action, priority rules, parallel checks, batches, setup and
# lifecycle pages) — the config format evolves, so this is not from memory.
# ============================================================================
#
# WHAT THIS DOES
# A SERIAL merge queue that ends the manual serial-bump grind and supersedes the
# hand-rolled "poor-man's merge queue" (autoupdate.yml + ci-trigger.yml +
# traffic-control.yml — all already `disabled_manually`). Mergify updates each
# queued PR onto the latest `main`, re-runs CI, and merges it with a MERGE COMMIT
# when the single required gate — the "CI passed" check — is green. One PR at a
# time, in P0–P9 priority order.
#
# HARD INVARIANTS (do NOT relax without the trilemma decision recorded in the spec):
# * require-up-to-date STAYS ON. This is Phase 1 = batch_size 1 + merge_method:
# merge — the ONLY trilemma combination that keeps GitHub's "Require branches to
# be up to date before merging" LITERALLY enabled AND preserves the merge-commit
# policy. Mergify honours it by updating each PR onto the latest `main` and
# re-running CI before merging ("Updates PRs against the latest main before
# merging" — docs.mergify.com/merge-queue/setup). NO batching: batching would
# require turning that checkbox OFF (docs.mergify.com/merge-queue/batches) and is
# the blocked Phase 2 / issue #410 — explicitly OUT OF SCOPE here.
# * The single required status check stays "CI passed" — the exact `name:` of the
# `ci-passed` job in .github/workflows/ci.yml. NOT "ci-passed". A wrong name means
# PRs queue but never merge.
# ---------------------------------------------------------------------------
# queue_rules — how a queued PR is validated and merged.
# ---------------------------------------------------------------------------
queue_rules:
- name: default
# Final merge gate. Merge ONLY when the single required context is green (the exact
# same check branch protection requires), the PR is not a draft, has no merge
# conflicts, and is not flagged `broken`. NOTE: branch protection requires 0
# approvals here (the active repository ruleset sets required_approving_review_count
# = 0), so there is deliberately NO `#approved-reviews-by` condition — adding one
# would wedge the solo-maintainer flow, where nobody can approve their own PR.
merge_conditions:
- check-success = CI passed
- -draft
- -conflict
- label != broken
# SERIAL: exactly one PR per merge. No batching (Phase 2 / #410). One merge commit
# per PR, which is what lets require-up-to-date stay literally ON.
batch_size: 1
# Merge commit — never squash / rebase / fast-forward (repo policy: merges use
# merge commits, never squash).
merge_method: merge
# ---------------------------------------------------------------------------
# merge_queue — queue-wide options.
# ---------------------------------------------------------------------------
merge_queue:
# Validate ONE PR at a time — true serial, no speculative parallel checks. This is the
# strictest, unambiguously require-up-to-date-compatible setting: Mergify updates the
# REAL PR branch onto the latest `main`, runs CI on that branch, and merges on the real
# green "CI passed" — with no speculative temp-branch/real-branch check mismatch to
# reason about. It also caps the expensive, wedge-prone ~15-min E2E matrix at a single
# concurrent run. Raising this (speculative parallelism) is a throughput optimisation to
# weigh alongside the Phase 2 / #410 batching decision — not part of serial Phase 1.
max_parallel_checks: 1
# ---------------------------------------------------------------------------
# priority_rules — map the repo's P0–P9 labels onto queue priority.
# Higher number merges first (Mergify keywords: low=1000 / medium=2000 / high=3000;
# numeric range 1–10000). P0 is emergency-only and outranks everything. PRs with no P-label
# fall to Mergify's default `medium` (2000).
# ---------------------------------------------------------------------------
priority_rules:
- name: p0-emergency
conditions:
- label = P0
priority: 10000
- name: p1
conditions:
- label = P1
priority: 9000
- name: p2
conditions:
- label = P2
priority: 8000
- name: p3
conditions:
- label = P3
priority: 7000
- name: p4
conditions:
- label = P4
priority: 6000
- name: p5
conditions:
- label = P5
priority: 5000
- name: p6
conditions:
- label = P6
priority: 4000
- name: p7
conditions:
- label = P7
priority: 3000
- name: p8
conditions:
- label = P8
priority: 2000
- name: p9
conditions:
- label = P9
priority: 1000
# ---------------------------------------------------------------------------
# pull_request_rules — WHICH PRs enter the queue.
# The `queue` action is what actually ADDS a PR to the merge queue: per
# docs.mergify.com/merge-queue/lifecycle, queue_conditions alone do NOT auto-queue a PR —
# a queue action (or an `@mergifyio queue` command / auto_merge) is required, otherwise the
# "Mergify Merge Queue" check sits permanently pending. A PR is queued as soon as it is green
# on "CI passed", targets `main`, is not a draft, has no conflicts, and is not flagged
# `broken`. `broken` / `draft` PRs are never queued.
# ---------------------------------------------------------------------------
pull_request_rules:
- name: Queue green, non-draft, non-conflicting PRs targeting main
conditions:
- base = main
- -draft
- -conflict
- label != broken
- check-success = CI passed
actions:
queue:
name: default