From c4f31f09e01216550ef0f0c389769322af90f7f1 Mon Sep 17 00:00:00 2001 From: ultrahighsuper Date: Fri, 17 Jul 2026 03:53:31 +0900 Subject: [PATCH] feat(openapi): document GET /v1/repos/:owner/:repo/gate-config/effective (#6611) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `gate-config/effective` (src/api/routes.ts) is the direct sibling of `live-gate-thresholds` — same auth (requireStaticProtectedApiToken + mcp-allowlist) — but was missing from the OpenAPI contract entirely (no schema, no registerPath, no test), so it never appeared in the generated openapi.json or the frontend copy, even though its live sibling did. - Add GateConfigEffectiveResponseSchema to src/openapi/schemas.ts matching the handler's inline response shape ({ repoFullName, effective: { confidenceFloor, scopeCap: { files, lines } }, shadowPending }), with the same .openapi("GateConfigEffectiveResponse") convention as the adjacent LiveGateThresholdsResponseSchema. - Register the schema and a registry.registerPath for GET /v1/repos/{owner}/{repo}/gate-config/effective in src/openapi/spec.ts, adjacent to live-gate-thresholds; the handler returns 200/401/403 (it always returns data with nulls rather than 404-ing). - Add a .toBeDefined() assertion for the new path to test/unit/openapi.test.ts. - Regenerate apps/loopover-ui/public/openapi.json (ui:openapi:check passes). Scoped to gate-config/effective only; no other undocumented route is touched. Closes #6611 --- apps/loopover-ui/public/openapi.json | 95 ++++++++++++++++++++++++++++ src/openapi/schemas.ts | 14 ++++ src/openapi/spec.ts | 16 +++++ test/unit/openapi.test.ts | 1 + 4 files changed, 126 insertions(+) diff --git a/apps/loopover-ui/public/openapi.json b/apps/loopover-ui/public/openapi.json index c90e4e9b27..a1e6f52e35 100644 --- a/apps/loopover-ui/public/openapi.json +++ b/apps/loopover-ui/public/openapi.json @@ -14056,6 +14056,52 @@ "amsCohort", "humanCohort" ] + }, + "GateConfigEffectiveResponse": { + "type": "object", + "properties": { + "repoFullName": { + "type": "string" + }, + "effective": { + "type": "object", + "properties": { + "confidenceFloor": { + "type": "number", + "nullable": true + }, + "scopeCap": { + "type": "object", + "properties": { + "files": { + "type": "integer", + "nullable": true + }, + "lines": { + "type": "integer", + "nullable": true + } + }, + "required": [ + "files", + "lines" + ] + } + }, + "required": [ + "confidenceFloor", + "scopeCap" + ] + }, + "shadowPending": { + "type": "boolean" + } + }, + "required": [ + "repoFullName", + "effective", + "shadowPending" + ] } }, "parameters": {}, @@ -18114,6 +18160,55 @@ } ] } + }, + "/v1/repos/{owner}/{repo}/gate-config/effective": { + "get": { + "summary": "Current effective self-tuned gate config for a repo (#6247)", + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "owner", + "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "repo", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Effective TunableOverride values (confidenceFloor / scopeCap.files / scopeCap.lines) with a shadowPending flag — never the raw override_audit history", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GateConfigEffectiveResponse" + } + } + } + }, + "401": { + "description": "Missing or invalid static protected API token" + }, + "403": { + "description": "Static mcp credential is outside MCP_READ_REPO_ALLOWLIST for this repo" + } + }, + "security": [ + { + "LoopOverBearer": [] + }, + { + "LoopOverSessionCookie": [] + } + ] + } } }, "servers": [ diff --git a/src/openapi/schemas.ts b/src/openapi/schemas.ts index a0c72a3c15..9d9bc62882 100644 --- a/src/openapi/schemas.ts +++ b/src/openapi/schemas.ts @@ -1749,6 +1749,20 @@ export const LiveGateThresholdsResponseSchema = z }) .openapi("LiveGateThresholdsResponse"); +export const GateConfigEffectiveResponseSchema = z + .object({ + repoFullName: z.string(), + effective: z.object({ + confidenceFloor: z.number().nullable(), + scopeCap: z.object({ + files: z.number().int().nullable(), + lines: z.number().int().nullable(), + }), + }), + shadowPending: z.boolean(), + }) + .openapi("GateConfigEffectiveResponse"); + export const BurdenForecastSchema = z .object({ repoFullName: z.string(), diff --git a/src/openapi/spec.ts b/src/openapi/spec.ts index 867a4a7481..30354b50c7 100644 --- a/src/openapi/spec.ts +++ b/src/openapi/spec.ts @@ -31,6 +31,7 @@ import { InstallationRepairSchema, IssueQualityReportSchema, IssueQualityResponseSchema, + GateConfigEffectiveResponseSchema, LabelAuditSchema, LaneAdviceSchema, LiveGateThresholdsResponseSchema, @@ -150,6 +151,7 @@ export function buildOpenApiSpec() { registry.register("ScorePreview", ScorePreviewSchema); registry.register("IssueQualityReport", IssueQualityReportSchema); registry.register("IssueQualityResponse", IssueQualityResponseSchema); + registry.register("GateConfigEffectiveResponse", GateConfigEffectiveResponseSchema); registry.register("LiveGateThresholdsResponse", LiveGateThresholdsResponseSchema); registry.register("BurdenForecast", BurdenForecastSchema); registry.register("ContributorScoringProfile", ContributorScoringProfileSchema); @@ -423,6 +425,20 @@ export function buildOpenApiSpec() { 404: { description: "Repo is unknown or has no issue-quality coverage yet" }, }, }); + registry.registerPath({ + method: "get", + path: "/v1/repos/{owner}/{repo}/gate-config/effective", + summary: "Current effective self-tuned gate config for a repo (#6247)", + request: { params: z.object({ owner: z.string(), repo: z.string() }) }, + responses: { + 200: { + description: "Effective TunableOverride values (confidenceFloor / scopeCap.files / scopeCap.lines) with a shadowPending flag — never the raw override_audit history", + content: { "application/json": { schema: GateConfigEffectiveResponseSchema } }, + }, + 401: { description: "Missing or invalid static protected API token" }, + 403: { description: "Static mcp credential is outside MCP_READ_REPO_ALLOWLIST for this repo" }, + }, + }); registry.registerPath({ method: "get", path: "/v1/repos/{owner}/{repo}/live-gate-thresholds", diff --git a/test/unit/openapi.test.ts b/test/unit/openapi.test.ts index dfbba5ba05..54d6c520e8 100644 --- a/test/unit/openapi.test.ts +++ b/test/unit/openapi.test.ts @@ -14,6 +14,7 @@ describe("OpenAPI contract", () => { expect(spec.paths["/v1/repos/{owner}/{repo}/intelligence"]).toBeDefined(); expect(spec.paths["/v1/repos/{owner}/{repo}/issue-quality"]).toBeDefined(); expect(spec.paths["/v1/repos/{owner}/{repo}/outcome-patterns"]).toBeDefined(); + expect(spec.paths["/v1/repos/{owner}/{repo}/gate-config/effective"]).toBeDefined(); expect(spec.paths["/v1/repos/{owner}/{repo}/registration-readiness"]).toBeDefined(); expect(spec.paths["/v1/repos/{owner}/{repo}/gittensor-config-recommendation"]).toBeDefined(); expect(spec.paths["/v1/repos/{owner}/{repo}/pulls/{number}/maintainer-packet"]).toBeDefined();