fix(api): validate discovery routes against published schemas - #10155
fix(api): validate discovery routes against published schemas#10155andriypolanski wants to merge 2 commits into
Conversation
Single-source FindOpportunities and IssueRag request bodies from @loopover/contract, enforce them on REST before hand-rolled checks, and stop silent truncation of over-long labels/body. Co-authored-by: Cursor <cursoragent@cursor.com>
…lidation Update validateIssueRagInput and route rejection expectations so length bounds live on RetrieveIssueContextInput and empty/malformed bodies map to invalid_body, matching the discovery schema gate. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Caution 🛑 LoopOver review result - fixes requiredReview updated: 2026-07-31 10:41:23 UTC
Review summary Nits — 5 non-blocking
CI checks failing
Decision 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. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #10155 +/- ##
===========================================
+ Coverage 91.96% 96.42% +4.46%
===========================================
Files 931 6 -925
Lines 113976 3133 -110843
Branches 27516 873 -26643
===========================================
- Hits 104818 3021 -101797
+ Misses 7858 21 -7837
+ Partials 1300 91 -1209
Flags with carried forward coverage won't be shown. Click here to find out more.
|
…together #10155 is flapping in production on beta.7: the label is removed and re-added roughly every 90 seconds -- four cycles in eight minutes, each pair a GitHub write and a subscriber notification. The audit trail shows both operations in the SAME pass, two seconds apart. Three post-plan transforms surface the manual-review hold -- the merge circuit-breaker, the close circuit-breaker, and the close-audit holdout (#8831). All three carried the identical idempotency check, which looks for an existing ADD (`labelOp !== "remove"`) and therefore cannot see a planned REMOVE. When the planner has already scheduled a release -- section 1b does, whenever nothing IT knows about still wants a hold -- the transform appended an add next to that remove, and the executor performed both. Latent until #10116. `noManualReviewHoldWanted` used to include `!mergeableStateUnstable`, which suppressed the release on exactly these PRs and accidentally masked the missing case. Removing that term (correctly -- it was the latch keeping #10098 stuck) let the release fire and exposed the contradiction. #10116 did not create this; the label was previously a sticky latch, quieter but strictly more broken. The planner cannot fix it from its side. Section 1b's doc calls noManualReviewHoldWanted "every reason that would ADD this label, in one place"; it is not, and cannot be, because these three run AFTER planning -- #8831 landed long after that comment was written. Extending the planner's list would just be one more thing to remember on the next transform. So one shared withManualReviewHoldLabel() drops a planned release of the same label before adding, and all three call sites use it. The contradiction becomes unrepresentable where the add happens. The remove is dropped rather than the add skipped: a transform only gets there by having just diverted a merge or a close, so its hold is strictly newer than the release decided before that diversion. Scoped to this label only -- dropping every label remove would silently defeat the stale-disposition-label cleanup, which is its own mutation test. gate.closeAuditHoldoutPct is 20 (the maximum) in the global Orb config, so about one in five would-close PRs is eligible for the path that triggered this. Closes #10164
…together (#10167) #10155 is flapping in production on beta.7: the label is removed and re-added roughly every 90 seconds -- four cycles in eight minutes, each pair a GitHub write and a subscriber notification. The audit trail shows both operations in the SAME pass, two seconds apart. Three post-plan transforms surface the manual-review hold -- the merge circuit-breaker, the close circuit-breaker, and the close-audit holdout (#8831). All three carried the identical idempotency check, which looks for an existing ADD (`labelOp !== "remove"`) and therefore cannot see a planned REMOVE. When the planner has already scheduled a release -- section 1b does, whenever nothing IT knows about still wants a hold -- the transform appended an add next to that remove, and the executor performed both. Latent until #10116. `noManualReviewHoldWanted` used to include `!mergeableStateUnstable`, which suppressed the release on exactly these PRs and accidentally masked the missing case. Removing that term (correctly -- it was the latch keeping #10098 stuck) let the release fire and exposed the contradiction. #10116 did not create this; the label was previously a sticky latch, quieter but strictly more broken. The planner cannot fix it from its side. Section 1b's doc calls noManualReviewHoldWanted "every reason that would ADD this label, in one place"; it is not, and cannot be, because these three run AFTER planning -- #8831 landed long after that comment was written. Extending the planner's list would just be one more thing to remember on the next transform. So one shared withManualReviewHoldLabel() drops a planned release of the same label before adding, and all three call sites use it. The contradiction becomes unrepresentable where the add happens. The remove is dropped rather than the add skipped: a transform only gets there by having just diverted a merge or a close, so its hold is strictly newer than the release decided before that diversion. Scoped to this label only -- dropping every label remove would silently defeat the stale-disposition-label cleanup, which is its own mutation test. gate.closeAuditHoldoutPct is 20 (the maximum) in the global Orb config, so about one in five would-close PRs is eligible for the path that triggered this. Closes #10164


Summary
FindOpportunitiesRequestSchema/IssueRagRetrieveRequestSchemafrom@loopover/contract'sFindOpportunitiesInput/RetrieveIssueContextInput(single source for MCP, REST, OpenAPI).invalid_request+reasonshapes..min(1)onRetrieveIssueContextInputowner/repo/title; stop silent truncation of over-longlabels/body; document the cross-fieldtargets/searchQueryrule on the OpenAPI operation.Test plan
npx vitest run test/unit/find-opportunities.test.ts test/unit/mcp-issue-rag.test.ts test/unit/mcp-find-opportunities.test.ts test/unit/contract-registry.test.ts test/unit/openapi.test.tsnpx vitest run test/integration/api.test.ts -t "REGRESSION: discovery routes"npm run ui:openapiFixes #10040