Skip to content

Audit: Merge queue reasonability

What we check

Repositories that enable a GitHub merge queue on their default branch must configure it so entries are processed serially and merged individually:

  • max_entries_to_build: 1 — no speculative stacking.
  • min_entries_to_merge: 1 — merge each green entry immediately.

Repositories without a merge queue are not in scope for this audit; whether to adopt two-stage CI at all is a per-project decision.

The check reads the effective rules for the default branch via GET /repos/shakenfist/<repo>/rules/branches/<branch> and validates the parameters of any merge_queue rule found.

Why these values

Two merge queue mechanics are easy to get wrong, and both were learned the hard way on shakenfist/shakenfist (August 2026):

Speculative stacking multiplies failures. With max_entries_to_build above 1, entry N+1 builds on a temporary branch containing entry N's changes. When any entry ahead fails, the entries behind it are ejected and rebuilt on new SHAs. On a CI cluster whose dominant failure mode is load, this is doubly wrong: the stacked builds waste runs (we observed single PRs rebuilt five times in one day) and the extra concurrent merge groups add the very load that causes the failures. A serialized queue (max_entries_to_build: 1) never starts CI for an entry until the entry ahead has merged or ejected, so a failure can never invalidate work behind it.

Batched merging is pure latency. The queue always builds one merge group and runs CI once per entry, no matter how merges are batched — min_entries_to_merge only controls how many green entries land in a single default-branch update. Raising it makes the queue idle for up to min_entries_to_merge_wait_minutes hoping more PRs arrive, which on a mostly-single-developer project delays every merge and saves nothing. With min_entries_to_merge: 1 that wait timer never engages (it is the timeout for reaching the minimum group size, which a single entry already satisfies).

Other parameters (grouping_strategy, merge_method, max_entries_to_merge, check_response_timeout_minutes) are conventions rather than correctness issues and are not enforced; the fleet reference is shakenfist/shakenfist's "Develop branch" ruleset: ALLGREEN, MERGE, 5, and 360 respectively.

Template

No template — this is a one-time ruleset configuration change.

To inspect the current configuration:

gh api repos/shakenfist/<repo>/rules/branches/develop \
    --jq '.[] | select(.type == "merge_queue") | .parameters'

To fix a non-compliant ruleset, fetch it, rewrite the two parameters, and PUT it back (preserving everything else, including bypass_actors):

gh api repos/shakenfist/<repo>/rulesets --jq \
    '.[] | select(.target == "branch") | {id, name}'
gh api repos/shakenfist/<repo>/rulesets/<id> | jq \
    '{name, target, enforcement, conditions,
      bypass_actors: (.bypass_actors // []),
      rules: (.rules | map(
        if .type == "merge_queue"
        then .parameters.max_entries_to_build = 1
           | .parameters.min_entries_to_merge = 1
        else . end))}' > /tmp/ruleset.json
gh api -X PUT repos/shakenfist/<repo>/rulesets/<id> \
    --input /tmp/ruleset.json

Afterwards, trigger the repository's export-repo-config workflow so the change is captured in .github/exported-config/.

Projects

This table is regenerated daily by the consistency audit workflow from scripts/audit-check.py results; do not edit it by hand.

Last regenerated: 2026-08-23T06:45:38.740880+00:00

Project Status Issue
actions N/A -
agent-python N/A -
client-python N/A -
client-python-k3s compliant -
clingwrap N/A -
cloudgood N/A -
development N/A -
divergulent N/A -
instar compliant -
kerbside compliant -
kerbside-patches N/A -
library-utilities N/A -
occystrap N/A -
private-ci N/A -
ryll compliant -
sfui N/A -
shakenfist compliant -

📝 Report an issue with this page