fix(ci): hold the Codecov verdict until every upload has landed - #9807
Merged
Conversation
`notify.after_n_builds` fires on the Nth upload to ARRIVE, and arrival order does not match importance. validate-code's rees / control-plane / engine reports land in ~2 minutes; validate-tests is still producing the backend lcov that covers most of src/**. So codecov/patch posted a conclusion computed from a partial report — on #9799 it went pass, then fail, while validate-tests was still pending, with Codecov's own comment printing `backend | BASE 1 | HEAD 0`. That is not cosmetic: the gate closes a contributor PR on any red check, codecov/patch included, so a transient partial-report red can close a PR that has no defect. `require_ci_to_pass` gates the final verdict but does not suppress the interim status, and `wait_for_ci` (already on by default) did not prevent it either. Raising the count cannot fix this — the reasoning preserved on `after_n_builds` explains why a floor above the guaranteed minimum leaves codecov/patch permanently un-posted on PRs that produce fewer uploads — because a count cannot express "wait for the backend upload specifically". `manual_trigger` can: Codecov sends nothing until `codecovcli send-notifications` runs, and the new codecov-notify job runs it once, after every uploading job has finished. The notifier is deliberately unable to fail a PR. It is not in `validate`'s needs, and every failure path — a missing pipx, a failed install, a rejected notification — warns and exits 0. If it never runs, no status posts, the check set stays incomplete and the gate waits: it cannot merge unreviewed and cannot false-close, which is the whole point. `!cancelled()` rather than `always()` so a run superseded by a newer push does not notify on a stale sha, and the job skips entirely when neither upload job ran, since notifying on a commit Codecov has no reports for is an error rather than a verdict. Closes #9801
Contributor
|
Important 🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨 ⏳ LoopOver is waiting…LoopOver has seen this pull request and is waiting on CI checks to finish before reviewing it. This comment will update once the review runs. 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed · 🟨 Waiting |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
loopover-ui | 3537439 | Commit Preview URL Branch Preview URL |
Jul 29 2026, 10:29 AM |
Contributor
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #9807 +/- ##
=======================================
Coverage 90.46% 90.46%
=======================================
Files 918 918
Lines 113935 113935
Branches 27049 27049
=======================================
Hits 103073 103073
Misses 9533 9533
Partials 1329 1329
Flags with carried forward coverage won't be shown. Click here to find out more. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
codecov/patchposts a red conclusion computed from a partial coverage report, whilevalidate-testsis still running.notify.after_n_builds: 1fires the verdict on the first upload to arrive, and arrival order doesnot match importance:
reesvalidate-codecontrol-planevalidate-codeenginevalidate-codebackendvalidate-testsObserved on #9799:
codecov/patchwentpass(afterrees), thenfail(afterengine), whilevalidate-testswas stillpendingthroughout. Codecov's own comment printed it plainly:This is not cosmetic. The gate closes a contributor PR on any red check,
codecov/patchincluded. A PR can be closed on a status computed from an incomplete report — one that would have
resolved green minutes later. Same class as #9798: a CI signal corresponding to no real defect.
require_ci_to_pass: truegates the final verdict but does not suppress the interim status, andwait_for_ci(alreadytrueby default) did not prevent it either.Why not just raise
after_n_buildsThe existing comment already rules it out, correctly:
A count cannot express "wait for the
backendupload specifically." Any floor high enough toguarantee it would hang the check on PRs producing fewer uploads. That reasoning is preserved on the
key rather than deleted.
The fix
codecov.notify.manual_trigger: true— Codecov sends nothing until told. A newcodecov-notifyjobruns
codecovcli send-notificationsonce, and itneedsevery uploading job, so it is the only placethat can truthfully say "that's all of them."
The notifier is deliberately unable to fail a PR. The bug being fixed is a phantom red closing a
good PR, so the fix must not add a new way to do that:
validate's needs, so it can never fail the required check;pipx, failed install, rejected notification — warns and exits 0;merge unreviewed and cannot false-close.
!cancelled()rather thanalways(), so a run superseded by a newer push does not notify on a stalesha. The job skips entirely when neither upload job ran, because notifying on a commit Codecov has no
reports for is an error, not a verdict.
Validation
Shell logic exercised against stubs for every path, all exiting 0:
exit=0, notification sentexit=0—[ -n "$X" ] && args+=(…)would have aborted underbash -eexit=0+::warningpipx installfailsexit=0+::warningpipxmissing entirelyexit=0+::warningtest/unit/ci-codecov-notify.test.ts(9 cases) pins the invariants that are silent when broken — thecommit sha matching the uploads'
override_commit, theneedslist, the non-fatal shape, thebash -etoken trap. Mutation-checked: each ofalways()for!cancelled(), dropping the failureguard, restoring the
&&token trap, and removingvalidate-testsfromneedsfails the guard.npx vitest run test/unit/ci-*.test.ts— 71 passed (12 files, incl. the 9 new)npm run actionlint— clean ·npm run lint:composite-actions— clean ·git diff --check— cleanCloses #9801
Scope
type(scope): short summaryConventional Commit format, for examplefix(api): restore profile access checks.CONTRIBUTING.mdand does not reintroduce GitHub Pages, VitePress,site/, orCNAME.Closes #123) — a linked open issue is required for every contributor PR.Validation
git diff --checknpm run actionlintnpm run typechecknpm run test:coveragelocally;codecov/patchrequires ≥99% coverage of the lines AND branches you changed (aim for 100% on your diff so CI variance does not fail near the threshold). Global coverage is a non-blocking trend with a loose 90% backstop, not the gate.npm run test:workersnpm run build:mcpnpm run test:mcp-packnpm run ui:openapi:checknpm run ui:lintnpm run ui:typechecknpm run ui:buildnpm audit --audit-level=moderateIf any required check was skipped, explain why:
codecov.ymlkey, and one test file. I ran what can regress:npm run actionlint,npm run lint:composite-actions, and all 12test/unit/ci-*.test.tsworkflowguards (71 passing), plus the stubbed shell matrix above. No
src/**line changes, socodecov/patchhas nothing to measure here; the MCP/UI/workers/audit paths are untouched and CI runs them anyway.
Safety
UI Evidencesection below with JPG/JPEG or PNG screenshots arranged as organized, captioned, clickable thumbnails. SVG screenshots are not used as review evidence. Review-only screenshots or recordings are not committed to the repository.CODECOV_TOKENis read from secrets into an env var and passed as a CLI flag only when non-empty; itis never echoed, and the
::warningmessages contain only the commit sha. Auth/CORS/session,OpenAPI/MCP and UI are not applicable — this diff adds no runtime code. No changelog edit.
UI Evidence
Not applicable — CI configuration only, no visible UI, frontend, docs, or extension change.
Notes
auto-merged. That seems right for a change to how the coverage gate reports.
premature red sent the investigation down a false path for a while, which is the cost this fixes.
pipxships on the GitHub-hosted Ubuntu images, but the step does not assume it: a missing or failingpipxwarns and exits 0 rather than reddening the check.