From 51c12793aff4ce93226f028073d4c0414a7a9abd Mon Sep 17 00:00:00 2001 From: luvs01 Date: Wed, 26 Aug 2026 10:51:07 +0900 Subject: [PATCH] fix(claude): withhold ambiguous bare context windows --- src/claude/context-windows.ts | 11 +++++------ tests/claude-context-windows.test.ts | 19 ++++++++++--------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/claude/context-windows.ts b/src/claude/context-windows.ts index dd22fc93a9..a0614f3b74 100644 --- a/src/claude/context-windows.ts +++ b/src/claude/context-windows.ts @@ -127,13 +127,12 @@ export function buildClaudeContextWindows( m.contextWindow > 0 && !(m.provider === "anthropic" && m.contextWindow < ONE_MILLION), ); - // Bare routed ids are registered only when unambiguous across providers (audit - // 021 #5) — natives are registered first, so a native slug always wins the bare - // key. Counted over the rows that can actually claim the key: a row this loop - // skips contributes no window, so letting it veto the bare key withholds an - // answer that was never in doubt. + // Bare routed ids are registered only when unambiguous across every provider + // that can route the id (audit 021 #5) — natives are registered first, so a + // native slug always wins the bare key. Rows without a registrable window must + // still count because they remain valid routing candidates for the bare id. const bareCounts = new Map(); - for (const m of registrable) bareCounts.set(m.id, (bareCounts.get(m.id) ?? 0) + 1); + for (const m of routedModels) bareCounts.set(m.id, (bareCounts.get(m.id) ?? 0) + 1); for (const m of registrable) { const window = m.contextWindow as number; put(`${m.provider}/${m.id}`, window); diff --git a/tests/claude-context-windows.test.ts b/tests/claude-context-windows.test.ts index 85c70d321e..82b79e9c5a 100644 --- a/tests/claude-context-windows.test.ts +++ b/tests/claude-context-windows.test.ts @@ -133,30 +133,31 @@ describe("auto-context (devlog 260712 020 + audit 021)", () => { expect(map["gpt-5.6-sol"]).toBe(272_000); // native default, not 999k }); - test("a row that registers nothing does not make a bare id ambiguous", () => { - // Only one of these two rows can claim the bare key, so there is nothing to - // be ambiguous about — withholding it left a 1M model with no window, and a - // slot set to the bare id lost its [1m] marker. + test("rows without a registrable window still make a bare id ambiguous", () => { + // Both rows remain routing candidates even though only one has a window, so + // the bare selector cannot safely inherit either provider's metadata. const noWindow = buildClaudeContextWindows([], [ { provider: "a", id: "shared-model", contextWindow: 1_000_000 }, { provider: "b", id: "shared-model" } as CatalogModel, ]); - expect(noWindow["shared-model"]).toBe(1_000_000); + expect(noWindow["shared-model"]).toBeUndefined(); + expect(noWindow["a/shared-model"]).toBe(1_000_000); - // Same for a row the anthropic sub-1M guard skips. + // Same for a row the anthropic sub-1M guard skips from registration. const anthropicSkipped = buildClaudeContextWindows([], [ { provider: "openrouter", id: "claude-x", contextWindow: 1_000_000 }, { provider: "anthropic", id: "claude-x", contextWindow: 200_000 }, ]); - expect(anthropicSkipped["claude-x"]).toBe(1_000_000); + expect(anthropicSkipped["claude-x"]).toBeUndefined(); + expect(anthropicSkipped["openrouter/claude-x"]).toBe(1_000_000); expect(anthropicSkipped["anthropic/claude-x"]).toBeUndefined(); - // A zero or negative window is not a claim either. + // Zero and negative windows also remain routing candidates. const zeroWindow = buildClaudeContextWindows([], [ { provider: "a", id: "shared-model", contextWindow: 400_000 }, { provider: "b", id: "shared-model", contextWindow: 0 }, ]); - expect(zeroWindow["shared-model"]).toBe(400_000); + expect(zeroWindow["shared-model"]).toBeUndefined(); }); test("two providers that both register keep the bare id withheld", () => {