feat(gate): add the block tier, so an enforcing screenshot gate holds a PR instead of destroying it - #9964
Conversation
… a PR instead of destroying it `close` and `advisory` were the only options: destroy the PR, or enforce nothing. That middle matters because reviews here are ONE-SHOT for contributor work -- there is no "changes requested, try again" state -- so an enforcing gate converted every miss into unrecoverable loss. Right for genuine slop, wrong for a PR that simply has not attached screenshots yet, and the contributor cannot reopen. `block` holds the PR (no merge, never a close) and says exactly what is missing, so the hold is actionable rather than a silent stall. It clears itself the moment evidence appears -- a body table, or a successful bot capture. The hold joins MERGE_HOLD_INPUTS, so every surface folds it in by construction rather than by three edits that can each be forgotten. It respects the same two exemptions the close does: a live capture retry (the evidence may still be coming) and a degraded enforcement (#9881's first half -- the bot cannot produce evidence in this repo at all). A hold with an unmeetable condition is just a slower close. Both label paths carry the message: the merge-authorized fallback and the disposition ternary. They cover different repo configurations -- the fallback serves merge-autonomy repos, the ternary serves label-only ones -- and either missing it would hand half the fleet a bare label with no reason. Also hoists that comment ternary's eligibility guard, which was restated in all six arms. Reaching a later arm already proved the earlier arm's identical guard true, so every copy after the first was structurally unreachable-false -- dead sub-branches that could never be covered because they could never be hit. Evaluated once now, and the whole chain is reachable. Closes #9881
|
Warning ⏸️ LoopOver review result - manual review recommendedReview updated: 2026-07-30 22:20:14 UTC
Review summary Nits — 6 non-blocking
Concerns raised — review before merging
📋 Copy for AI agents — paste into your coding agentDecision drivers
Context & advisory signals — never blocks the verdict
Linked issue satisfactionAddressed 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
Visual preview
Click any thumbnail to open the full-size screenshot. Before = production · After = this PR's preview deploy. Scroll preview
A short scroll-through clip (desktop) — click either thumbnail to open the full animation. Evidence for scroll-linked behavior a single screenshot can't show. 🟩 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. |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
loopover-ui | 0149387 | Commit Preview URL Branch Preview URL |
Jul 30 2026, 09:56 PM |
Bundle ReportChanges will increase total bundle size by 35 bytes (0.0%) ⬆️. This is within the configured threshold ✅ Detailed changes
Affected Assets, Files, and Routes:view changes for bundle: loopover-uiAssets Changed:
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #9964 +/- ##
==========================================
- Coverage 91.87% 91.87% -0.01%
==========================================
Files 928 928
Lines 113697 113704 +7
Branches 27421 27427 +6
==========================================
+ Hits 104460 104463 +3
Misses 7936 7936
- Partials 1301 1305 +4
Flags with carried forward coverage won't be shown. Click here to find out more.
|
…om VALID_ACTIONS The warning still named only close/advisory after JSONbored#9964 added the block tier, so an operator who mistyped the action was pointed at a vocabulary that no longer matched what the gate actually accepts. Interpolate VALID_ACTIONS and the resolved default instead of restating them as a literal, mirroring normalizeSelfLoopAutonomy's derived-vocabulary pattern in ams-policy-spec.ts.
…om VALID_ACTIONS (#10105) The warning still named only close/advisory after #9964 added the block tier, so an operator who mistyped the action was pointed at a vocabulary that no longer matched what the gate actually accepts. Interpolate VALID_ACTIONS and the resolved default instead of restating them as a literal, mirroring normalizeSelfLoopAutonomy's derived-vocabulary pattern in ams-policy-spec.ts. Co-authored-by: bitfathers94 <237535319+bitfathers94@users.noreply.github.com>


Closes #9881 (second half — the first, degrading enforcement where the bot cannot produce evidence, is already on main)
Why a middle tier
closeandadvisorywere the only options: destroy the PR, or enforce nothing.That middle matters here specifically because reviews are one-shot for contributor work. There is no "changes requested, try again" state, so an enforcing gate converted every miss into unrecoverable loss — right for genuine slop, wrong for a PR that simply has not attached screenshots yet. The contributor cannot reopen.
blockholds the PR (no merge, never a close) and says exactly what is missing, so the hold is actionable rather than a silent stall. It clears itself the moment evidence appears — a body table, or a successful bot capture.Correct by construction
The hold joins
MERGE_HOLD_INPUTS, so every surface folds it in automatically rather than through three edits that can each be forgotten — the table exists precisely because a hold once got declared and silently not honoured.It respects the same two exemptions the close does: a live capture retry (the evidence may still be coming) and a degraded enforcement (the bot cannot produce evidence in this repo at all). A hold with an unmeetable condition is just a slower close.
Both label paths carry the message — the merge-authorized fallback and the disposition ternary. They serve different repo configurations (merge-autonomy vs label-only), and either one missing it would hand half the fleet a bare label with no reason.
A dead-branch cleanup found on the way
That comment ternary restated its eligibility guard (
!linkedIssueCloseInFlight && !unlinkedIssueMatchViolated && reviewGood) in all six arms. Reaching a later arm already proved the earlier arm’s identical guard true, so every copy after the first was structurally unreachable-false — dead sub-branches that could never be covered because they could never be hit. Hoisted to a single evaluation; the whole chain is now reachable.That is also what surfaced the one genuinely untested configuration: the disposition ternary only fires where merge autonomy is off and
review_state_labelis on, because the fallback otherwise claims the label first. Now covered.Verification
Full suite: 25,895 passing, zero failures. Changed-line coverage: zero uncovered statements, zero uncovered branches (measured against a full-suite run, not a scoped one).
contract:api-schemas,ui:openapi,db:schema-drift,dead-exportsandfixture-clock-racesall clean.blockis additive — no repo gets it without opting in, so existingclose/advisorybehaviour is byte-identical.