At 6:40 PM on a Wednesday, our pull request board looked perfect. Every card was green. Every check had passed. And yet by 7:15 PM, main was red, rollback was in progress, and the incident channel was asking the same question nobody likes hearing: “How did this merge?”
The answer was uncomfortable. Nothing “hacked” us. Nothing dramatic failed. We failed at coordination. CI validated each PR in isolation, branch protections were uneven across repos, and cloud credentials still lived as long-lived secrets. Each piece looked fine. The system did not.
This article is a practical, implementation-first playbook for fixing that with GitHub rulesets and merge queue, reusable policy workflows, and OIDC-based short-lived cloud auth. If your team is tired of green checks that still ship surprises, this is the setup I recommend in 2026.
Why this stack works (and where teams get it wrong)
From GitHub’s own docs, four details matter more than most teams realize:
- Rulesets layer together, and when rules conflict, the most restrictive one wins.
- Merge queue validates queued changes as a combined group, not only one PR at a time.
- If you require merge queue checks, your workflows must listen to the
merge_groupevent. - OIDC gives short-lived credentials per job, so you stop copying long-lived cloud keys into secrets.
What teams get wrong is treating these as independent toggles. They are a system. Rulesets define policy, merge queue enforces integration order, and OIDC reduces credential blast radius during deploy.
The three-control-plane model
1) Policy plane: repository rulesets
Use rulesets for global policy and for branch-specific rules. Keep branch protection only where you still need legacy behavior. In most orgs, I see better outcomes when repos have:
- One org-level baseline ruleset (signed commits, review minimums, blocked force pushes).
- One repo-level release ruleset for
mainand release branches. - A narrow bypass list, ideally GitHub Apps plus a small incident-admin group.
Tradeoff: stricter layering can slow urgent hotfixes unless your bypass model is explicit and audited. Do not wait for a production outage to define bypass policy.
2) Integration plane: merge queue
Merge queue shines when many engineers merge to the same branch. It removes “rebase roulette” and validates the queue with up-to-date combined changes. This is exactly where branch protections alone usually break down under traffic.
Tradeoff: queue throughput depends on CI duration and flaky tests. If your suite is noisy, merge queue can become a bottleneck. You may need build concurrency tuning and stricter flaky-test quarantine.
3) Credential plane: OIDC
OIDC does not make your pipeline magically secure, but it removes a major class of risk: static cloud credentials in repository secrets. GitHub issues a short-lived token per job, and your cloud trust policy decides who gets what.
Tradeoff: initial trust-policy setup takes care. If you over-broaden subject or audience conditions, you can recreate the same risk in a different form.
Reference implementation you can ship this week
I recommend rolling this out in two passes. First, make checks deterministic for queue events. Then enforce with rulesets.
Step A: make CI queue-aware
name: ci-core
on:
pull_request:
branches: [main]
merge_group:
permissions:
contents: read
id-token: write
jobs:
test-and-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- run: npm ci
- run: npm test -- --runInBand
- run: npm run build
deploy-preview:
if: github.event_name == 'pull_request'
needs: test-and-build
uses: your-org/platform/.github/workflows/preview-deploy.yml@9b4db0f6f4d8d5dc4ad7a7f6f3a3d4a7e9ab2c18
secrets: inherit
The non-obvious line is merge_group:. Without it, required checks may never report for queued merges, and your queue stalls with “waiting for status checks.”
Step B: standardize policy with reusable workflows
# .github/workflows/policy-gate.yml
name: policy-gate
on:
workflow_call:
inputs:
run_sast:
required: false
type: boolean
default: true
jobs:
gate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Secret scan
run: ./scripts/check-secrets.sh
- name: Dependency policy
run: ./scripts/check-dependencies.sh
- name: SAST
if: inputs.run_sast
run: ./scripts/run-sast.sh
Then call this from app repos. Keep the reusable workflow in a platform repo and pin by commit SHA, not just a mutable branch tag. GitHub docs explicitly call SHA pinning the safer path.
Step C: move deploy auth to OIDC
At minimum, set permissions: id-token: write only in jobs that truly need cloud auth, and scope cloud trust policy to repository, branch, and workflow identity claims. Keep deploy and non-deploy jobs separate so you are not giving token minting rights to every test job.
Troubleshooting when rollout gets messy
Problem 1: Merge queue never completes
Symptom: PR is queued, but checks stay pending forever.
Likely cause: Required workflow is missing the merge_group trigger.
Fix: Add merge_group to every workflow that reports required checks, then re-queue PRs.
Problem 2: Rules feel inconsistent across repos
Symptom: One repo blocks force push, another allows it unexpectedly.
Likely cause: Mixed legacy branch protections and partial ruleset rollout.
Fix: Document final source of truth. Use org-level baseline rulesets, then repo exceptions with explicit rationale.
Problem 3: OIDC login works in one workflow and fails in another
Symptom: Cloud provider denies token exchange.
Likely cause: Trust policy subject/audience mismatch, or missing id-token: write.
Fix: Compare the expected claims in trust policy against the job’s actual workflow/ref context. Tighten scope but match real workflow identity.
How this connects to the rest of your delivery system
If you already adopted structured dependency triage, this setup complements it. Start with this related guide: Dependabot triage workflow.
If your deployment identity still depends on long-lived keys, pair this with: multi-account OIDC session policy guardrails.
If your organization is tuning merge reliability, read: merge queue with workflow rulesets and merge_group CI and GitHub Actions pipeline hardening.
FAQ
Do rulesets replace branch protection completely?
Not always on day one. They can coexist, and rules aggregate. In practice, teams gradually migrate to rulesets for clarity and visibility, then retire redundant branch protection rules.
Will merge queue slow us down?
Short term, sometimes yes, especially with flaky tests. Medium term, high-traffic repos usually gain throughput because fewer rebases and fewer broken-main incidents mean less hidden rework.
Is OIDC enough to secure deployments by itself?
No. It removes static credential risk, which is huge, but you still need least-privilege roles, environment protections, audit logging, and explicit deployment approvals where appropriate.
Actionable takeaways
- Enable queue-compatible CI first: add
merge_groupto required workflows before enforcing merge queue. - Define one org-level baseline ruleset, then add repo-level exceptions only with written justification.
- Move deploy authentication to OIDC and scope trust policies to repo, branch, and workflow claims.
- Pin reusable workflows by commit SHA and keep bypass permissions intentionally small.
- Measure success by fewer broken-main rollbacks, not by “number of enabled GitHub features.”
Primary keyword: github rulesets and merge queue
Secondary keywords: github actions merge_group event, required workflows github actions, oidc short-lived credentials

Leave a Reply