fix(api): validate discovery routes against the schemas their spec publishes - #10225
Conversation
…blishes FindOpportunitiesRequestSchema and IssueRagRetrieveRequestSchema were published in the OpenAPI document but never wired into the handlers, which validated with independent hand-rolled checks instead — so a generated client accepted bodies the server rejected and vice versa, and the REST route disagreed with its own MCP tool on the same input. Both request schemas now re-derive from @loopover/contract's FindOpportunitiesInput and RetrieveIssueContextInput (single source instead of three restatements), the routes parse against them before the hand-rolled cross-field/trim pass, and that pass no longer silently truncates over-long labels/body or accepts non-integer limit/topK. RetrieveIssueContextInput's owner/repo/title also gain .min(1), since the catalog published empty strings as valid when every handler always rejected them. gen-contract-api-schemas.ts only scanned packages/loopover-contract/src/*.ts, so it never resolved the contract-tool imports these two schemas rely on; extended it to walk one level of subdirectories the same way the top-level scan already does.
|
Tip ✅ LoopOver review result - approve/merge recommendedReview updated: 2026-07-31 13:34:04 UTC
Review summary Nits — 5 non-blocking
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. 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✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #10225 +/- ##
==========================================
- Coverage 92.22% 92.22% -0.01%
==========================================
Files 937 937
Lines 114379 114370 -9
Branches 27655 27647 -8
==========================================
- Hits 105486 105476 -10
- Misses 7588 7589 +1
Partials 1305 1305
Flags with carried forward coverage won't be shown. Click here to find out more.
|


fix(api): validate discovery routes against the schemas their spec publishes
FindOpportunitiesRequestSchema and IssueRagRetrieveRequestSchema were published
in the OpenAPI document but never wired into the handlers, which validated with
independent hand-rolled checks instead — so a generated client accepted bodies
the server rejected and vice versa, and the REST route disagreed with its own
MCP tool on the same input.
Both request schemas now re-derive from @loopover/contract's FindOpportunitiesInput
and RetrieveIssueContextInput (single source instead of three restatements), the
routes parse against them before the hand-rolled cross-field/trim pass, and that
pass no longer silently truncates over-long labels/body or accepts non-integer
limit/topK. RetrieveIssueContextInput's owner/repo/title also gain .min(1), since
the catalog published empty strings as valid when every handler always rejected
them.
gen-contract-api-schemas.ts only scanned packages/loopover-contract/src/*.ts, so
it never resolved the contract-tool imports these two schemas rely on; extended
it to walk one level of subdirectories the same way the top-level scan already
does.
Closes #10040