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 out of scope; 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¶
Both were learned the hard way on shakenfist/shakenfist in 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, so 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 never starts CI for an entry until the one
ahead has merged or ejected.
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.
At 1 that wait timer never engages.
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¶
Per-project compliance for this criterion is regenerated every morning by the consistency audit: see the compliance page.