Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .mergify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
merge_queue:
max_parallel_checks: 1

queue_rules:
- name: default
batch_size: 1
checks_timeout: null
merge_method: merge
queue_conditions:
- -draft
- check-success=verify
- check-success=Unfret
- check-success=live-gate
Comment on lines +11 to +13

@unfret-eal unfret-eal Bot Oct 2, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Critical · Auto-queue gates accept spoofable check names

The queue conditions (L11-13) and merge-protection success conditions (L27-29) match verify, Unfret and live-gate by name only, with no source check. auto_merge_conditions (L18) admits any PR to main with no author or approval restriction. Scenario: a non-draft PR adds a workflow with jobs named live-gate/Unfret (and can edit the verify workflow). The PR could come from a fork whose Actions run, or from the builder bot. Those jobs post successful check-runs with the gated names on the PR head. Mergify then auto-queues the PR and, with empty merge_conditions, merges it. The operator never posted live-gate and the real Unfret may never have passed. Before this diff, merges were manual. Branch protection that requires human approval would block this, but it is not visible here and may exist outside this diff. Also reported for this defect: - Unqualified check names allow gate spoofing (.mergify.yml:11-13): Mergify's bare check-success form accepts a successful check with the matching name regardless of its producer. A pull request can publish trivially successful checks named verify, Unfret, and live-gate; these conditions then pass even if the trusted checks failed or never ran. The same unqualified names at lines 27-29 let the spoof satisfy merge protection too, allowing an untrusted PR to be auto-queued and merged without the mandated gates. - Authenticate the live-gate result's source (.mergify.yml:13): Mergify's unqualified check-success accepts a matching check from any app, not just the operator's commit status. A fork contributor can add a pull_request Actions job named live-gate that simply succeeds; GitHub publishes that check even with a read-only token. For an up-to-date, non-draft PR whose genuine verify and Unfret pass, this line and line 29 accept the workflow-produced result, allowing automatic merging without the operator's installed-candidate test required by AGENTS.md. Both gate conditions need to distinguish the trusted live-test result from PR-controlled check runs.

Also affects:

Withdrawn in the latest review.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied the operator-confirmed correction in f65069f: both queue_conditions and success_conditions now include -files~=^\.github/, with a short explanatory comment. PRs touching .github/ cannot auto-queue and require manual operator queueing. No other mechanism added.

merge_conditions: []
Comment on lines +9 to +14

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Merged tree goes untested

If main moves after a PR passes verify, Unfret, and live-gate, these queue conditions still allow the older branch to merge. The merge commit can then contain a tree the live test never covered. Require the branch to be up to date with main so a move calls for fresh checks and live evidence.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Operator adjudication: no change. With in-place checks, Mergify updates a behind branch; the new head must pass the queue conditions again, including fresh exact-head live-gate after operator re-testing.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. With max_parallel_checks: 1, batch_size: 1, and no additional merge_conditions, Mergify uses the in-place queue flow: when main advances, it updates the queued branch and restarts validation on the resulting head. Because check-success results are commit-specific, the old verify, Unfret, and exact-head live-gate results cannot satisfy the new head; fresh checks and live evidence are required before merging. No configuration change is needed.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Checks can miss newer main

If main changes while a PR waits, its head can keep the earlier green checks. merge_conditions: [] does not require the branch to catch up or the checks to run against the new base. Mergify can then merge code that was not tested against current main. Require the branch to be up to date before merging, with fresh checks after it changes.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Operator adjudication: no change. With in-place checks, Mergify updates a behind branch; the new head must pass the queue conditions again, including fresh exact-head live-gate after operator re-testing.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood. Given Mergify’s in-place update behavior, advancing main produces a new PR head and causes the queue conditions—including the exact-head live-gate—to be evaluated again before merge. That addresses the stale-base concern, so no change is needed.

Tip: You can customize Greptile's behavior for this repo with .greptile/rules.md and .greptile/config.json.


merge_protections_settings:
auto_merge_conditions:
- base = main
# Workflow changes never auto-queue; the operator queues them by hand.
- -files~=^\.github/

merge_protections:
- name: Queue ready pull requests
description: Automatically queue non-draft pull requests targeting main after verify, Unfret, and live-gate pass on the head.
if:
- base = main
success_conditions:
- -draft
- check-success=verify
- check-success=Unfret
- check-success=live-gate

@unfret-eal unfret-eal Bot Oct 2, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium · Stale queue admission invalidates the exact-head live gate

These admission conditions allow a PR whose branch is behind main to enter the serial in-place queue while all three checks pass on its old head. Mergify then merges main into the PR branch, creating a new head SHA without the operator-posted live-gate. The gate required here and at line 31 cannot pass until the operator installs, live-tests, and publishes another status on that Mergify-created head, so the admitted PR cannot complete automatically. No automatic live-gate publisher is present. An up-to-date admission guard is still missing; the carried repair remains unresolved. Also reported for this defect: - Queue updates invalidate the exact-head live gate (.mergify.yml:13): A PR can satisfy this condition and enter the serial queue, then become stale when an earlier PR merges. Mergify's in-place update creates a new head SHA without the manually published live-gate, so the PR cannot complete automatically until an operator installs, retests, and posts a new status for the Mergify-created commit. - In-place update strands exact-head live-gate (.mergify.yml:31): Still present. Serial in-place checks (L2 max_parallel_checks: 1, L6 batch_size: 1) make Mergify merge main into a queued PR that has fallen behind, for example after an earlier queued PR merges. That creates a new head SHA. The operator's manual live-gate exists only on the tested SHA, and admission (L9-13) has no up-to-date guard. So L13 and L31 cannot pass on the head Mergify would merge, and nothing publishes live-gate automatically. With checks_timeout: null (L7), the PR either waits at the serial queue head, blocking every later PR, or is dequeued. Either way, progress requires an operator live-gate on a Mergify-made commit nobody live-tested. - In-place update strands exact-head live-gate (.mergify.yml:33, .mergify.yml:10-15, .mergify.yml:15, .mergify.yml:29): Carried, unchanged at this head. Serial in-place checking (L2, L6) merges main into a behind PR, creating a new head SHA. The operator's live-gate exists only on the old SHA, so L33 (and L15) cannot pass on the head Mergify would merge. With checks_timeout null (L7), the PR either waits at the serial queue head, blocking later PRs, or is dequeued. Either way, progress requires a live-gate on a Mergify-made commit the operator never tested. Trigger: a queued PR falls behind after another PR merges. Also reported for this defect: - Queue updates invalidate the approved live-gate (.mergify.yml:10-15): These admission conditions accept a green PR whose branch is behind main, including one made stale by an earlier queued merge. The serial in-place queue then merges main into that branch, creating a new head SHA. The operator's live-gate status remains on the old SHA, while lines 15 and 33 require it on the new one and no automatic publisher exists. The admitted PR consequently cannot complete its automatic merge until the operator installs and live-tests the Mergify-created head and posts another status. The prior missing up-to-date admission guard remains unresolved. - Updating a stale queued PR invalidates its live gate (.mergify.yml:15): A PR can satisfy this exact-head status while current, then become stale when an earlier PR merges. The serial in-place queue updates that PR to a new head, where the manually posted live-gate no longer exists; the protection therefore cannot pass until an operator tests and posts another status on Mergify's new commit, and checks_timeout: null leaves the wait unbounded. Thus ordinary queue advancement can strand or dequeue an otherwise ready PR instead of automatically merging it. - In-place update drops exact-head live-gate, stalling queue (.mergify.yml:29): batch_size: 1 (L6) and max_parallel_checks: 1 (L2) make Mergify check queued PRs in place. Scenario: a ready PR is behind main because another PR merged first. Mergify merges main into the PR branch and creates a new head SHA. The operator's exact-head live-gate exists only on the old SHA. So check-success=live-gate cannot pass on the head Mergify is about to merge, whether it comes from merge protections (L29), the queue condition (L13), or a live-gate check Mergify injects from branch protection. With checks_timeout: null, the serial queue then waits indefinitely, or the PR is dequeued. Every later PR is blocked until the operator posts live-gate on a Mergify-made commit they never live-tested. The planned proof PR will be up to date with main, so it will not exercise this path.

Now at .mergify.yml:9-13

Also affects:

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Operator adjudication: no change. This is the documented fails-loudly hold: an updated head lacks exact-head live-gate and leaves the queue; the operator re-tests that head and re-posts live-gate before it can proceed.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Operator adjudication: no change. Mergify removes a PR from the queue when queue_conditions stop matching, including a new head without exact-head live-gate, so the queue does not stall. The operator re-tests the new head and re-posts live-gate.


commands_restrictions:
requeue:
conditions:
- sender-permission >= write
Loading