diff --git a/apps/loopover-ui/public/openapi.json b/apps/loopover-ui/public/openapi.json index 424fb6588..8124cf79d 100644 --- a/apps/loopover-ui/public/openapi.json +++ b/apps/loopover-ui/public/openapi.json @@ -26291,14 +26291,20 @@ } ], "responses": { - "200": { - "description": "Transfer requested" + "202": { + "description": "Transfer initiation accepted (pending acceptance)" }, "400": { "description": "Malformed request" }, "401": { "description": "Missing or invalid token" + }, + "409": { + "description": "Transfer gate rejected the request; GitHub was not touched" + }, + "502": { + "description": "Transfer attempt failed downstream" } } } diff --git a/src/openapi/internal-and-public-route-specs.ts b/src/openapi/internal-and-public-route-specs.ts index adb85db87..2bb6a204c 100644 --- a/src/openapi/internal-and-public-route-specs.ts +++ b/src/openapi/internal-and-public-route-specs.ts @@ -403,7 +403,13 @@ const MISC_ROUTES: SpecEntry[] = [ tags: ["Loop"], summary: "Request an APR transfer for a rented loop", auth: "token", - responses: { 200: { description: "Transfer requested" }, 400: { description: "Malformed request" }, 401: { description: "Missing or invalid token" } }, + responses: { + 202: { description: "Transfer initiation accepted (pending acceptance)" }, + 400: { description: "Malformed request" }, + 401: { description: "Missing or invalid token" }, + 409: { description: "Transfer gate rejected the request; GitHub was not touched" }, + 502: { description: "Transfer attempt failed downstream" }, + }, }, { method: "post", diff --git a/test/unit/openapi.test.ts b/test/unit/openapi.test.ts index b7b79b8d5..41626c747 100644 --- a/test/unit/openapi.test.ts +++ b/test/unit/openapi.test.ts @@ -353,6 +353,10 @@ describe("OpenAPI contract", () => { const aprTransfer = spec.paths["/v1/loop/request-apr-transfer"]?.post; expect(aprTransfer?.operationId).toBe("requestAprTransfer"); expect(aprTransfer?.requestBody).toBeUndefined(); + // #9711: the declared statuses must match the handler's real exits (400/409/502/202) — never the 200 it + // could never return. A spec claiming 200 for a route that only ever answers 202 misleads every generated + // client. + expect(Object.keys(aprTransfer?.responses ?? {}).sort()).toEqual(["202", "400", "401", "409", "502"]); }); // #9308: the eight /v1/lint/* + /v1/validate/focus-manifest advisory-check routes are each backed by an MCP