Skip to content

openapi(internal-jobs): de-duplicate job operations and publish real handler statuses #9706

Description

@JSONbored

⚠️ 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

buildOpenApiSpec() registers the /v1/internal/jobs/* family twice, from two places, with nothing detecting the collision:

  • src/openapi/spec.ts:2318-2397 — five explicit registry.registerPath blocks plus a nine-entry loop at :2376-2397, six of whose paths are registered again below.
  • jobRoutes() in src/openapi/internal-and-public-route-specs.ts:65-110, called from registerInternalAndPublicRouteSpecs at src/openapi/spec.ts:2415.

11 POST operations are therefore registered twice. @asteasolutions/zod-to-openapi keeps the last one, so the spec.ts versions are silently thrown away. Verified against the committed apps/loopover-ui/public/openapi.json: POST /v1/internal/jobs/backfill-pr-details publishes operationId: "queueBackfillPrDetailsJob" with responses ["202","401"] — the spec.ts block at :2352 declaring postInternalJobsBackfillPrDetails and a 400 is gone from the document entirely. The route does return 400 (src/api/routes.ts:5055).

Three loop entries are NOT duplicated and stay in spec.tsbuild-contributor-evidence, build-burden-forecasts, repair-data-fidelity. Because they are legacy registerPath calls with no declared auth, and requiresApiToken returns false for /v1/internal/* (src/auth/route-auth.ts:79 — that family is gated by its own middleware at src/api/routes.ts:1216-1220), applySecurityMetadata leaves them with no security key at all. They publish a 401 response and no scheme that could produce it.

On top of the duplication, the surviving jobRoutes() table publishes statuses the handlers do not return:

  • SINGLE_JOBS (internal-and-public-route-specs.ts:47-50) gives rag-index and regate-pr { RAN (200), 400, 401 } and the summary "Run the job to ...". Both handlers enqueue and answer 202 (src/api/routes.ts:4964, :4995), and both can answer 404 (:4952, :4986). The published 200 is unreachable for both, and rag-index never returns 400 at all (:4953 swallows an unparseable body into {}).
  • QUEUED/RAN (:26-27) declare no 400, but nine job routes validate their body and return one: backfill-repo-segment (routes.ts:5016, :5018) and /run (:5037, :5039), backfill-pr-details (:5055) and /run (:5072), build-contributor-decision-packs/run (:5128), refresh-contributor-activity (:5134) and /run (:5143), generate-review-recap (:5188) and /run (:5229).

Requirements

  • Delete the duplicate /v1/internal/jobs/* registrations from src/openapi/spec.ts (the five explicit blocks at :2318-2375 and the whole loop at :2376-2397). POST /v1/internal/bounties/import at :2398-2408 is NOT a job route and must be left alone.
  • Add build-contributor-evidence, build-burden-forecasts, and repair-data-fidelity to SINGLE_JOBS so all three keep an operation, now with auth: "internal" and therefore a real LoopOverBearer security stanza. Their handlers (src/api/routes.ts:5110, :5148, :5195) all return 202 and never 400.
  • SINGLE_JOBS entries must publish 202 as the success status (not 200), because every one of them enqueues. Correct their summaries accordingly ("Queue a job to ...", not "Run the job to ...").
  • rag-index and regate-pr must additionally declare 404. rag-index must NOT declare 400; regate-pr must.
  • The nine routes listed in Context that validate their body must declare 400 in addition to their existing statuses. Add per-entry response overrides; do not widen the shared QUEUED/RAN constants, which would wrongly attach a 400 to the routes that accept any body.
  • Add a duplicate-registration guard: a test that asserts no method + path pair is contributed to the registry more than once by buildOpenApiSpec().
  • Regenerate and commit apps/loopover-ui/public/openapi.json.

⚠️ Required pattern: every job operation must be declared through registerRouteSpec from the tables in src/openapi/internal-and-public-route-specs.ts (the JOB_PAIRS / SINGLE_JOBS / RUN_ONLY_JOBS shape already there), exactly as #9531 established. It does NOT satisfy this issue to keep the spec.ts blocks and merely make them agree with the table; to delete the spec.ts blocks without carrying their 400s across; to add a new third registration site; or to "fix" the duplication by making registerRouteSpec overwrite-tolerant instead of removing the duplicate call sites.

Deliverables

  • src/openapi/spec.ts no longer registers any /v1/internal/jobs/* path; grep -c 'internal/jobs' src/openapi/spec.ts returns 0.
  • SINGLE_JOBS contains rag-index, regate-pr, build-contributor-evidence, build-burden-forecasts, and repair-data-fidelity, each with a 202 success response and auth: "internal".
  • rag-index and regate-pr each declare 404; rag-index declares no 400; regate-pr declares 400.
  • The nine body-validating job routes named in Context each declare 400 in the generated document.
  • A new test in test/unit/route-spec-ratchet.test.ts asserts that no method + path pair is registered twice by the spec builder. This test must fail on current main.
  • A new test asserts every operation under /v1/internal/ in buildOpenApiSpec() has security equal to [{ LoopOverBearer: [] }] — currently false for the three loop entries above.
  • apps/loopover-ui/public/openapi.json regenerated and committed; the existing test/unit/route-spec-ratchet.test.ts assertions still pass.

All Deliverables above are required in a single PR. A PR that satisfies only some of them — for example deleting the duplicate spec.ts blocks without adding the duplicate-registration guard test, or correcting the SINGLE_JOBS success status without declaring the nine missing 400s — does not resolve this issue.

Test Coverage Requirements

src/openapi/** is inside Codecov's src/** include; the 99% branch-counted patch gate applies. The spec modules are data tables consumed by registerRouteSpec, so coverage comes from the assertions above running buildOpenApiSpec(). Both arms of any conditional added for a per-entry response override must be exercised (an entry that overrides and one that does not). The duplicate-registration guard is the named regression test for this fix.

Expected Outcome

Each /v1/internal/jobs/* operation is registered exactly once, from one table; the published success status matches what the handler returns; every body-validating job route documents its 400; every /v1/internal/* operation declares the bearer it actually requires; and a future second registration of the same path fails CI instead of silently discarding one of the two.

Links & Resources

src/openapi/spec.ts:2318-2408, :2414-2415; src/openapi/internal-and-public-route-specs.ts:25-110; handlers at src/api/routes.ts:4951-5000, :5015-5240, :5110, :5148, :5195; src/auth/route-auth.ts:79; test/unit/route-spec-ratchet.test.ts. Related closed work: #9519, #9531.

Metadata

Metadata

Assignees

No one assigned

    Labels

    gittensor:bugGittensor-scored bug fix — scores a 0.05x multiplier.help wantedExtra attention is needed

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions