Skip to content

tools: the merge gate reads a merged PR as green, and a skip can outrank a failure #16046

Closed

Description

@mrveiss

Two blind spots in scripts/pr_required_gate.py

Both found by using the gate, not by reading it. PR opened alongside this issue.

1. A merged PR reads CONTEXTS-GREEN. Its required contexts all succeeded — before it landed. The verdict is true and useless, and read as clearance it says "ready to merge" about something already merged.

That is not hypothetical: I read it as clearance and told another session a merge window was open, alongside three PRs that had merged hours earlier.

2. A skip can outrank a conclusive failure. The gate takes the newest observation per context name. That is right for supersession — one workflow re-run leaving cancelled behind a success — and wrong for two workflows publishing one context name, where a path-filtered shim can land skipped AFTER a real failure on the same commit.

Observed on #16027: the gate reported green while the merge button stayed red. The tool disagreeing with the thing it exists to predict.

The property underneath, which matters more than either fix

These are the third and fourth states this tool could not see. The others:

  • pending contributing to no bucket, so a histogram of pass/fail cannot see a suite that has not started
  • a conflicted PR producing no runs at all, which reads identically to "CI has not started"
  • a superseded cancelled taken as the current answer

Four symptoms, one design property: a check that enumerates conditions is blind to the conditions it does not enumerate, and every blind spot reads as success.

The corollary is the part that should shape the code rather than sit in an issue: the design has to assume there is a fifth. A verdict that cannot say "I do not know what I did not look at" will always spend its blind spots as green — which is the same failure as a reach floor bound to findings rather than to what was examined.

Acceptance criteria

  • A merged or closed PR reports a non-green verdict naming its state, and exits non-zero
  • A skip never overrides a conclusive result in either ordering — the API returns runs unordered, and the case where the skip is seen first is the one a naive fix misses
  • Supersession still resolves to the newest conclusive observation, so cancelled → success and failure → success are unaffected
  • A lone skip is still the answer for a path-filtered context nothing else reported — otherwise "a skip is never authoritative" becomes "path-filtered contexts never report"
  • Each fix has a contrast: an OPEN PR with green contexts is still green, and the skip rule does not make the gate refuse everything
  • The blind-spot property is stated where a future editor reads it, not only in this issue

Note for whoever extends it next

A mutation that ignored skips entirely passed every test written for finding 2, because all of them put the conclusive run later in the list. The unordered-API ordering is the only path to the promotion branch. Worth assuming the same is true of the fifth blind spot: the test that would catch it is probably one nobody has thought to write, not one that exists and fails.

Activity

  1. mrveiss commented on Sep 8, 2026

    @mrveiss
    OwnerAuthor

    Closing. All six criteria verified against merged base (7a647890f6, PR #16052), each with the code and the named test that proves it.

    # Criterion Evidence on origin/Dev_new_gui
    1 Merged/closed PR reports non-green naming its state, exits non-zero pr_required_gate.py:417-418 → NOT-OPEN ({pr_state}). pr_required_gate_test.py:363 test_a_merged_pull_request_is_not_reported_as_mergeable asserts gate.main(["123"]) == 1 at :391 and NOT-OPEN (MERGED) in the output at :392
    2 Skip never overrides a conclusive result in either ordering _INCONCLUSIVE at :73; both directions handled at :108 and :110. Tests :311 test_a_skip_never_overrides_a_conclusive_result_whatever_the_order and :417 test_a_conclusive_result_wins_even_when_the_skip_is_seen_first
    3 Supersession still resolves to newest conclusive :334 test_supersession_is_still_newest_wins, plus :34 test_a_superseded_cancelled_before_a_success_reads_green
    4 A lone skip is still the answer for a path-filtered context :354 test_a_skip_is_still_the_answer_when_nothing_else_reported — "A path-filtered context that only ever skips is legitimately green"
    5 Each fix has a contrast :395 test_an_open_pull_request_with_green_contexts_is_still_green for the state fix; :354 is the contrast for the skip rule, so it does not become "path-filtered contexts never report"
    6 The blind-spot property stated where a future editor reads it pr_required_gate.py:303-304 and :346-352, in the module, not only here

    On the note for whoever extends it next. The warning was that a mutation ignoring skips entirely passed every test written for finding 2, because all of them put the conclusive run later. :417 is the test that closes that specific path — it is the ordering the unordered API produces and the only route to the promotion branch.

    What I am not claiming. Criterion 1 was found by using the gate — I read CONTEXTS-GREEN on merged PRs as clearance and told another session a merge window was open. That is fixed and tested. But the issue's own corollary is that there is a fifth blind spot, and closing this does not mean the tool now sees everything; it means the four named states are handled and the verdict says what it did not examine (:436 test_the_verdict_names_what_it_did_not_examine). I have used the NOT_EXAMINED line in every sweep today, and it is what stopped me merging on contexts alone — threads are still checked separately, by hand.

  2. added 2 commits that reference this issue on Sep 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingfrontend

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions