Skip to content

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

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

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

Conversation

@hurryup52

Copy link
Copy Markdown
Contributor

Closes #9310

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 — 3/3 pass.
  • npm run ui:openapi:check — passes.
  • npm run typecheck — clean.
  • Scoped coverage run on test/unit/openapi.test.ts: src/openapi/spec.ts at 100% statements/functions/lines, 95.65% branches (the one uncovered branch, spec.ts:1468, is pre-existing code in applySecurityMetadata untouched by this diff).
  • Full unsharded npm run test:coverage: no failures related to this change. The run does show ~69 pre-existing failing test files, all in packages/loopover-miner/** (worktree-allocator, purge-cli, rejection-signal, etc.) — unrelated to src/openapi/**, reproducible on a clean upstream/main checkout with none of this diff applied.

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 requested a review from JSONbored as a code owner July 27, 2026 18:01
@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:04:28 UTC

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

⏸️ Suggested Action - Manual Review

Review summary
This PR adds OpenAPI documentation for two previously-undocumented routes (`/v1/opportunities/find`, `/v1/issue-rag/retrieve`) by defining request/response Zod schemas in schemas.ts, registering them with the OpenAPI registry in spec.ts, and regenerating the committed openapi.json. Cross-checking schemas.ts against src/mcp/find-opportunities.ts confirms the field shapes and limits (searchQuery max 500, limit max 50, MAX_FIND_OPPORTUNITIES_* constants) genuinely mirror the underlying MCP tool's validation, so the stated 'can't silently drift' claim holds up. The test additions are shallow (path/schema-presence assertions) but appropriate for a pure-documentation change with no runtime logic.

Nits — 5 non-blocking
  • src/openapi/schemas.ts:1952 hardcodes `.max(500)` for searchQuery instead of importing a shared constant from find-opportunities.ts (which itself hardcodes 500 in the same check) — a named export shared between the two would prevent future drift if either side changes independently.
  • The response schemas mark almost every field `.optional()` (e.g. FindOpportunitiesResponseSchema's `ranked`, `totalCandidates`) even though the actual result type in find-opportunities.ts always populates `ranked`/`totalCandidates`/`status` — slightly looser than necessary for API consumers relying on the generated types.
  • test/unit/openapi.test.ts only asserts string-containment (`toContain("aiPolicyAllowed")`) rather than validating the full schema shape against the MCP source types, so a partial field-name collision elsewhere in the JSON could pass without truly proving parity.
  • Consider exporting a shared `MAX_FIND_OPPORTUNITIES_SEARCH_QUERY_LENGTH` constant from find-opportunities.ts and importing it in schemas.ts, matching the pattern already used for the other MAX_FIND_OPPORTUNITIES_* constants.
  • For src/openapi/schemas.ts's IssueRagRetrieveRequestSchema, consider double-checking that `PREFLIGHT_LIMITS.titleChars/bodyChars/labelChars/labels` values actually match what routes.ts enforces at runtime, since that mapping isn't visible in this diff.

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: 21 registered-repo PR(s), 7 merged, 0 issue(s).
Contributor context ✅ Confirmed Gittensor contributor hurryup52; Gittensor profile; 21 PR(s), 0 issue(s).
Improvement ✅ Minor risk: clean · value: minor · LLM: moderate
Linked issue satisfaction

Addressed
The PR adds FindOpportunitiesRequestSchema/ResponseSchema and IssueRagRetrieveRequestSchema/ResponseSchema in schemas.ts, registers both as components and registerPath entries in spec.ts mirroring the gate-config pattern, regenerates and commits openapi.json, and adds regression tests checking both paths and schema fields (aiPolicyAllowed, retrievedPathCount) appear in buildOpenApiSpec's output.

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: JavaScript, TypeScript, Dart, MDX, Rust
  • Official Gittensor activity: 21 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.

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

@superagent-security

Copy link
Copy Markdown
Contributor

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

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

loopover-orb Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

LoopOver is closing this pull request on the maintainer's behalf (conflicts with the base branch — resolve and open a fresh PR). This is an automated maintenance action — to pursue this change, please open a new pull request with the issues resolved. Closed PRs may be analyzed later to improve review accuracy, but they are not automatically reopened or re-reviewed.

@loopover-orb loopover-orb Bot closed this Jul 27, 2026
loopover-orb Bot pushed a commit that referenced this pull request Jul 27, 2026
#9452)

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

Both discovery routes were live (and MCP-backed) but absent from OpenAPI.
Add request/response schemas mirroring the MCP tool shapes, register the
POST paths, regenerate openapi.json, and add regression assertions.
Replaces conflict-closed #9423 on current main.

Co-authored-by: Cursor <cursoragent@cursor.com>

* recheck

---------

Co-authored-by: Andriy Polanski <andriy.polanski@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
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.

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)

1 participant