diff --git a/apps/loopover-ui/public/openapi.json b/apps/loopover-ui/public/openapi.json index 11592c4ed7..6043b11b3a 100644 --- a/apps/loopover-ui/public/openapi.json +++ b/apps/loopover-ui/public/openapi.json @@ -16159,6 +16159,137 @@ "type": "boolean" } } + }, + "ValidateLinkedIssueRequest": { + "type": "object", + "properties": { + "owner": { + "type": "string" + }, + "repo": { + "type": "string" + }, + "issueNumber": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true + }, + "plannedChange": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "changedFiles": { + "type": "array", + "items": { + "type": "string" + } + }, + "contributorLogin": { + "type": "string" + } + } + } + }, + "required": [ + "owner", + "repo", + "issueNumber" + ] + }, + "ValidateLinkedIssueResponse": { + "type": "object", + "properties": { + "status": { + "type": "string" + }, + "repoFullName": { + "type": "string" + }, + "issueNumber": { + "type": "number" + }, + "found": { + "type": "boolean" + }, + "multiplierStatus": { + "type": "string" + }, + "multiplierWouldApply": { + "type": "boolean" + }, + "blockingReason": { + "type": "string" + }, + "reasons": { + "nullable": true + }, + "report": { + "nullable": true + } + } + }, + "CheckBeforeStartRequest": { + "type": "object", + "properties": { + "owner": { + "type": "string" + }, + "repo": { + "type": "string" + }, + "issueNumber": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true + }, + "title": { + "type": "string" + }, + "plannedPaths": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "owner", + "repo" + ] + }, + "CheckBeforeStartResponse": { + "type": "object", + "properties": { + "status": { + "type": "string" + }, + "repoFullName": { + "type": "string" + }, + "found": { + "type": "boolean" + }, + "claimStatus": { + "type": "string" + }, + "duplicateClusterRisk": { + "type": "string" + }, + "recommendation": { + "type": "string" + }, + "reasons": { + "nullable": true + }, + "blockers": { + "nullable": true + }, + "report": { + "nullable": true + } + } } }, "parameters": {}, @@ -21919,6 +22050,128 @@ } ] } + }, + "/v1/repos/{owner}/{repo}/validate-linked-issue": { + "post": { + "summary": "Validate a linked issue for a planned change — REST mirror of loopover_validate_linked_issue (#9304)", + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "owner", + "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "repo", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ValidateLinkedIssueRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Linked-issue validation over the planned change — mirrors the loopover_validate_linked_issue MCP tool's output shape", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ValidateLinkedIssueResponse" + } + } + } + }, + "400": { + "description": "Invalid validate-linked-issue request body" + }, + "401": { + "description": "Missing or invalid static protected API token" + }, + "403": { + "description": "Static mcp credential is outside MCP_READ_REPO_ALLOWLIST for this repo" + } + }, + "security": [ + { + "LoopOverBearer": [] + }, + { + "LoopOverSessionCookie": [] + } + ] + } + }, + "/v1/repos/{owner}/{repo}/check-before-start": { + "post": { + "summary": "Pre-work claim/duplicate check before starting an issue — REST mirror of loopover_check_before_start (#9304)", + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "owner", + "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "repo", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CheckBeforeStartRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Claim status, duplicate-cluster risk, and a start recommendation — mirrors the loopover_check_before_start MCP tool's output shape", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CheckBeforeStartResponse" + } + } + } + }, + "400": { + "description": "Invalid check-before-start request body" + }, + "401": { + "description": "Missing or invalid static protected API token" + }, + "403": { + "description": "Static mcp credential is outside MCP_READ_REPO_ALLOWLIST for this repo" + } + }, + "security": [ + { + "LoopOverBearer": [] + }, + { + "LoopOverSessionCookie": [] + } + ] + } } }, "servers": [ diff --git a/src/openapi/schemas.ts b/src/openapi/schemas.ts index 47937224a8..5c11c616b8 100644 --- a/src/openapi/schemas.ts +++ b/src/openapi/schemas.ts @@ -2056,6 +2056,76 @@ export const EvaluateEscalationResponseSchema = z }) .openapi("EvaluateEscalationResponse"); +/** + * Request body for POST /v1/repos/{owner}/{repo}/validate-linked-issue. Field-level parity with + * `validateLinkedIssueShape` (the `loopover_validate_linked_issue` MCP tool `inputSchema`) in + * src/mcp/server.ts — #9304. owner/repo are also path params; the body carries the planned-change context. + */ +export const ValidateLinkedIssueRequestSchema = z + .object({ + owner: z.string(), + repo: z.string(), + issueNumber: z.number().int().positive(), + plannedChange: z + .object({ + title: z.string().optional(), + changedFiles: z.array(z.string()).optional(), + contributorLogin: z.string().optional(), + }) + .optional(), + }) + .openapi("ValidateLinkedIssueRequest"); + +/** + * Response body for POST /v1/repos/{owner}/{repo}/validate-linked-issue. Field-level parity with + * `validateLinkedIssueOutputSchema` (the `loopover_validate_linked_issue` MCP tool `outputSchema`) — #9304. + */ +export const ValidateLinkedIssueResponseSchema = z + .object({ + status: z.string().optional(), + repoFullName: z.string().optional(), + issueNumber: z.number().optional(), + found: z.boolean().optional(), + multiplierStatus: z.string().optional(), + multiplierWouldApply: z.boolean().optional(), + blockingReason: z.string().optional(), + reasons: z.unknown().optional(), + report: z.unknown().optional(), + }) + .openapi("ValidateLinkedIssueResponse"); + +/** + * Request body for POST /v1/repos/{owner}/{repo}/check-before-start. Field-level parity with + * `checkBeforeStartShape` (the `loopover_check_before_start` MCP tool `inputSchema`) in src/mcp/server.ts — #9304. + */ +export const CheckBeforeStartRequestSchema = z + .object({ + owner: z.string(), + repo: z.string(), + issueNumber: z.number().int().positive().optional(), + title: z.string().optional(), + plannedPaths: z.array(z.string()).optional(), + }) + .openapi("CheckBeforeStartRequest"); + +/** + * Response body for POST /v1/repos/{owner}/{repo}/check-before-start. Field-level parity with + * `checkBeforeStartOutputSchema` (the `loopover_check_before_start` MCP tool `outputSchema`) — #9304. + */ +export const CheckBeforeStartResponseSchema = z + .object({ + status: z.string().optional(), + repoFullName: z.string().optional(), + found: z.boolean().optional(), + claimStatus: z.string().optional(), + duplicateClusterRisk: z.string().optional(), + recommendation: z.string().optional(), + reasons: z.unknown().optional(), + blockers: z.unknown().optional(), + report: z.unknown().optional(), + }) + .openapi("CheckBeforeStartResponse"); + /** * Request body for POST /v1/loop/results-payload. Field-level parity with `buildResultsPayloadShape` * (the `loopover_build_results_payload` MCP tool `inputSchema`) in src/mcp/server.ts — #9309. diff --git a/src/openapi/spec.ts b/src/openapi/spec.ts index 6163051020..7e2ce4d3d0 100644 --- a/src/openapi/spec.ts +++ b/src/openapi/spec.ts @@ -41,6 +41,10 @@ import { ScoreBreakdownResponseSchema, EvaluateEscalationRequestSchema, EvaluateEscalationResponseSchema, + ValidateLinkedIssueRequestSchema, + ValidateLinkedIssueResponseSchema, + CheckBeforeStartRequestSchema, + CheckBeforeStartResponseSchema, BuildResultsPayloadRequestSchema, BuildResultsPayloadResponseSchema, BuildProgressSnapshotRequestSchema, @@ -211,6 +215,10 @@ export function buildOpenApiSpec() { registry.register("ScoreBreakdownResponse", ScoreBreakdownResponseSchema); registry.register("EvaluateEscalationRequest", EvaluateEscalationRequestSchema); registry.register("EvaluateEscalationResponse", EvaluateEscalationResponseSchema); + registry.register("ValidateLinkedIssueRequest", ValidateLinkedIssueRequestSchema); + registry.register("ValidateLinkedIssueResponse", ValidateLinkedIssueResponseSchema); + registry.register("CheckBeforeStartRequest", CheckBeforeStartRequestSchema); + registry.register("CheckBeforeStartResponse", CheckBeforeStartResponseSchema); registry.register("BuildResultsPayloadRequest", BuildResultsPayloadRequestSchema); registry.register("BuildResultsPayloadResponse", BuildResultsPayloadResponseSchema); registry.register("BuildProgressSnapshotRequest", BuildProgressSnapshotRequestSchema); @@ -614,6 +622,46 @@ export function buildOpenApiSpec() { 403: { description: "Insufficient role" }, }, }); + registry.registerPath({ + method: "post", + path: "/v1/repos/{owner}/{repo}/validate-linked-issue", + summary: "Validate a linked issue for a planned change — REST mirror of loopover_validate_linked_issue (#9304)", + request: { + params: z.object({ owner: z.string(), repo: z.string() }), + body: { + content: { "application/json": { schema: ValidateLinkedIssueRequestSchema } }, + }, + }, + responses: { + 200: { + description: "Linked-issue validation over the planned change — mirrors the loopover_validate_linked_issue MCP tool's output shape", + content: { "application/json": { schema: ValidateLinkedIssueResponseSchema } }, + }, + 400: { description: "Invalid validate-linked-issue request body" }, + 401: { description: "Missing or invalid static protected API token" }, + 403: { description: "Static mcp credential is outside MCP_READ_REPO_ALLOWLIST for this repo" }, + }, + }); + registry.registerPath({ + method: "post", + path: "/v1/repos/{owner}/{repo}/check-before-start", + summary: "Pre-work claim/duplicate check before starting an issue — REST mirror of loopover_check_before_start (#9304)", + request: { + params: z.object({ owner: z.string(), repo: z.string() }), + body: { + content: { "application/json": { schema: CheckBeforeStartRequestSchema } }, + }, + }, + responses: { + 200: { + description: "Claim status, duplicate-cluster risk, and a start recommendation — mirrors the loopover_check_before_start MCP tool's output shape", + content: { "application/json": { schema: CheckBeforeStartResponseSchema } }, + }, + 400: { description: "Invalid check-before-start request body" }, + 401: { description: "Missing or invalid static protected API token" }, + 403: { description: "Static mcp credential is outside MCP_READ_REPO_ALLOWLIST for this repo" }, + }, + }); registry.registerPath({ method: "post", path: "/v1/loop/evaluate-escalation",