⚠️ Definition of Done: this issue must be completed in full, in a single PR. Do not split this
work across multiple PRs, and do not defer any Deliverable below to a follow-up issue. A PR that
satisfies only some of the Deliverables, stubs a required test, or leaves a checkbox
partially-done does NOT resolve this issue and will be closed.
Context
POST /v1/loop/request-apr-transfer (src/api/routes.ts:4004-4013) has exactly four exits: a 400 for a failed parse; 409 when result.status === "rejected"; 502 when result.status === "failed"; and 202 otherwise.
The route's own comment (:3999-4003) states the design: "Rejected gate -> 409 without touching GitHub; initiation is still pending-acceptance (202), never 'transfer done'."
Its spec entry (src/openapi/internal-and-public-route-specs.ts:326-334) declares 200: "Transfer requested", 400, 401. So the published contract advertises the one status the handler can never return, labels it with the semantics the comment explicitly rejects ("never 'transfer done'"), and omits all three statuses the handler actually produces on the non-400 paths. A generated client checking response.status === 200 treats every real outcome — including a hard 502 — as a non-success it cannot classify.
Requirements
- Replace the
200 in the requestAprTransfer spec entry with 202, described as the pending-acceptance initiation the handler returns.
- Add
409 (the transfer gate rejected the request; GitHub was not touched) and 502 (the transfer attempt failed downstream).
- Keep
400 and 401.
- Do not change the handler's statuses.
- Regenerate and commit
apps/loopover-ui/public/openapi.json.
⚠️ Required pattern: mirror the description style of the neighbouring MISC_ROUTES entries in src/openapi/internal-and-public-route-specs.ts:268-344, which name the concrete outcome per status ("Batch accepted", "Notification queued"). It does NOT satisfy this issue to keep 200 alongside 202; to change the handler to return 200 (202/409/502 are the shipped, deliberately-chosen statuses); or to declare 409/502 without correcting the success status.
Deliverables
All Deliverables above are required in a single PR. A PR that satisfies only some of them — for example correcting the spec without the handler-status tests, or adding the tests while leaving 200 declared — does not resolve this issue.
Test Coverage Requirements
src/api/** and src/openapi/** are inside Codecov's src/** include; the 99% branch-counted patch gate applies. All three arms of the status ladder (rejected -> 409, failed -> 502, otherwise -> 202) plus the 400 parse failure must be covered. Extend test/unit/routes-request-apr-transfer.test.ts rather than creating a parallel file. The exact-status-set assertion is the named regression test for this fix.
Expected Outcome
The published operation for the APR transfer request states the three real outcomes, so a client can tell a pending-acceptance initiation from a gate rejection from a downstream failure — instead of matching on a 200 that never arrives.
Links & Resources
src/api/routes.ts:3999-4013; src/openapi/internal-and-public-route-specs.ts:317-334; test/unit/routes-request-apr-transfer.test.ts.
Context
POST /v1/loop/request-apr-transfer(src/api/routes.ts:4004-4013) has exactly four exits: a400for a failed parse;409whenresult.status === "rejected";502whenresult.status === "failed"; and202otherwise.The route's own comment (
:3999-4003) states the design: "Rejected gate -> 409 without touching GitHub; initiation is still pending-acceptance (202), never 'transfer done'."Its spec entry (
src/openapi/internal-and-public-route-specs.ts:326-334) declares200: "Transfer requested",400,401. So the published contract advertises the one status the handler can never return, labels it with the semantics the comment explicitly rejects ("never 'transfer done'"), and omits all three statuses the handler actually produces on the non-400 paths. A generated client checkingresponse.status === 200treats every real outcome — including a hard 502 — as a non-success it cannot classify.Requirements
200in therequestAprTransferspec entry with202, described as the pending-acceptance initiation the handler returns.409(the transfer gate rejected the request; GitHub was not touched) and502(the transfer attempt failed downstream).400and401.apps/loopover-ui/public/openapi.json.Deliverables
requestAprTransferentry declares exactly202,400,401,409,502— and no200.202for a request whoserequestAprRepoTransferresult status is neitherrejectednorfailed.409when the result status isrejected, and502when it isfailed.buildOpenApiSpec()declares forPOST /v1/loop/request-apr-transferis exactly{202, 400, 401, 409, 502}.apps/loopover-ui/public/openapi.jsonregenerated and committed.All Deliverables above are required in a single PR. A PR that satisfies only some of them — for example correcting the spec without the handler-status tests, or adding the tests while leaving
200declared — does not resolve this issue.Test Coverage Requirements
src/api/**andsrc/openapi/**are inside Codecov'ssrc/**include; the 99% branch-counted patch gate applies. All three arms of the status ladder (rejected-> 409,failed-> 502, otherwise -> 202) plus the 400 parse failure must be covered. Extendtest/unit/routes-request-apr-transfer.test.tsrather than creating a parallel file. The exact-status-set assertion is the named regression test for this fix.Expected Outcome
The published operation for the APR transfer request states the three real outcomes, so a client can tell a pending-acceptance initiation from a gate rejection from a downstream failure — instead of matching on a 200 that never arrives.
Links & Resources
src/api/routes.ts:3999-4013;src/openapi/internal-and-public-route-specs.ts:317-334;test/unit/routes-request-apr-transfer.test.ts.