Repository navigation
Description
Summary
The gate picks check runs by name only, across workflow runs, events and PR bases.
Severity: medium. Found in the 2026-11-04 review of the v1.0.1 change set (all 1.x.x merges since v1.0.0 plus the open PRs that target it), against 1.x.x 21fee6ee8e; area: #171.
Observed
Confidence: false red confirmed (production log). Fail-open confirmed against the real gate script with modelled API data; not exercised on GitHub.
- Location:
.github/workflows/required-merge-gate.yml:770-778(check_run_filter:map(select(.name == $name)) | sort_by(.started_at // .created_at // "") | last) at 21fee6e. - Scenario: The newest check run with the matching name wins, whichever suite it came from. Two outcomes follow.
- False red (observed). subtree: import libbitcoinpqc v0.3.2 signer stack scrub #268 was marked ready_for_review at 17:46:10Z. Its gate run 41101389289 found only the draft-era
Full Validation Gate(conclusionskipped, check suite 111398300834), because the new suite's gate job did not exist yet. It failed withFull Validation Gate concluded skipped.at 17:47:16Z, and a maintainer had to re-run it. - Fail-open (reasoned, and reproduced against the real gate script). When a PR's head SHA already carries
Core Checks Gate/Full Validation Gatesuccesses from another context, the gate accepts them before the PR's own Full Validation exists. The realistic case is a protected-branch head used as a PR head, which is how v1.0.0 shipped (Stage qbit v1.0.0 mainnet release #135,1.0.0→main). That SHA carries both push-event and PR-event suites (confirmed viacheck-suitesfor 9ea1ac9). The 1.x.x→mainintegration needed to deploy Run scheduled validation against the maintained release line and report the tested commit #152/Forward the IBD workflow's reserved timeout inputs to the implemented harness flags #158/fuzz: seed qbit targets and verify replay and mutation #164 is the same pattern. If the last push to1.x.xbefore integration is release-notes-only (doc/release-notes-*.md→public-docs), the push-event gates pass trivially ("does not require full source validation"). The integration PR's gate then returns success immediately on those runs, before the PR-context Full Validation (ofmainmerged with1.x.x) has run. If that validation later fails, the required check is already green.
- False red (observed). subtree: import libbitcoinpqc v0.3.2 signer stack scrub #268 was marked ready_for_review at 17:46:10Z. Its gate run 41101389289 found only the draft-era
- Evidence: production check-run data for 18943b7 and 9ea1ac9, and the job log of run 41101389289. A local probe using the repository's
FakeGhharness and the gate's realrun:script produced rc=0 for push-suite successes with no PR-suite gate run, and rc=1concluded skippedfor a stale draft-era run.
Impact
This one check is the only required check on main and 1.x.x (.github/rulesets/1.x.x.json), and it decides whether a regression can merge into the release line. #171's own contract is "never success while a required check failed, was absent, or could not be read". A check that is absent in the current context but succeeded in another context is outside the tested model.
Suggested fix
Bind the wait to this event's own runs. Either resolve the Full Validation and Core Checks workflow runs for head_sha with the same event (and, for PRs, the same pull_requests[].number), created at or after this gate run, and read their gate jobs through the Actions jobs API. Or filter check runs on check_suite.id from those runs. Treat runs from other suites as absent, which makes them pending, not passed or failed. Add contract tests for a push-context success, a stale skipped run and a re-run in the same suite.
Related: new. #169 and #171 dealt with transient errors and the budget, not with which run is selected.
Activity
ci: run Core Checks for every gate event and test run selection
ci: bind the Required Merge Gate to its own event's runs
ci: run Core Checks for every gate event and test run selection
ci: bind the Required Merge Gate to its own event's runs, fail drafts…