Skip to content

fix(openapi): document /v1/opportunities/find + /v1/issue-rag/retrieve - #9428

Closed
hurryup52 wants to merge 1 commit into
JSONbored:mainfrom
hurryup52:fix/9310-openapi-opportunities-issue-rag-v2
Closed

fix(openapi): document /v1/opportunities/find + /v1/issue-rag/retrieve#9428
hurryup52 wants to merge 1 commit into
JSONbored:mainfrom
hurryup52:fix/9310-openapi-opportunities-issue-rag-v2

Conversation

@hurryup52

Copy link
Copy Markdown
Contributor

Closes #9310

(Supersedes #9423, which the gate auto-closed for a base-branch conflict — several other OpenAPI-documentation PRs for other routes landed on main in the meantime and touched the same import/registration blocks. This PR is rebased onto current main with those conflicts resolved; no content changes from #9423.)

What

/v1/opportunities/find (OPPORTUNITIES_FIND_PATH, backing the loopover_find_opportunities MCP tool) and /v1/issue-rag/retrieve (ISSUE_RAG_RETRIEVE_PATH, backing loopover_retrieve_issue_context) are fully implemented and access-gated in src/api/routes.ts, but were never registered with the OpenAPI generator — grep for opportunities/find/issue-rag in src/openapi/spec.ts previously returned nothing, so neither route appeared in GET /openapi.json or the committed apps/loopover-ui/public/openapi.json.

Changes

  • src/openapi/schemas.ts: added FindOpportunitiesRequestSchema/FindOpportunitiesResponseSchema and IssueRagRetrieveRequestSchema/IssueRagRetrieveResponseSchema. Field shapes mirror the MCP tools' own Zod shapes verbatim (findOpportunitiesShape/findOpportunitiesOutputSchema and issueRagShape/issueRagOutputSchema in src/mcp/server.ts) so the contract can't silently drift from what the tools actually validate — reused the same MAX_FIND_OPPORTUNITIES_*/MAX_ISSUE_RAG_*/PREFLIGHT_LIMITS constants those shapes already use.
  • src/openapi/spec.ts: registered both response schemas as named components (FindOpportunitiesResponse, IssueRagRetrieveResponse) and added two registerPath POST entries, following the same pattern used for GET /v1/repos/{owner}/{repo}/gate-config/effective (OpenAPI spec is missing GET /v1/repos/{owner}/{repo}/gate-config/effective (documented sibling: live-gate-thresholds) #6611) and the existing POST-with-body routes (e.g. the incident-reports pair). Response codes (400/401/403) are matched to each route handler's actual behavior in routes.ts.
  • apps/loopover-ui/public/openapi.json: regenerated via npm run ui:openapi and committed. npm run ui:openapi:check passes.
  • test/unit/openapi.test.ts: added assertions that both paths are defined, that both response schema components are registered, and that their fields (aiPolicyAllowed, retrievedPathCount) match the MCP output shapes — a regression guard against future drift between the two.

Verification

  • npx vitest run test/unit/openapi.test.ts — 5/5 pass (merged cleanly with the other recently-landed OpenAPI-documentation tests in this file).
  • npm run ui:openapi:check — passes.
  • npm run typecheck — clean.
  • Rebased directly onto current main (no stale-base conflict this time).

@hurryup52
hurryup52 requested a review from JSONbored as a code owner July 27, 2026 18:15
@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@loopover-orb

loopover-orb Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Warning

⏸️ LoopOver review result - manual review recommended

Review updated: 2026-07-27 18:37:11 UTC

4 files · 1 AI reviewer · no blockers · CI green · dirty

⏸️ Suggested Action - Manual Review

Review summary
This PR registers two previously-undocumented POST routes (/v1/opportunities/find and /v1/issue-rag/retrieve) with the OpenAPI generator, adding request/response Zod schemas that mirror the corresponding MCP tools' shapes and reusing existing shared limit constants (MAX_FIND_OPPORTUNITIES_*, MAX_ISSUE_RAG_*, PREFLIGHT_LIMITS) rather than redefining them. The generated openapi.json diff is large but is pure regenerated output (schema reordering plus the two new path/schema entries), consistent with the described `npm run ui:openapi` regen. Test coverage is thin (adds only existence/shape assertions, not response-parsing round-trips), but this is largely acceptable for a documentation-only PR with no runtime behavior changes.

Nits — 5 non-blocking
  • test/unit/openapi.test.ts only asserts the two paths and two schemas exist plus two substring checks; there's no assertion that the request schemas actually validate a realistic accepted/rejected payload for either route.
  • The response schemas (FindOpportunitiesResponseSchema, IssueRagRetrieveResponseSchema) make nearly every field .optional(), which weakens the documented contract against silent drift if the MCP tool's actual output schema is stricter — worth double-checking against findOpportunitiesOutputSchema/issueRagOutputSchema in src/mcp/server.ts.
  • Numeric literals like 500/50/12 in schemas.ts (searchQuery maxLength, languages maxItems, topK maximum) aren't backed by named constants the way the other limits are, which slightly undercuts the PR's own stated goal of avoiding drift from the MCP tools' validation.
  • Add a small round-trip test that safeParse's a representative payload against FindOpportunitiesRequestSchema/IssueRagRetrieveRequestSchema to catch future drift from the MCP tool shapes.
  • Consider exporting the 500/50/12 literals as named constants alongside the other MAX_* imports for consistency.

Decision drivers

  • ✅ Code review — No blockers (1 reviewer)
  • ✅ Gate result — Passing (No configured blocker found.)
Context & advisory signals — never blocks the verdict
Signal Result Evidence
Linked issue ✅ Linked #9310
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (1 linked issue).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 19 registered-repo PR(s), 5 merged, 0 issue(s).
Contributor context ✅ Confirmed Gittensor contributor hurryup52; Gittensor profile; 19 PR(s), 0 issue(s).
Improvement ✅ Minor risk: clean · value: minor · LLM: moderate
Linked issue satisfaction

Addressed
The PR adds FindOpportunitiesResponseSchema/IssueRagRetrieveResponseSchema plus matching request-body schemas mirroring the MCP tools' shapes, registers both as components and registerPath entries in spec.ts following the gate-config pattern, regenerates and commits openapi.json, and adds regression tests asserting both paths and response schema keys (aiPolicyAllowed, retrievedPathCount) appear in

Review context
  • Author: hurryup52
  • Role context: outside_contributor
  • Public audience mode: oss maintainer
  • Lane context: Repository is configured for direct PR review.
  • Public profile languages: not available
  • Official Gittensor activity: 19 PR(s), 0 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Keep the PR focused and include validation evidence before maintainer review.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.
🧪 Chat with LoopOver

Ask 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.

  • @loopover ask <question> answers contribution-quality Q&A with source citations and freshness.
  • @loopover chat <question> answers in natural prose from cached decision-pack facts via local inference (maintainer/collaborator; read-only).
  • A plain-language @loopover mention with a real question is routed to the closest matching read-only command automatically — no exact syntax required.

Full command reference: https://loopover.ai/docs/loopover-commands

🧪 Experimental — new and may change.

Decision record
  • action: hold · clause: ai_review_inconclusive
  • config: f19e0dc6eace40bcf59a6ef69a9b05713f4ac11926ae7c5e631a89d092e70009 · pack: oss-anti-slop · ci: passed
  • note: diverted by the randomized close-audit holdout (calibration: randomized ε-holdout on would-close PRs with propensity logging #8831) — the deterministic pipeline would otherwise have closed this PR
  • record: 9a628017c396549680270fc12b1891da34afd6890b8b06a8dd5b355a03843926 (schema v5, head 13c845e)
Visual preview
Route Viewport Before (production) After (this PR's preview) Diff
/ desktop before /
before /
after /
after /
/ mobile before / (mobile)
before / (mobile)
after / (mobile)
after / (mobile)

Click any thumbnail to open the full-size screenshot. Before = production · After = this PR's preview deploy.

Scroll preview
Route Before (production) After (this PR's preview)
/ before / (scroll)
before / (scroll)
after / (scroll)
after / (scroll)

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.

  • Re-run LoopOver review

@loopover-orb loopover-orb Bot added the gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. label Jul 27, 2026
@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 75.66%. Comparing base (4b761fa) to head (13c845e).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff             @@
##             main    #9428       +/-   ##
===========================================
- Coverage   89.53%   75.66%   -13.88%     
===========================================
  Files         843      277      -566     
  Lines      109947    58400    -51547     
  Branches    26184     6216    -19968     
===========================================
- Hits        98445    44187    -54258     
- Misses      10239    13942     +3703     
+ Partials     1263      271      -992     
Flag Coverage Δ
backend 99.72% <100.00%> (+4.47%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/openapi/schemas.ts 100.00% <100.00%> (ø)
src/openapi/spec.ts 99.55% <100.00%> (+<0.01%) ⬆️

... and 704 files with indirect coverage changes

@loopover-orb loopover-orb Bot added the manual-review Gittensor contributor context label Jul 27, 2026
Both routes were fully implemented and gated correctly but never wired
into the OpenAPI generator, so they were invisible in GET /openapi.json
and the committed apps/loopover-ui/public/openapi.json even though their
MCP tool counterparts (loopover_find_opportunities,
loopover_retrieve_issue_context) already validate full Zod shapes.

Adds FindOpportunitiesRequestSchema/ResponseSchema and
IssueRagRetrieveRequestSchema/ResponseSchema to src/openapi/schemas.ts,
mirroring the MCP tools' own shapes field-for-field
(findOpportunitiesShape/findOpportunitiesOutputSchema and
issueRagShape/issueRagOutputSchema in src/mcp/server.ts) so the contract
can't silently drift from what the tools actually validate. Registers
both response schemas as OpenAPI components and both routes as POST
paths in src/openapi/spec.ts, following the same pattern used for
GET /v1/repos/{owner}/{repo}/gate-config/effective.

Regenerates apps/loopover-ui/public/openapi.json via `npm run
ui:openapi` and extends test/unit/openapi.test.ts with assertions that
both paths are defined and that the response schemas' fields match the
MCP output shapes.

Closes JSONbored#9310
@hurryup52
hurryup52 force-pushed the fix/9310-openapi-opportunities-issue-rag-v2 branch from 13c845e to 5e8e648 Compare July 27, 2026 18:39

@JSONbored JSONbored left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test coverage is thin (adds only existence/shape assertions, not response-parsing round-trips).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. manual-review Gittensor contributor context

Projects

None yet

Development

Successfully merging this pull request may close these issues.

openapi: /v1/opportunities/find + /v1/issue-rag/retrieve missing from spec (MCP tools + schemas already exist)

2 participants