fix(gate): an ignored check must not hold the PR via mergeable_state either - #9820
Conversation
…either gate.ignoredCheckRuns (#9813) excluded the check from LoopOver's own CI aggregate, but mergeable_state is GITHUB's computation and stays "unstable" while the check exists at all -- and derivePrDisposition holds unconditionally on unstable. So the ignore was half-effective: the check no longer failed the gate, and the PR was held anyway. Observed live on #9816 immediately after the config flip, reason "mergeable_state is unstable -- non-required check(s) not passing: Contributor trust". Dismiss an unstable state ONLY when the ignore list fully explains it: at least one ignored run concluded non-passing, our aggregate found no non-required failure, and ciState is not failed. Any other unstable cause still holds, and the flag never rescues a PR held for a different reason. Also make the un-itemized unstable message actionable. It used to say only "a non-required check or status is not passing" -- no check name, no next step. GitHub never says why, and the aggregate can legitimately fail to itemize it (a commit status rather than a check-run, an unreadable app page, a run that appeared after CI was read). Name that ambiguity and point at the Checks tab.
|
Warning ⏸️ LoopOver review result - manual review recommendedReview updated: 2026-07-29 12:49:17 UTC
Review summary Nits — 5 non-blocking
Concerns raised — review before merging
📋 Copy for AI agents — paste into your coding agentDecision drivers
Context & advisory signals — never blocks the verdict
Review context
Contributor next steps
Signal definitions
🧪 Chat with LoopOverAsk LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.
Full command reference: https://loopover.ai/docs/loopover-commands 🧪 Experimental — new and may change. Decision record
🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.
|
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Codecov Report❌ Patch coverage is
❌ Your patch check has failed because the patch coverage (75.00%) is below the target coverage (99.00%). You can increase the patch coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## main #9820 +/- ##
==========================================
+ Coverage 91.59% 94.10% +2.50%
==========================================
Files 915 815 -100
Lines 112700 63095 -49605
Branches 27077 21968 -5109
==========================================
- Hits 103229 59375 -43854
+ Misses 8144 2645 -5499
+ Partials 1327 1075 -252
Flags with carried forward coverage won't be shown. Click here to find out more.
|
…either gate.ignoredCheckRuns (#9813) excluded the check from LoopOver's own CI aggregate, but mergeable_state is GITHUB's computation and stays "unstable" while the check exists at all -- and derivePrDisposition holds unconditionally on unstable. So the ignore was half-effective: the check no longer failed the gate, and the PR was held anyway. Observed live on #9816 immediately after the config flip, reason "mergeable_state is unstable -- non-required check(s) not passing: Contributor trust". Dismiss an unstable state ONLY when the ignore list fully explains it: at least one ignored run concluded non-passing, our aggregate found no non-required failure, and ciState is not failed. Any other unstable cause still holds, and the flag never rescues a PR held for a different reason. Also make the un-itemized unstable message actionable. It used to say only "a non-required check or status is not passing" -- no check name, no next step. GitHub never says why, and the aggregate can legitimately fail to itemize it (a commit status rather than a check-run, an unreadable app page, a run that appeared after CI was read). Name that ambiguity and point at the Checks tab.
24e0005 to
118588e
Compare
…' into fix/ignored-checks-unstable-hold
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
loopover-ui | 96e5d71 | Commit Preview URL Branch Preview URL |
Jul 29 2026, 12:32 PM |
The gap #9813 left open
gate.ignoredCheckRunsexcluded the check from LoopOver's own CI aggregate — butmergeable_stateis GitHub's computation, not ours, and it staysunstablewhile the check exists at all.derivePrDispositionholds unconditionally onunstable. So the ignore was only half-effective: the check no longer failed the gate, and the PR was held anyway.Caught live within minutes of flipping the config on the ORB.
JSONbored/loopover#9816, a shin-core PR — the exact contributor this was meant to unblock:Gate green, review good, held anyway.
The fix
Dismiss an unstable state only when the ignore list fully explains it — all three must hold:
ciState !== "failed".Any other unstable cause still holds, and the flag never rescues a PR held for a different reason (guardrail hit, migration collision, advisory hold) — both pinned by invariant tests. An ignore must never mask a real failure, so if some other check is also red the hold stands.
Also: the un-itemized
unstablemessage was uselessSecond point raised while reviewing this. When the aggregate can't itemize the culprit, the reason read:
No check name, no next step — it just restates the state. GitHub never says why a PR is unstable, and our aggregate can legitimately fail to itemize it: a commit status rather than a check-run, a check from an app whose page we couldn't read, or a run that appeared after CI was sampled. The message now names that ambiguity and points at the one place the answer always exists (the PR's Checks tab), in both the audit reason and the public comment.
Tests
5 new disposition cases: the live regression, "any other unstable cause still holds", "absent flag is byte-identical", "never rescues a differently-held PR", and the positive case (the PR becomes approvable again). 494 pass across the touched files;
tsc, docs-drift, dead-source-files, engine-parity green.