diff --git a/server/src/routes/openapi.ts b/server/src/routes/openapi.ts index c1efdf31fec..807faef2032 100644 --- a/server/src/routes/openapi.ts +++ b/server/src/routes/openapi.ts @@ -5731,7 +5731,12 @@ registry.registerPath({ method: "post", path: "/api/approvals/{id}/resubmit", tags: ["approvals"], - summary: "Resubmit an approval", + summary: "Revise and resubmit an approval or proposed hire", + description: + "Replace the payload of an approval whose status is revision_requested, or resubmit its existing payload. " + + "Use this operation to revise a changes-requested hire proposal, such as changing its agent configuration to a cheaper model, and send it back for board review. " + + "An agent must be the original requesting agent; board callers still need access to the company and approval. " + + "The approval returns to pending. This operation does not approve the request or create or execute the hire.", request: { params: z.object({ id: z.string() }), body: jsonBody(resubmitApprovalSchema), diff --git a/server/src/services/native-runtime/runner-api.test.ts b/server/src/services/native-runtime/runner-api.test.ts index f17a1a82a67..ed76e86a157 100644 --- a/server/src/services/native-runtime/runner-api.test.ts +++ b/server/src/services/native-runtime/runner-api.test.ts @@ -175,6 +175,24 @@ describe("runner API catalog", () => { expect(previewBody.properties).not.toHaveProperty("adapterOverrides"); }); + it("discovers proposal revision without confusing it with approval decisions or reads", () => { + const resubmit = "POST /api/approvals/{id}/resubmit"; + for (const query of [ + "revise changes-requested hire proposal", + "resubmit approval after changes", + "Change the proposed hire to the cheaper model and send it back for approval.", + ]) expect(searchRunnerApi({ query }).results.map(row => row.operationId), query).toContain(resubmit); + for (const [query, expected] of [ + ["GET /api/approvals/{id}", "GET /api/approvals/{id}"], + ["approve approval", "POST /api/approvals/{id}/approve"], + ["reject approval", "POST /api/approvals/{id}/reject"], + [resubmit, resubmit], + ]) expect(searchRunnerApi({ query }).results[0]?.operationId, query).toBe(expected); + const operation = runnerApiOperation(resubmit); + expect(operation.requestBody?.content?.["application/json"]?.schema.properties).toHaveProperty("payload"); + expect(operation.description).toContain("returns to pending"); + }); + it("keeps natural-language function words from hiding the requested API resource", () => { expect(searchRunnerApi({ query: "list comments for an issue" }).results[0]?.operationId) .toBe("GET /api/issues/{id}/comments");