Public: anyone can read this; the crew can change it.
The OpenAPI document describes a pull comment, a pull variant, a steered run and a prompt run #5136
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/5114-the-openapi-document-describes-a-pull-co → main 3 commits fork 1 thread · 1 unresolved
6 files
changed +73
−0
src/contract/openapi/routes/index.ts +4
| @@ -168,14 +168,18 @@ export { route as postApiV1ReposOwnerNameMakeItYours } from "./postApiV1ReposOwn | ||
| 168 | 168 | export { route as postApiV1ReposOwnerNameOriginReconnect } from "./postApiV1ReposOwnerNameOriginReconnect.ts"; |
| 169 | 169 | export { route as postApiV1ReposOwnerNamePlan } from "./postApiV1ReposOwnerNamePlan.ts"; |
| 170 | 170 | export { route as postApiV1ReposOwnerNamePrompts } from "./postApiV1ReposOwnerNamePrompts.ts"; |
| 171 | export { route as postApiV1ReposOwnerNamePromptsIdRun } from "./postApiV1ReposOwnerNamePromptsIdRun.ts"; | |
| 171 | 172 | export { route as postApiV1ReposOwnerNamePulls } from "./postApiV1ReposOwnerNamePulls.ts"; |
| 172 | 173 | export { route as postApiV1ReposOwnerNamePullsNumberApproveHere } from "./postApiV1ReposOwnerNamePullsNumberApproveHere.ts"; |
| 173 | 174 | export { route as postApiV1ReposOwnerNamePullsNumberChecks } from "./postApiV1ReposOwnerNamePullsNumberChecks.ts"; |
| 175 | export { route as postApiV1ReposOwnerNamePullsNumberComments } from "./postApiV1ReposOwnerNamePullsNumberComments.ts"; | |
| 174 | 176 | export { route as postApiV1ReposOwnerNamePullsNumberMerge } from "./postApiV1ReposOwnerNamePullsNumberMerge.ts"; |
| 175 | 177 | export { route as postApiV1ReposOwnerNamePullsNumberReviews } from "./postApiV1ReposOwnerNamePullsNumberReviews.ts"; |
| 176 | 178 | export { route as postApiV1ReposOwnerNamePullsNumberThreadsAnchorResolve } from "./postApiV1ReposOwnerNamePullsNumberThreadsAnchorResolve.ts"; |
| 179 | export { route as postApiV1ReposOwnerNamePullsNumberVariant } from "./postApiV1ReposOwnerNamePullsNumberVariant.ts"; | |
| 177 | 180 | export { route as postApiV1ReposOwnerNameReports } from "./postApiV1ReposOwnerNameReports.ts"; |
| 178 | 181 | export { route as postApiV1ReposOwnerNameRunsIdCancel } from "./postApiV1ReposOwnerNameRunsIdCancel.ts"; |
| 182 | export { route as postApiV1ReposOwnerNameRunsIdSteer } from "./postApiV1ReposOwnerNameRunsIdSteer.ts"; | |
| 179 | 183 | export { route as postApiV1ReposOwnerNameSpend } from "./postApiV1ReposOwnerNameSpend.ts"; |
| 180 | 184 | export { route as postApiV1ReposOwnerNameSync } from "./postApiV1ReposOwnerNameSync.ts"; |
| 181 | 185 | export { route as postApiV1ReposOwnerNameUpstreamCheck } from "./postApiV1ReposOwnerNameUpstreamCheck.ts"; |
src/contract/openapi/routes/postApiV1ReposOwnerNamePromptsIdRun.ts added +12
| @@ -0,0 +1,12 @@ | ||
| 1 | import { REPO, type Route } from "../shared.ts"; | |
| 2 | ||
| 3 | export const route: Route = { | |
| 4 | method: "post", | |
| 5 | path: `${REPO}/prompts/{id}/run`, | |
| 6 | summary: "Look at a prompt source now, whatever the hour (admin scope); answers the source as the run left it", | |
| 7 | auth: true, | |
| 8 | refusals: { | |
| 9 | no_such_source: "the repository has no such prompt source (422)", | |
| 10 | not_running: "the prompt routine is not running on this forge (422)", | |
| 11 | }, | |
| 12 | }; |
src/contract/openapi/routes/postApiV1ReposOwnerNamePullsNumberComments.ts added +14
| @@ -0,0 +1,14 @@ | ||
| 1 | import { REPO, type Route } from "../shared.ts"; | |
| 2 | ||
| 3 | export const route: Route = { | |
| 4 | method: "post", | |
| 5 | path: `${REPO}/pulls/{number}/comments`, | |
| 6 | summary: "Comment on a pull (rw scope; anyone who may take part in the repository); answers 201 with the comment", | |
| 7 | auth: true, | |
| 8 | status: 201, | |
| 9 | body: { body: "string" }, | |
| 10 | refusals: { | |
| 11 | empty_body: "`body` is empty (422)", | |
| 12 | mirrored: "the repository mirrors an origin; comment there (409)", | |
| 13 | }, | |
| 14 | }; |
src/contract/openapi/routes/postApiV1ReposOwnerNamePullsNumberVariant.ts added +16
| @@ -0,0 +1,16 @@ | ||
| 1 | import { REPO, type Route } from "../shared.ts"; | |
| 2 | ||
| 3 | export const route: Route = { | |
| 4 | method: "post", | |
| 5 | path: `${REPO}/pulls/{number}/variant`, | |
| 6 | summary: "Like or dislike a pull among its variant siblings (rw scope and write access); a dislike closes the pull with a one-line reason; answers 201 with { answer, closed }", | |
| 7 | auth: true, | |
| 8 | status: 201, | |
| 9 | body: { answer: "string", reason: "string" }, | |
| 10 | refusals: { | |
| 11 | invalid_answer: "`answer` is not like or dislike (422)", | |
| 12 | not_variant: "the pull is not one of a set of variants (422)", | |
| 13 | not_open: "the pull is closed or merged (422)", | |
| 14 | failed: "the vote could not be recorded (422)", | |
| 15 | }, | |
| 16 | }; |
src/contract/openapi/routes/postApiV1ReposOwnerNameRunsIdSteer.ts added +15
| @@ -0,0 +1,15 @@ | ||
| 1 | import { REPO, type Route } from "../shared.ts"; | |
| 2 | ||
| 3 | export const route: Route = { | |
| 4 | method: "post", | |
| 5 | path: `${REPO}/runs/{id}/steer`, | |
| 6 | summary: "Say something to an agent's run (rw scope; a verified person with write access, not an agent); a running agent hears it as its next turn, otherwise it lands as a comment on the issue or pull; answers { outcome: sent | commented }", | |
| 7 | auth: true, | |
| 8 | body: { text: "string" }, | |
| 9 | refusals: { | |
| 10 | empty: "`text` is empty (422)", | |
| 11 | not_agent: "the run is not an agent's (422)", | |
| 12 | unverified: "verify your email first (403)", | |
| 13 | forbidden: "the caller is an agent (403)", | |
| 14 | }, | |
| 15 | }; |
test/openapi.test.ts +12
| @@ -269,6 +269,18 @@ test("the admin's writes are described: a capacity setting, a limit and a team", | ||
| 269 | 269 | expect(doc.paths["/api/v1/admin/limits/{key}"].put.summary).toContain("api.anonymous_per_source"); |
| 270 | 270 | }); |
| 271 | 271 | |
| 272 | test("a pull comment, a pull variant, a steered run and a prompt run are described", () => { | |
| 273 | const doc = openapi("https://f.test", "1") as any; | |
| 274 | const R = "/api/v1/repos/{owner}/{name}"; | |
| 275 | for (const path of ["pulls/{number}/comments", "pulls/{number}/variant", "runs/{id}/steer", "prompts/{id}/run"]) { | |
| 276 | expect(doc.paths[`${R}/${path}`]?.post?.security, path).toBeDefined(); | |
| 277 | } | |
| 278 | expect(doc.paths[`${R}/pulls/{number}/comments`].post.responses["201"]).toBeDefined(); | |
| 279 | expect(doc.paths[`${R}/pulls/{number}/variant`].post.responses["201"]).toBeDefined(); | |
| 280 | const steer = doc.paths[`${R}/runs/{id}/steer`].post; | |
| 281 | expect(Object.keys(steer.requestBody.content["application/json"].schema.properties)).toEqual(["text"]); | |
| 282 | }); | |
| 283 | ||
| 272 | 284 | test("a person's sign-ins and ending one are described, with no session secret", () => { |
| 273 | 285 | const doc = openapi("https://f.test", "1") as any; |
| 274 | 286 | const list = doc.paths["/api/v1/users/{username}/signins"]?.get; |
new above
offline