From 18ff14ba1a7f4be90dc73d8852c687bd8d273a55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Fri, 23 Oct 2026 15:53:59 +0200 Subject: [PATCH] fix(openapi): declare the body-cap 413 and the outbound-screen 502 of a briefing generation Both have always been answered; neither was declared. The parity walk now checks them against the contract instead of skipping it. --- docs/api/openapi.yaml | 13 +++++++++++++ src/lib/openapi/routes/insights/paths.ts | 9 +++++++++ .../routes/parity/insights-generate.parity.test.ts | 14 ++------------ 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/docs/api/openapi.yaml b/docs/api/openapi.yaml index c8049282e..3933181fb 100644 --- a/docs/api/openapi.yaml +++ b/docs/api/openapi.yaml @@ -12654,6 +12654,12 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" + "413": + description: The request body is over 16 KB. Nothing was generated. + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" "422": description: No AI provider can serve the `briefing` capability (`meta.errorCode` = `ai.provider.none`, `meta.reason` = `no_provider`), or the provider answered something that was not valid JSON (`meta.errorCode` = @@ -12672,6 +12678,13 @@ paths: application/json: schema: $ref: "#/components/schemas/ErrorEnvelope" + "502": + description: The generated briefing did not pass the outbound safety screen and was discarded (`meta.errorCode` = + `insights.generate.outboundScreened`). Regenerating may succeed. + content: + application/json: + schema: + $ref: "#/components/schemas/ErrorEnvelope" "503": description: "Every provider in the chain failed for a reason that is not a rate limit — auth, timeout or an upstream 5xx. A dated failure marker is written, so the read GET reports the briefing as held rather than fresh. diff --git a/src/lib/openapi/routes/insights/paths.ts b/src/lib/openapi/routes/insights/paths.ts index 82d0c7e3c..d66169878 100644 --- a/src/lib/openapi/routes/insights/paths.ts +++ b/src/lib/openapi/routes/insights/paths.ts @@ -276,6 +276,15 @@ export const insightsPaths: NonNullable = { "Every provider in the chain failed for a reason that is not a rate limit — auth, timeout or an upstream 5xx. A dated failure marker is written, so the read GET reports the briefing as held rather than fresh. Also: the `briefing` capability could not be resolved (`meta.errorCode` = `ai.unavailable`, `meta.reason` = `check_failed`); nothing was generated.", content: { "application/json": { schema: errorEnvelope } }, }, + "413": { + description: "The request body is over 16 KB. Nothing was generated.", + content: { "application/json": { schema: errorEnvelope } }, + }, + "502": { + description: + "The generated briefing did not pass the outbound safety screen and was discarded (`meta.errorCode` = `insights.generate.outboundScreened`). Regenerating may succeed.", + content: { "application/json": { schema: errorEnvelope } }, + }, "401": stdResponses["401"], }, }, diff --git a/tests/integration/routes/parity/insights-generate.parity.test.ts b/tests/integration/routes/parity/insights-generate.parity.test.ts index 0b2d475e8..86b33a7fd 100644 --- a/tests/integration/routes/parity/insights-generate.parity.test.ts +++ b/tests/integration/routes/parity/insights-generate.parity.test.ts @@ -745,11 +745,7 @@ describe("POST /api/insights/generate parity", () => { it("P03 a body over the cap", async () => { await resetBriefing(OWNER_ID); - await post( - "P03-body-too-large", - { pad: "x".repeat(17 * 1024) }, - { undeclared: "the body cap answers 413, which the contract omits" }, - ); + await post("P03-body-too-large", { pad: "x".repeat(17 * 1024) }); }); it("P04 a cached briefing written today", async () => { @@ -984,13 +980,7 @@ describe("POST /api/insights/generate parity", () => { "Your 10-year cardiovascular risk is elevated — consider stepping your dose up.", }), }); - await post( - "P29-outbound-screened", - { force: true }, - { - undeclared: "the outbound screen answers 502, which the contract omits", - }, - ); + await post("P29-outbound-screened", { force: true }); }); it("P30 the about-me text changing during the generation", async () => {