Remove redundant CI Gate run on push to main - #6136
Conversation
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
evnchn
left a comment
There was a problem hiding this comment.
Posted by Claude Code on evnchn's behalf.
TL;DR:
LGTM — clean, low-risk. Traced the CI graph and ran an independent adversarial pass through a second model (Codex, different lineage). Both converge: dropping the push: main trigger removes a strict subset of what merge_group already validates on the resulting commit, so it adds no coverage.
The whole safety argument rests on one assumption worth confirming:
- The merge queue must be required on
main(not merely enabled), with admin-bypass disallowed.merge_groupis a distinct event frompush/pull_request— GitHub gives no automatic fallback run. If a normal merge or admin direct-push can still land onmain, this removes the only automatic CI Gate run on that SHA.
Ask: can you confirm branch protection requires the queue? If so, ship it. (One minor cache nuance folded below — not a blocker.)
Verified claims (traced from outside the repo)
| Claim in the PR | Status |
|---|---|
| No CI/Actions badge keyed to the push run | ✓ README has only the DOI badge |
publish.yml triggers on tags, not CI Gate |
✓ on: push: tags: |
| Nothing chains off CI Gate's result | ✓ no workflow_run trigger in any workflow |
| push run is a strict subset of merge_group | ✓ ci-gate.yml branches by event — _check unconditional; quick test if: github.event_name != 'merge_group'; full 5-version matrix if: == 'merge_group'. After this change the != merge_group path serves only pull_request. |
Independent adversarial pass (Codex, different model lineage)
Handed Codex the diff + rationale and asked it to find any way the change is wrong/risky/incomplete. It found no hidden merge-queue semantic bug — "clean and low-risk if main truly requires merge queue." Two nuances beyond the required-queue point above:
-
"Exact commit" is slightly overstated. The queue tests the PR changes applied atop the latest target + earlier queued PRs, on a
gh-readonly-queue/...ref. If the queue's merge method is squash/rebase, the commit object landing onmainisn't byte-identical to the tested SHA — same integration state, different SHA. Doesn't touch the redundancy argument; "tests the integration state that will merge" is just more precise wording. -
Default-branch-scoped cache may go cold. With
push: maingone, nothing runs on themainref itself (PRs run on their branches,merge_groupon a temporary queue branch). GitHub Actions caches restore from the current branch or the default branch — so this run plausibly kept amain-scoped uv cache (enable-cache: truein_check/_test) warm for fresh PRs to restore from. Post-change, a brand-new PR branch may cold-start its uv cache on first run. Low severity, performance-only — the miss is the uv download/build cache (keyed on the lockfile), not correctness, and it self-warms per-branch after the first run.
Merge as-is once the required-queue assumption is confirmed; happy to open a tiny follow-up if you want the cache point addressed.
|
@evnchn At the moment a commit to main can be forced by an admin which will bypass CI. But I think that's ok. I rarely use it for tiny changes to documentation, sponsors, CI config itself, ... knowing that only PRs get the full community attention and CI treatment. Sometimes I push dependency updates raised by Dependabot alerts without creating a PR. Technically they could break something if dependencies mess up semver. But that's why I wouldn't do that as the final commit before a release. Long story short: I think an admin bypass is exactly that - a bypass - and should remain possible. |
Motivation
Every commit that lands on
maingoes through the merge queue, where themerge_groupevent runs the full test matrix (5 Python versions × pytest + startup) on the exact commit that will becomemain's new HEAD. The subsequentpush: mainevent then re-runs CI Gate again — but only asquick-test(Python 3.10, pytest), a strict subset of what the merge queue just validated on the identical tree. This second run consumes CI minutes without adding coverage. Nothing depends on its result:publish.ymltriggers on tags, and there is no CI badge keyed to it.Beyond the wasted minutes, this run actively slows down releases: after merging the last feature for a release, we have to wait ~20 min for the redundant push-to-main run to finish before cutting the release — even though the merge queue already validated that exact commit moments earlier.
Implementation
Drop the
push: branches: [main]trigger fromci-gate.yml. CI Gate now runs onpull_request,merge_group, andworkflow_dispatchonly. The merge queue remains the gate for everything that reachesmain.Trade-off: a commit pushed directly to
main— bypassing the merge queue via an admin override — would no longer trigger CI Gate. Since merges go through the required merge queue, this path should not occur in normal operation, and the old push run only ranquick-testanyway (so it would have missed e.g. a Python 3.14-only or startup-only regression regardless).Progress