ci: Actions re-run the full local gate on every push, burning runner time #426
Labels
No labels
breaking
bug
documentation
enhancement
epic
good first issue
help wanted
refactoring
resolution/duplicate
resolution/invalid
resolution/wontfix
security
severity/critical
severity/high
severity/low
severity/medium
status/abandoned
status/blocked
status/needs-decision
status/needs-info
status/needs-verification
testing
upstream
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
sixfold-space/madtea#426
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
Every push and merge triggers CI Actions that re-run the same
./scripts/gate.shplus conformance that the contributor already ran locally beforefinish(the collapsed gate plus conformance check set, #362). The runner is a VM; if its Go caches are not persisted between runs, it rebuilds and re-tests from scratch each time. Even a docs-only change re-runs the full heavy analyzer suite and conformance.Concrete instance: #373 was gated green locally - build, vet, gofmt, staticcheck, govulncheck, gosec, test-race, mcp-conformance - and CI then re-ran the identical set on merge.
Why it matters
CI time is a real cost: runner occupancy, wall-clock before a merge reads as "done", and it compounds because the workflow is many small branch-to-finish cycles, not a few large ones. The same state is confirmed two or three times (local gate, sometimes a manual re-run, then CI).
Candidate levers (directions, not a decided design)
GOCACHE/GOMODCACHEacross runs). Likely the single biggest win with zero coverage loss - a cold cache rebuilds and re-tests everything.Open question I need to settle
Should CI ever TRUST the local gate and skip re-verification for a locally-gated merge? That trades CI's independence - it verifies the merged state regardless of a committer's local claim, and it catches environment drift - for speed. This is a CI trust-boundary question, not an efficiency tweak, and I am not going to answer it as a side effect of a speed fix. The cache and path-filter levers above need no such trade, so they come first.
Acceptance criteria
The no-trade levers come first, because neither costs any coverage: persist and warm the runner Go caches (
GOCACHE/GOMODCACHE) across runs, which is likely the biggest single win, and path-filter the jobs so a docs-only diff skips the code analyzers and conformance while a code diff skips nothing. Measuring where the CI minutes actually go should precede either, so the fix targets the real cost instead of the suspected one.Plan: land the path-filter first, since it is repo-only and loses no coverage, and file the runner cache-persist plus a measurement pass separately.
The trust-boundary question in the body stays open and separate. It is not part of this speed work.
Grounded the where-does-the-time-go question against the config. Findings:
gatejob is./scripts/gate.sh --no-conformance(ci.yml), withmcp-conformanceas its own job, so conformance runs once per CI run, not twice.GOCACHE/GOMODCACHEare runner docker volumes mounted only when the event is notpull_request(the #272 fork-cache-poisoning guard). So push/schedule runs are warm; everypull_requestrun compiles cold and re-runs the full heavy suite. That is the slow path, and it is the one now on the merge critical path.needs:, but one runner). Noconcurrency:block, so a superseded push keeps running instead of being cancelled.paths:filters, so a docs-only diff still runs the full analyzer suite.ci / gate (pull_request)+ci / mcp-conformance (pull_request). A path-filter that SKIPS the gate job on a docs-only diff would leave the required check unreported and deadlock the merge, so a path-filter has to report success while no-oping the heavy steps, not skip the job.First no-trade lever going in (PR incoming, refs this issue): a
concurrency: cancel-in-progressblock, cancelling superseded runs on non-default refs while letting main runs complete. No coverage or trust change.Still open for a ruling, unchanged:
Added a lever beyond the ones listed above: SHA-dedup of the push re-run (PR #435, auto-merges on green).
Because merges are fast-forward-only, main's tip is the PR head commit byte for byte, and CI already ran the full gate on that exact SHA as a
pull_requestevent. Thegateandmcp-conformancejobs now check, onpushonly, for a prior(pull_request)success on the SHA and skip the heavy steps when it exists. It fails safe: a PR run is never skipped, and any doubt (no prior success, status API unreachable) runs the full job, so a direct-to-main push still gets the gate.Narrow ruling for this lever only: it trusts a prior CI run on the identical commit, not a contributor's local gate, so I am treating it as no-trade. That PR run is also the cold one (no cache, #272), i.e. the stronger check, so nothing is lost.
This does NOT settle the open question in the body. "Should CI ever trust the local gate and skip re-verification" stays open and keeps
status/needs-decision. The path-filter, the PR-cache trade, and the measurement pass are all still unchanged.Decided: routine internal CI maintenance, not a standing product decision. Clearing needs-decision.
This is madtea's own repository CI (the gate + conformance re-run), so it is project devops, not a product capability. It does not need product-decision ceremony; the safe posture is also the live-correct one, so I am applying it.
Posture:
Speed comes from the no-trade levers, none of which trade coverage:
The remaining levers are execution, not a decision.