From 810a949d2bddec4ebde4d27ecdf366ca6d13f8ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sat, 24 Oct 2026 08:06:33 +0200 Subject: [PATCH] fix(ai): move the Codex slug ladder onto the gpt-5.6 line The ChatGPT Codex backend now rejects every slug from gpt-5.5 down, so a deploy without a CODEX_MODEL_FALLBACK_CHAIN override walked the whole default ladder into "all configured Codex slugs were rejected". The default now starts at gpt-5.6-terra, with its luna and sol siblings and gpt-5.5 as the last rung. The gpt-6 line stays out of the default: the reasoning, sampling and vision gates do not recognise that family yet, and a test now holds every default slug to a family the reasoning gate knows. --- docs/codex-protocol-spec.md | 12 ++++---- .../baselines/source-file-lines.json | 4 +-- .../ai/__tests__/codex-slug-fallback.test.ts | 29 ++++++++++++------- src/lib/ai/codex-client.ts | 25 +++++++--------- 4 files changed, 37 insertions(+), 33 deletions(-) diff --git a/docs/codex-protocol-spec.md b/docs/codex-protocol-spec.md index 1e49cab8c..826d6c03b 100644 --- a/docs/codex-protocol-spec.md +++ b/docs/codex-protocol-spec.md @@ -630,7 +630,8 @@ Confirmed-rejected slugs (despite the source-tree references in the table above) Confirmed-accepted slugs on a ChatGPT Plus subscription (per `available_in_plans` in `models.json`): -- `gpt-5.5` — HealthLog's default (first chain entry) as of 2026-06 +- `gpt-5.6-terra` — HealthLog's default (first chain entry) as of 2026-10-24, with `gpt-5.6-luna` and `gpt-5.6-sol` behind it; the backend rejected `gpt-5.5` and everything below it that day +- `gpt-5.5` — the default from 2026-06 until 2026-10-24 - `gpt-5.4` / `gpt-5.4-mini` / `gpt-5.2` — accepted per plan - `gpt-5.3-codex` — was the default (verified 2026-05-09); the backend rotated off the `-codex` line on 2026-06-02 and this slug is now only accepted intermittently, so it sits on a late chain rung @@ -646,11 +647,10 @@ To eliminate that risk class, HealthLog's `CodexClient` walks an ordered chain o ```ts const DEFAULT_SLUG_FALLBACK_CHAIN = [ - "gpt-5.5", // current default across paid tiers as of 2026-06-02 - "gpt-5.4", // documented fallback - "gpt-5.4-mini", // documented lighter-weight fallback - "gpt-5.3-codex", // late rung — still accepted intermittently - "gpt-5.2", // legacy floor + "gpt-5.6-terra", // current default, verified against the backend 2026-10-24 + "gpt-5.6-luna", // lighter sibling on the same line + "gpt-5.6-sol", // heavier sibling on the same line + "gpt-5.5", // previous default, kept as the last rung while plans differ ]; ``` diff --git a/src/__tests__/baselines/source-file-lines.json b/src/__tests__/baselines/source-file-lines.json index 936e877ea..f03269e86 100644 --- a/src/__tests__/baselines/source-file-lines.json +++ b/src/__tests__/baselines/source-file-lines.json @@ -1,7 +1,7 @@ { "$comment": "Production files over 800 lines and their length. Maintained by src/__tests__/source-size-ratchet.test.ts.", "count": 89, - "total": 99598, + "total": 99593, "entries": { "app/medications/page-client.tsx": 839, "components/charts/mood-chart.tsx": 1166, @@ -29,7 +29,7 @@ "lib/ai/coach/stream-events.ts": 817, "lib/ai/coach/turn/model.ts": 820, "lib/ai/coach/types.ts": 1011, - "lib/ai/codex-client.ts": 1094, + "lib/ai/codex-client.ts": 1089, "lib/ai/local-client.ts": 880, "lib/ai/prompts/insight-generator.ts": 1110, "lib/ai/provider.ts": 1632, diff --git a/src/lib/ai/__tests__/codex-slug-fallback.test.ts b/src/lib/ai/__tests__/codex-slug-fallback.test.ts index fd9e218dd..c7ea32c68 100644 --- a/src/lib/ai/__tests__/codex-slug-fallback.test.ts +++ b/src/lib/ai/__tests__/codex-slug-fallback.test.ts @@ -1,6 +1,7 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { CodexClient, __test } from "../codex-client"; import { singleUserTurn } from "../types"; +import { openAIReasoningFamily } from "../reasoning/support"; import { clearCodexSlugCache, getCachedCodexSlug, @@ -341,17 +342,26 @@ describe("Codex slug fallback chain", () => { }); it("DEFAULT_SLUG_FALLBACK_CHAIN tracks the current ChatGPT-auth ladder", () => { - // The backend rotated its ChatGPT-auth slugs onto the gpt-5.x line on - // 2026-06-02; the in-code default must match the accepted set, in order. + // The backend rejected every slug from gpt-5.5 down on 2026-10-24 and + // served the gpt-5.6 line; the in-code default must match the accepted + // set, in order. expect([...__test.DEFAULT_SLUG_FALLBACK_CHAIN]).toEqual([ + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.6-sol", "gpt-5.5", - "gpt-5.4", - "gpt-5.4-mini", - "gpt-5.3-codex", - "gpt-5.2", ]); }); + it("DEFAULT_SLUG_FALLBACK_CHAIN holds only slugs the reasoning gate recognises", () => { + // A slug whose family the reasoning gate does not know goes out with the + // wrong parameters. A new line (gpt-6) joins the default only together + // with its family support. + for (const slug of __test.DEFAULT_SLUG_FALLBACK_CHAIN) { + expect(openAIReasoningFamily(slug), slug).toBe("gpt-5.1+"); + } + }); + it("loadFallbackChain falls back to the default ladder with no env override", () => { const origModel = process.env.CODEX_MODEL; const origChain = process.env.CODEX_MODEL_FALLBACK_CHAIN; @@ -359,11 +369,10 @@ describe("Codex slug fallback chain", () => { delete process.env.CODEX_MODEL_FALLBACK_CHAIN; try { expect(__test.loadFallbackChain()).toEqual([ + "gpt-5.6-terra", + "gpt-5.6-luna", + "gpt-5.6-sol", "gpt-5.5", - "gpt-5.4", - "gpt-5.4-mini", - "gpt-5.3-codex", - "gpt-5.2", ]); } finally { if (origModel === undefined) delete process.env.CODEX_MODEL; diff --git a/src/lib/ai/codex-client.ts b/src/lib/ai/codex-client.ts index e5013e9fc..86c034022 100644 --- a/src/lib/ai/codex-client.ts +++ b/src/lib/ai/codex-client.ts @@ -51,23 +51,18 @@ import { const CODEX_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses"; /** - * Default fallback chain — most-current-first as of 2026-06-02. Override - * via `CODEX_MODEL_FALLBACK_CHAIN` (comma-separated) on apps01 if a - * specific plan ladder needs different ordering. `CODEX_MODEL` is - * folded into position 0 of the chain when set; duplicates are dropped. - * - * The ChatGPT Codex backend rotated its ChatGPT-auth slugs off the - * `-codex` specialist line onto the plain `gpt-5.x` line on 2026-06-02; - * the former `-codex` defaults are now rejected with a 503 "all - * configured Codex slugs were rejected". The ladder below tracks the - * current accepted set. + * Default fallback chain, most-current-first as of 2026-10-24. Override via + * `CODEX_MODEL_FALLBACK_CHAIN` (comma-separated); `CODEX_MODEL` is folded + * into position 0 when set, duplicates dropped. The backend rotates its + * ChatGPT-auth slugs without notice: by 2026-10-24 it rejected everything + * from `gpt-5.5` down and served the `gpt-5.6-*` line. The `gpt-6` line is + * left out until the reasoning, sampling and vision gates recognise it. */ const DEFAULT_SLUG_FALLBACK_CHAIN = [ - "gpt-5.5", // current default across paid tiers as of 2026-06-02 - "gpt-5.4", // documented fallback - "gpt-5.4-mini", // documented lighter-weight fallback - "gpt-5.3-codex", // late rung — still accepted intermittently - "gpt-5.2", // legacy floor + "gpt-5.6-terra", // current default, verified against the backend 2026-10-24 + "gpt-5.6-luna", // lighter sibling on the same line + "gpt-5.6-sol", // heavier sibling on the same line + "gpt-5.5", // previous default, kept as the last rung while plans differ ] as const; /**