Context
GET /v1/repos/:owner/:repo/gate-config/effective (src/api/routes.ts:2981-3002) and
GET /v1/repos/:owner/:repo/live-gate-thresholds (src/api/routes.ts:3009-3025) are direct siblings: they
sit back-to-back in src/api/routes.ts, share the exact same auth gate
(requireStaticProtectedApiToken + the mcp-actor isMcpReadRepoAllowed check), and the comment directly
above live-gate-thresholds says so explicitly — src/api/routes.ts:3008: "Auth matches gate-config/effective above."
live-gate-thresholds is fully documented in the OpenAPI contract: it has a registered
LiveGateThresholdsResponseSchema (src/openapi/schemas.ts:1743-1750) and a registry.registerPath entry
in src/openapi/spec.ts (confirmed present via buildOpenApiSpec()'s output and asserted in
test/unit/openapi.test.ts).
gate-config/effective has neither. It is missing from src/openapi/schemas.ts, missing from
src/openapi/spec.ts's registry, and there is no assertion for it anywhere in test/unit/openapi.test.ts.
As a result it is silently absent from the generated openapi.json (and from the committed frontend copy at
apps/loopover-ui/public/openapi.json), so it never shows up for any API consumer generating a client from
the spec, even though its live sibling route does.
The handler's response shape is fully inline in src/api/routes.ts:2992-2999 (no external service-layer type
to chase down):
return c.json({
repoFullName: fullName,
effective: {
confidenceFloor: override?.confidenceFloor ?? null,
scopeCap: {
files: override?.scopeCap?.files ?? null,
lines: override?.scopeCap?.lines ?? null,
},
},
shadowPending: shadow !== null,
});
Requirements
- Add a new Zod schema to
src/openapi/schemas.ts, named GateConfigEffectiveResponseSchema, matching the
exact response shape above:
{ repoFullName: string; effective: { confidenceFloor: number | null; scopeCap: { files: number | null; lines: number | null } }; shadowPending: boolean }. Give it a trailing .openapi("GateConfigEffectiveResponse")
call, matching the convention used by every other schema in that file (e.g.
LiveGateThresholdsResponseSchema immediately preceding it).
- Register the new schema and add a
registry.registerPath entry for
GET /v1/repos/{owner}/{repo}/gate-config/effective in src/openapi/spec.ts, placed adjacent to the
existing live-gate-thresholds registration and modeled on it directly (same params: z.object({ owner: z.string(), repo: z.string() }), same 401/403/200 response shape — this route shares
requireStaticProtectedApiToken + the mcp-allowlist check with live-gate-thresholds, so its documented
auth/response-status behavior must match that sibling's registration exactly).
- Add an assertion for the new path to
test/unit/openapi.test.ts
(expect(spec.paths["/v1/repos/{owner}/{repo}/gate-config/effective"]).toBeDefined();), placed alongside
the file's other spec.paths[...]).toBeDefined() assertions for /v1/repos/{owner}/{repo}/... maintainer
routes, so a future removal of this registration is caught by CI instead of silently regressing again.
- Do not add or modify a
registerPath for any other currently-undocumented route
(gate-precision, outcome-calibration, selftune/overrides, etc.) — this issue is scoped to
gate-config/effective only.
Deliverables
Test Coverage Requirements
This change touches src/openapi/schemas.ts, src/openapi/spec.ts, and test/unit/openapi.test.ts, all
under this repo's src/** Codecov patch gate (99%+ patch coverage, hard gate). buildOpenApiSpec() is a pure
function exercised end-to-end by test/unit/openapi.test.ts's existing describe("OpenAPI contract") block,
so the new registration is covered automatically once the new assertion is added and the suite passes — no
additional mocking is required.
Expected Outcome
GET /v1/repos/{owner}/{repo}/gate-config/effective appears in buildOpenApiSpec()'s output (and therefore
in the generated openapi.json / apps/loopover-ui/public/openapi.json) with the same fidelity as its
documented sibling live-gate-thresholds, so any API consumer generating a client from the published spec
can discover and type this route instead of having to reverse-engineer it from the route implementation.
Links & Resources
src/api/routes.ts:2981-3002 (the undocumented handler)
src/api/routes.ts:3008-3025 (the documented sibling live-gate-thresholds, whose comment explicitly cross-references gate-config/effective's matching auth)
src/openapi/schemas.ts:1743-1750 (LiveGateThresholdsResponseSchema, the schema pattern to mirror)
src/openapi/spec.ts (where the sibling's registerPath call lives — add the new one immediately alongside it)
test/unit/openapi.test.ts (the assertion list to extend)
Context
GET /v1/repos/:owner/:repo/gate-config/effective(src/api/routes.ts:2981-3002) andGET /v1/repos/:owner/:repo/live-gate-thresholds(src/api/routes.ts:3009-3025) are direct siblings: theysit back-to-back in
src/api/routes.ts, share the exact same auth gate(
requireStaticProtectedApiToken+ themcp-actorisMcpReadRepoAllowedcheck), and the comment directlyabove
live-gate-thresholdssays so explicitly —src/api/routes.ts:3008:"Auth matches gate-config/effective above."live-gate-thresholdsis fully documented in the OpenAPI contract: it has a registeredLiveGateThresholdsResponseSchema(src/openapi/schemas.ts:1743-1750) and aregistry.registerPathentryin
src/openapi/spec.ts(confirmed present viabuildOpenApiSpec()'s output and asserted intest/unit/openapi.test.ts).gate-config/effectivehas neither. It is missing fromsrc/openapi/schemas.ts, missing fromsrc/openapi/spec.ts's registry, and there is no assertion for it anywhere intest/unit/openapi.test.ts.As a result it is silently absent from the generated
openapi.json(and from the committed frontend copy atapps/loopover-ui/public/openapi.json), so it never shows up for any API consumer generating a client fromthe spec, even though its live sibling route does.
The handler's response shape is fully inline in
src/api/routes.ts:2992-2999(no external service-layer typeto chase down):
Requirements
src/openapi/schemas.ts, namedGateConfigEffectiveResponseSchema, matching theexact response shape above:
{ repoFullName: string; effective: { confidenceFloor: number | null; scopeCap: { files: number | null; lines: number | null } }; shadowPending: boolean }. Give it a trailing.openapi("GateConfigEffectiveResponse")call, matching the convention used by every other schema in that file (e.g.
LiveGateThresholdsResponseSchemaimmediately preceding it).registry.registerPathentry forGET /v1/repos/{owner}/{repo}/gate-config/effectiveinsrc/openapi/spec.ts, placed adjacent to theexisting
live-gate-thresholdsregistration and modeled on it directly (sameparams: z.object({ owner: z.string(), repo: z.string() }), same 401/403/200 response shape — this route sharesrequireStaticProtectedApiToken+ themcp-allowlist check withlive-gate-thresholds, so its documentedauth/response-status behavior must match that sibling's registration exactly).
test/unit/openapi.test.ts(
expect(spec.paths["/v1/repos/{owner}/{repo}/gate-config/effective"]).toBeDefined();), placed alongsidethe file's other
spec.paths[...]).toBeDefined()assertions for/v1/repos/{owner}/{repo}/...maintainerroutes, so a future removal of this registration is caught by CI instead of silently regressing again.
registerPathfor any other currently-undocumented route(
gate-precision,outcome-calibration,selftune/overrides, etc.) — this issue is scoped togate-config/effectiveonly.Deliverables
GateConfigEffectiveResponseSchemaadded tosrc/openapi/schemas.tsand registered insrc/openapi/spec.ts.registry.registerPathentry forGET /v1/repos/{owner}/{repo}/gate-config/effectiveadded tosrc/openapi/spec.ts, modeled on the adjacentlive-gate-thresholdsregistration.test/unit/openapi.test.tsupdated with a.toBeDefined()assertion for the new path.Test Coverage Requirements
This change touches
src/openapi/schemas.ts,src/openapi/spec.ts, andtest/unit/openapi.test.ts, allunder this repo's
src/**Codecov patch gate (99%+ patch coverage, hard gate).buildOpenApiSpec()is a purefunction exercised end-to-end by
test/unit/openapi.test.ts's existingdescribe("OpenAPI contract")block,so the new registration is covered automatically once the new assertion is added and the suite passes — no
additional mocking is required.
Expected Outcome
GET /v1/repos/{owner}/{repo}/gate-config/effectiveappears inbuildOpenApiSpec()'s output (and thereforein the generated
openapi.json/apps/loopover-ui/public/openapi.json) with the same fidelity as itsdocumented sibling
live-gate-thresholds, so any API consumer generating a client from the published speccan discover and type this route instead of having to reverse-engineer it from the route implementation.
Links & Resources
src/api/routes.ts:2981-3002(the undocumented handler)src/api/routes.ts:3008-3025(the documented siblinglive-gate-thresholds, whose comment explicitly cross-referencesgate-config/effective's matching auth)src/openapi/schemas.ts:1743-1750(LiveGateThresholdsResponseSchema, the schema pattern to mirror)src/openapi/spec.ts(where the sibling'sregisterPathcall lives — add the new one immediately alongside it)test/unit/openapi.test.ts(the assertion list to extend)