From 70304cb98b8d082a0e06e38e0428f4fdc9b8db9b Mon Sep 17 00:00:00 2001 From: jun Date: Sat, 5 Sep 2026 04:39:08 +0900 Subject: [PATCH] docs(oauth): scope the inert pool-settings marker to strategy and threshold inert: true read as 'the whole DTO changes nothing'. That stopped being true when reactive and proactive activation were split: enabled: false still refuses the pre-dispatch account preference, it just can no longer refuse 429 rotation. A dashboard reading inert as covering enabled would render a live control as decorative. --- src/oauth/pool-settings-capability.ts | 19 +++++++++++++++---- src/server/management/oauth-account-routes.ts | 6 ++++-- tests/account-pool-management-api.test.ts | 12 ++++++++++++ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/oauth/pool-settings-capability.ts b/src/oauth/pool-settings-capability.ts index b7475167ab..82f8c06863 100644 --- a/src/oauth/pool-settings-capability.ts +++ b/src/oauth/pool-settings-capability.ts @@ -6,8 +6,13 @@ import type { OcxProviderConfig } from "../types"; * * `codex` and `anthropic` keep their own routes and storage untouched. `generic` is every * other OAuth provider the generic failover module admits; its settings persist on - * `providers..oauthAccountFailover`. Settings stored for a generic provider are a - * declared contract the selector can consume in a later slice; today they change nothing. + * `providers..oauthAccountFailover`. + * + * `strategy` and `autoSwitchThreshold` are still a declared contract the selector does not + * consume — that is what `inert` reports. `enabled` is NOT inert any more: an explicit + * `false` refuses the pre-dispatch account preference (`preferredInitialAccount`). What it can + * no longer do is refuse reactive 429 rotation, which activates on account presence and is not + * disableable. */ export type PoolSettingsKind = "codex" | "anthropic" | "generic"; @@ -37,7 +42,14 @@ export interface GenericPoolSettingsDto { enabled: boolean | null; strategy: GenericPoolStrategy | null; autoSwitchThreshold: number | null; - /** Slice-1 marker: persisted, not yet consumed by the selector. */ + /** + * Slice-1 marker for `strategy` and `autoSwitchThreshold` only: persisted, not yet consumed + * by the selector. + * + * It deliberately does NOT describe `enabled`, which governs the pre-dispatch preference. + * Widening it to the whole DTO would tell a dashboard that `enabled` changes nothing, which + * has been false since reactive and proactive activation were split. + */ inert: true; } @@ -52,4 +64,3 @@ export function genericPoolSettingsDto(name: string, provider: OcxProviderConfig inert: true, }; } - diff --git a/src/server/management/oauth-account-routes.ts b/src/server/management/oauth-account-routes.ts index 9206f96bd5..763cbc0257 100644 --- a/src/server/management/oauth-account-routes.ts +++ b/src/server/management/oauth-account-routes.ts @@ -333,8 +333,10 @@ export async function handleOauthAccountRoutes(ctx: ManagementContext): Promise< if (url.pathname === "/api/oauth/accounts/pool" && req.method === "GET") { const provider = (url.searchParams.get("provider") ?? "").trim().toLowerCase(); if (provider !== "anthropic") { - // Generic OAuth pool-settings contract (#695 slice 1): persisted per provider, inert until - // the selector consumes it. Codex keeps /api/codex-auth; api-key providers have no pool. + // Generic OAuth pool-settings contract (#695 slice 1): persisted per provider. `strategy` + // and `autoSwitchThreshold` stay inert until the selector consumes them; `enabled` already + // governs the pre-dispatch account preference. Codex keeps /api/codex-auth; api-key + // providers have no pool. const { poolSettingsCapability, genericPoolSettingsDto } = await import("../../oauth/pool-settings-capability"); const prov = config.providers[provider]; if (!provider || !prov || poolSettingsCapability(provider, prov) !== "generic") { diff --git a/tests/account-pool-management-api.test.ts b/tests/account-pool-management-api.test.ts index 5f1b0ad43e..30accf7285 100644 --- a/tests/account-pool-management-api.test.ts +++ b/tests/account-pool-management-api.test.ts @@ -432,6 +432,18 @@ describe("Anthropic account pool strategy management API", () => { await server.stop(true); } }); + test("the inert marker describes strategy/threshold only, never enabled", async () => { + // `inert: true` used to read as "the whole DTO changes nothing". That stopped being true + // when reactive and proactive activation were split: `enabled: false` still refuses the + // pre-dispatch account preference, it just can no longer refuse 429 rotation. A dashboard + // reading `inert` as covering `enabled` would render a live control as decorative. + const source = await Bun.file("src/oauth/pool-settings-capability.ts").text(); + const start = source.indexOf("autoSwitchThreshold: number | null;"); + const marker = source.slice(start, source.indexOf("inert: true;", start)); + expect(marker).toContain("strategy"); + expect(marker).toContain("autoSwitchThreshold"); + expect(marker).toContain("enabled"); + }); }); describe("generic OAuth pool-settings contract (#695)", () => {