⚠️ 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
#8377 gave GET /v1/public/repos/:owner/:repo/badge.svg and .../badge.json route-level error handling. The handlers now distinguish three outcomes deliberately — the route comment at src/api/routes.ts:1452-1459 spells it out: "503, never 404: 404 stays reserved for the real 'no public badge for this repo' case so a monitor can tell the two apart, and the 503 branch uses the SHORT cache so a transient failure is never cached for the long stale-while-revalidate window."
badge.svg (:1461-1475): 404 at :1467 (no opted-in badge), 503 at :1472 (loader threw), 200 otherwise.
badge.json (:1477-1491): 404 at :1482, 503 at :1489, 200 otherwise.
The spec entries added in #9531 (src/openapi/internal-and-public-route-specs.ts:230-247) declare only 200: "Badge payload" / 200: "SVG badge". The two statuses a monitor is explicitly supposed to be able to tell apart are absent from the published contract, and so is the distinct Cache-Control each branch sets.
These are the most externally-visible routes in the API — they are embedded in third-party READMEs behind GitHub's camo proxy — and the OpenAPI document is what an integrator building a badge monitor reads.
Requirements
- Add
404 and 503 to both getRepoBadgeSvg and getRepoBadgeJson in src/openapi/internal-and-public-route-specs.ts.
- The
404 description must say the repo has no public badge (unknown, private, uninstalled, or badgeEnabled off) and the 503 description must say the badge data could not be loaded — matching the distinction the handler comment declares.
- Do not change any handler behaviour, status, or cache header.
- Regenerate and commit
apps/loopover-ui/public/openapi.json.
⚠️ Required pattern: mirror the sibling public route GET /v1/public/repos/{owner}/{repo}/quality, whose handler (src/api/routes.ts:1495-1506) has the same 404/503 split. It does NOT satisfy this issue to collapse 404 and 503 into one entry; to change the badge handlers to return a single status; or to add the statuses to only one of the two badge routes.
Deliverables
All Deliverables above are required in a single PR. A PR that satisfies only some of them — for example adding the two statuses to the spec without the tests that pin which branch produces which status — 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. Both arms of the if (!quality) check and both arms of the surrounding try/catch must be covered for each of the two routes — four branches per route. The 404-vs-503 assertions are the named regression tests for this fix.
Expected Outcome
A badge monitor generated from the published document can distinguish "this repo has no public badge" from "the badge service is degraded", which is the exact distinction #8377 built into the handlers.
Links & Resources
src/api/routes.ts:1450-1506; src/openapi/internal-and-public-route-specs.ts:230-247; src/api/badge.ts. Related closed work: #8377.
Context
#8377 gave
GET /v1/public/repos/:owner/:repo/badge.svgand.../badge.jsonroute-level error handling. The handlers now distinguish three outcomes deliberately — the route comment atsrc/api/routes.ts:1452-1459spells it out: "503, never 404: 404 stays reserved for the real 'no public badge for this repo' case so a monitor can tell the two apart, and the 503 branch uses the SHORT cache so a transient failure is never cached for the long stale-while-revalidate window."badge.svg(:1461-1475):404at:1467(no opted-in badge),503at:1472(loader threw),200otherwise.badge.json(:1477-1491):404at:1482,503at:1489,200otherwise.The spec entries added in #9531 (
src/openapi/internal-and-public-route-specs.ts:230-247) declare only200: "Badge payload"/200: "SVG badge". The two statuses a monitor is explicitly supposed to be able to tell apart are absent from the published contract, and so is the distinctCache-Controleach branch sets.These are the most externally-visible routes in the API — they are embedded in third-party READMEs behind GitHub's camo proxy — and the OpenAPI document is what an integrator building a badge monitor reads.
Requirements
404and503to bothgetRepoBadgeSvgandgetRepoBadgeJsoninsrc/openapi/internal-and-public-route-specs.ts.404description must say the repo has no public badge (unknown, private, uninstalled, orbadgeEnabledoff) and the503description must say the badge data could not be loaded — matching the distinction the handler comment declares.apps/loopover-ui/public/openapi.json.Deliverables
apps/loopover-ui/public/openapi.json,GET /v1/public/repos/{owner}/{repo}/badge.svgandGET /v1/public/repos/{owner}/{repo}/badge.jsoneach declare200,404, and503.badge.jsonanswers404(not 503) for a repo with no public badge, and that the response carriesCache-Control: public, max-age=300.badge.jsonanswers503(not 404) when the loader throws, and that the response carries the same shortCache-Control: public, max-age=300.badge.svg, including that the body isrenderUnavailableBadgeSvg()in both branches.buildOpenApiSpec().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 adding the two statuses to the spec without the tests that pin which branch produces which status — does not resolve this issue.
Test Coverage Requirements
src/api/**andsrc/openapi/**are inside Codecov'ssrc/**include; the 99% branch-counted patch gate applies. Both arms of theif (!quality)check and both arms of the surroundingtry/catchmust be covered for each of the two routes — four branches per route. The 404-vs-503 assertions are the named regression tests for this fix.Expected Outcome
A badge monitor generated from the published document can distinguish "this repo has no public badge" from "the badge service is degraded", which is the exact distinction #8377 built into the handlers.
Links & Resources
src/api/routes.ts:1450-1506;src/openapi/internal-and-public-route-specs.ts:230-247;src/api/badge.ts. Related closed work: #8377.