feat(meta): CTL-2266 — Mergify merge queue config + labels for catalyst - #4079
Conversation
queue:ready is the review attestation (CTC-1200); hold:hand-steps is the escape hatch. Both labels created on coalesce-labs/catalyst via the API (colors/descriptions match the catalyst-cloud trial).
Extends the catalyst-cloud merge-queue trial (CTC-1196) to coalesce-labs/catalyst. Mirrors the ruleset's own skip-tolerant check shape (success|neutral|skipped) for the six required contexts plus the three always-on non-required gates, excludes quality via the negative -check-failure form (it is path-filtered and often absent), and excludes publish-* workflows, db-migrations, .mergify.yml itself, and release-please branches from unattended merge. max_parallel_checks: 3 per Ryan's direction — the live ruleset still has strict_required_status_checks_policy: true, so real parallel throughput needs that turned off separately (not done here; see the PR description). Validated: `mergify config validate` clean; `mergify config simulate` against #4070 and dependabot #4066 in both polarities, plus #4028 (release-please) as a real negative control for the head-branch exclusion.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Tick the box to add this pull request to the merge queue (same as
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c7003e00d5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
… pending Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…platform-enforced (#4089) ## Summary Adds `merge_protections` rules to `.mergify.yml`, extending the catalyst-cloud config-as-code adoption ([Mergify feature-suite evaluation](https://github.com/coalesce-labs/catalyst-cloud/blob/main/thoughts/shared/research/2026-08-28-mergify-feature-suite-evaluation.md), "Merge Protections rules — ADOPT NOW") to this repo: two checks that hold even for a PR merged **by hand**, outside the Mergify queue. 1. **No unresolved review threads before merge** — `#review-threads-unresolved=0`, active on every non-draft PR against `main`. 2. **Title follows the Conventional-Commit convention** — `type(scope): ...`, active on every non-draft PR against `main` except automation: `dependabot[bot]`, Mergify's own queue-train/dashboard-config branches (`head~=^mergify/`), and release-please (`head~=^release-please--` — it opens PRs under a human GitHub identity in this repo, not a bot login, so it can't be author-exempted). **This does not touch `queue_rules`/`pull_request_rules`.** The auto-queue rule CTC-1231 also asks for (`label=queue:ready` + `#review-threads-unresolved=0` + the required-check set → `queue`) already shipped today via CTL-2266 (#4079) — re-checked, unchanged, still correct. **Not required yet.** A failing active `merge_protections` rule surfaces as a non-required `Mergify Merge Protections` check — this PR does not mark it required in the `main branch protection` ruleset, so nothing is newly blocking. That flip is Ryan's call, same restraint #4079 took with branch protection. **Note on `.mergify.yml`'s own path exclusion:** this repo's `queue_conditions`/`merge_conditions` already hard-exclude `-files~=^\.mergify\.yml$`, so this PR (and any future `.mergify.yml` change) can never enter the queue regardless of labeling — hand-merge only, by design, already in place before this PR. Explicitly **not** in this PR (per the sibling catalyst-cloud evaluation and the dispatch instructions): CI Insights auto-retry, any Test Insights config. ## Validation ### `mergify config validate` ``` $ mergify config validate -f .mergify.yml Fetching schema from https://docs.mergify.com… Configuration file '.mergify.yml' is valid. ``` ### `mergify config simulate` — scope limitation, documented `mergify config simulate <PR_URL>` only previews `pull_request_rules`/`queue_rules` evaluation. Ran it against #4066 (real Dependabot PR) to confirm the pre-existing queue rule is unaffected by this diff — it evaluated correctly (all conditions still resolve as before). It never surfaced the new `merge_protections` rules on any PR — that's a CLI/product scope limit (I found no documented way to preview `merge_protections` outside the dashboard), not a config defect. In place of `simulate` for the new rules, I verified both directly: - **Review-threads condition** — the exact same `#review-threads-unresolved=0` attribute the existing `queue_conditions` already gates on; unchanged in this diff, and I didn't need to re-derive its correctness. - **Title regex** — scripted the exact YAML-parsed pattern against real titles pulled from `gh pr list` history for this repo, both polarities: - **Match:** `feat(meta): CTL-2266 — ...`, `perf(ci): CTC-1229 — ...`, `ci(runner-image): CTC-1228 — ...`, `chore(deps): Bump ...` (Dependabot), `ci: Bump actions/checkout from 4 to 7` (Dependabot, no scope), `chore: release main` (release-please). - **No-match, correctly flagged:** `CTC-746: A ticket's agent session should still show its plan after th` (a real, old, merged PR using a pre-convention title shape — historical, not currently open, so nothing bricks). ## Local gate No unified `bun run check`-equivalent here; ran `mergify config validate` (pass) and `make lint`/`make test` locally. `make lint`'s `trunk check` currently fails on a pre-existing `fmt`/prettier formatting complaint about `.mergify.yml` — **reproduced identically against the unmodified `origin/main` copy of the same file**, so it predates this PR and isn't something this diff introduced or should fix (touching those pre-existing lines was explicitly avoided to keep the live, already-proven queue config untouched). `make test` surfaced one unrelated pre-existing failure (`catalyst-config.test.sh`, a config-fingerprint test with no relation to `.mergify.yml`) before my own `timeout 180` truncated the (very large) suite; nothing in this diff touches that test or its dependencies. ## Linear CTC-1231 stays in Implement — dispatched with `--phase implement`; not moving it to In Review myself per the dispatch instructions (report only, don't merge). --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Summary
Phases 2–3 of the CTL-2266 plan — extends the catalyst-cloud Mergify merge-queue trial to
coalesce-labs/catalyst. Phase 1 (the monotonic version guard) already merged as #4077, so AC2's silent version-collision case is already covered bycheck-versionsonmain— this PR does not touch that logic again.queue:readyandhold:hand-stepslabels created on the repo (colors/descriptions match the catalyst-cloud trial);AGENTS.mddocuments queue-merge as the PR default once activated..mergify.yml, validated and simulated against real open PRs below.This PR does not activate the queue and is not merged by the queue itself (it touches
.mergify.yml, which the config's own path exclusion routes to hand-merge). The coordinator merges this by hand, then runs the live@Mergifyioprobe per plan Phase 4 — Ryan has already activated the repo in the Mergify dashboard.One deliberate deviation from the plan:
max_parallel_checks: 3The plan as researched specified
max_parallel_checks: 1(in-place checks, matching the catalyst-cloud shape). Per Ryan's direction from today's catalyst-cloud experience, this PR ships withmax_parallel_checks: 3instead.Measured caveat, not fixed by this PR:
gh api repos/coalesce-labs/catalyst/rulesets/13503799still reports the "main branch protection" ruleset'srequired_status_checks.strict_required_status_checks_policy: true(verbatim, re-checked just before opening this PR). Mergify computesallow_inplace_checksautomatically, and only enables it whenmax_parallel_checks == 1+batch_size == 1+merge_conditions == queue_conditionsfor every queue — settingmax_parallel_checks: 3opts OUT of in-place checks, so Mergify will run CI on temporary merge-queue branches instead of pushing update commits onto the PR branch. Separately, withstrict_required_status_checks_policy: true, each queued PR's required checks are only valid against the exact currentmain, so real parallel throughput still needs that ruleset setting turned off — otherwise batches serialize behind rebase-and-recheck regardless of the configured slot count.I have not touched branch protection — that's explicitly out of scope for me per the dispatch instructions; it's a call for Ryan. The config comments in
.mergify.ymlspell this out inline so it isn't only in this PR description.Validation
mergify config validatemergify config simulate— real open PRs, both polarities#4070 (
CTL-2240, Ryan's own PR, all required + non-required checks green) — real state, noqueue:readylabel:Every guard is TRUE except the label — as expected for an unlabeled PR. While testing this (toggling
queue:readyon and off on #4070 to get the positive-polarity read, see below), I discovered #4070 genuinely has unresolved Codex P1 review threads right now (#review-threads-unresolved = 0reads[ ]false) — real, not caused by my testing. That's a live negative-polarity data point for that specific condition, not a config defect: the guard correctly refuses to admit a PR with unresolved review threads. Ryan/coordinator: #4070 is not actuallyqueue:ready-eligible today for that reason, independent of this PR.#4066 (
dependabot/bun/eslint-10.9.1) — I temporarily appliedqueue:ready(removed immediately after, confirmed back to["dependencies","javascript"]), to get the positive polarity on a real PR:Full green — this dependabot PR would actually enter the queue with the label applied. This is the genuine positive control the plan's Phase 3 step 3 asks for (not a list of ticks on a PR that was never going to queue).
#4028 (
release-please--branches--main) — real state, read-only, no label change — negative control for the outward-facing exclusion:Confirms the head-branch regex correctly refuses a real release-please PR (this repo has one live right now, since CTL-2263 reinstated release-please — landed on
mainbetween this branch's original cut and this rebase; the-head~=^release-please--exclusion already covers it, no config change needed, matching the plan's "correct under both futures" claim in decision 3).Required-check population, re-confirmed live
gh api .../commits/<sha>/check-runson both #4070 and #4066 shows all six ruleset-required contexts pluspackaging-gate,skills-gate,check-plugin-manifest-parity, andqualitypresent and green on both — consistent with the plan's check-population table.What's not in this PR
hold:hand-stepsrefusal, pause behavior — run live after activation.strict_required_status_checks_policy— that's Ryan's call, not made here.Linear
CTL-2266 → In Review.