From e8936f53f462a768fed7bcbd0b3479e1682a8fec Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Fri, 7 Aug 2026 04:08:35 -0300 Subject: [PATCH] fix(db,combo): clear the NEW base-reds the 08-06 merge batch introduced MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tip moved while the first sweep PR (#9600) was in review, and three fresh base-reds landed with it — same classes as before, all reproduced on the pure tip 9995bc4893: 1. ANOTHER migration collision: #9061 shipped 134_ccr_blocks.sql onto the slot 134_proxy_logs_egress_ip.sql (#9291) has held since 08-04. getMigrationFiles() throws on collision, so every DB-touching test died at bootstrap again. Renumbered to 139 (next free slot). No retroactive guard needed this time: both statements are IF NOT EXISTS, and no DB can have applied it as 134 — the runner refused to run at all while the collision existed. 2. BROKEN IMPORT killing the combo module graph: #8894 imported preferAntigravityConnectionsWithStoredProject from ../antigravityProjectPersistence.ts — a module that exists NOWHERE in the repo (it came from an unmerged sibling branch). Anything importing quotaStrategies.ts died with ERR_MODULE_NOT_FOUND. Implemented the helper in the real persistence module (antigravityProjectPersist.ts, #8491) with the semantics the call site needs — prefer connections that already carry a stored projectId, never emptying the pool — and pointed the import there. New regression suite tests/unit/antigravity-prefer-stored-project.test.ts (5/5), including an import-graph probe that reproduces the break shape. 3. Sibling-test drift from #9106 (gemini-3.1-pro-high now user-callable): its own suites were updated but provider-models-route.test.ts was not. Expected discovery list realigned; testFrozen 1784->1787 justified in the baseline (irreducible +2 after comment compression; gate counts split-newlines). Also regenerated tests/snapshots/provider/translate-path.json — addition-only: devin-cli-agentic, raycast, regolo (today's provider merges), zero removals. image-generation-route 20/20 (was import-dead), provider-models-route 59/59, antigravity-prefer-stored-project 5/5, provider-translate-path-golden 3/3. Refs #9298 --- config/quality/file-size-baseline.json | 5 +- .../services/antigravityProjectPersist.ts | 37 ++++++++++ open-sse/services/combo/quotaStrategies.ts | 2 +- ...{134_ccr_blocks.sql => 139_ccr_blocks.sql} | 0 tests/snapshots/provider/translate-path.json | 69 +++++++++++++++++++ .../antigravity-prefer-stored-project.test.ts | 52 ++++++++++++++ tests/unit/provider-models-route.test.ts | 12 ++-- 7 files changed, 166 insertions(+), 11 deletions(-) rename src/lib/db/migrations/{134_ccr_blocks.sql => 139_ccr_blocks.sql} (100%) create mode 100644 tests/unit/antigravity-prefer-stored-project.test.ts diff --git a/config/quality/file-size-baseline.json b/config/quality/file-size-baseline.json index c498ceefaa..e97f86d599 100644 --- a/config/quality/file-size-baseline.json +++ b/config/quality/file-size-baseline.json @@ -371,7 +371,7 @@ "tests/unit/model-sync-route.test.ts": 1016, "tests/unit/models-catalog-route.test.ts": 1636, "tests/unit/perplexity-web.test.ts": 1355, - "tests/unit/provider-models-route.test.ts": 1784, + "tests/unit/provider-models-route.test.ts": 1787, "tests/unit/provider-validation-specialty.test.ts": 2985, "tests/unit/providers-page-utils.test.ts": 1106, "tests/unit/response-sanitizer.test.ts": 1063, @@ -607,5 +607,6 @@ "_rebaseline_2026_08_05_9323_agentrouter_waf_retry": "PR #9323 (fix(agentrouter): retry on 400 content-blocked + burst guard) own growth: open-sse/executors/base.ts 1578->1623 (check-file-size.mjs conta via split(\"\\n\").length; wc -l ve 1622). As +45 linhas sao o WAF_RETRY_CONFIG + o burst guard via gateOutboundRequest() para o WAF do agentrouter.org, com comentarios explicando o porque de cada mitigacao e cobertos por tests/unit/base-executor-waf-retry.test.ts e tests/unit/wafRateLimit.test.ts. Crescimento funcional legitimo, nao inchaco.", "_rebaseline_2026_08_05_9529_own_growth": "PR #9529 own growth (base release/v3.8.50 medida EXATAMENTE nos frozen antigos, entao o modo base-relative #8522 nao cobre): open-sse/services/rateLimitManager.ts 1060->1105 (+45: helper applyLimiterSettings() que re-arma o heartbeat do reservoir apos updateSettings — fix do bug Bottleneck 2.19.5 que congelava a fila weighted; TDD em tests/unit/ratelimit-reservoir-refresh.test.ts); tests/integration/chat-pipeline.test.ts 1592->1598 (+6: User-Agent do codex derivado de getCodexClientVersion() em vez de literal pinado — teste-irmao alinhado ao contrato); tests/unit/provider-validation-specialty.test.ts 2980->2985 (+5: cobertura NOVA claude-web 429 -> valid:false, alinhamento #9406); open-sse/translator/response/openai-responses.ts 1174->1204 (+30: buildResponsesReasoningSummaryDelta MOVIDA do leaf pureHelpers.ts para o host — a funcao do #9500 muta stream state e violava o contrato do leaf puro; o LOC total do par host+leaf nao cresceu, o pureHelpers encolheu o mesmo tanto). Crescimento por fix de producao + cobertura adicional + realocacao arquitetural, nao inchaco.", "_rebaseline_2026_08_06_v3850_inherited_drift_reconcile": "Reconciliacao 2026-08-06 do drift ACUMULADO da release/v3.8.50 apos o lote de merges de 08-05/06: 13 arquivos acima do frozen no tip puro 8180b49ce1 (medidos pelo proprio gate). O modo PR base-relative (#8522) deixa PRs inocentes passarem, e os rebaselines individuais dos PRs se perderam nas resolucoes sucessivas de conflito deste hot-file — o drift so aparece no modo absoluto (nightly/local). Crescimentos funcionais dos PRs mergeados: #9024 topology click-nav src/app/(dashboard)/dashboard/HomePageClient.tsx; #9324 OpenRouter enrich src/app/(dashboard)/dashboard/providers/page.tsx; #9329 quota card ordering src/app/(dashboard)/dashboard/usage/components/ProviderLimits/index.tsx; #9193 context-window suffixes src/sse/handlers/chat.ts; #9332 nested Claude server tool ids open-sse/executors/base.ts; #9228 strip orphaned tool outputs open-sse/executors/codex.ts; #9236 nvidia tool-name normalize open-sse/executors/default.ts; #9314 nested tool_call validation open-sse/executors/kiro.ts; #9260 caller identity REST hops open-sse/mcp-server/server.ts; #8934 cache breakpoints tests tests/unit/chatcore-translation-paths.test.ts; #9193 suffix tests tests/unit/combo-routing-engine.test.ts; #9196 reasoning-on-tool-finish tests tests/unit/sse-auth.test.ts; #9163 GPT-5.6 Max reasoning tests tests/unit/translator-openai-to-kiro.test.ts. default.ts e kiro.ts entram no frozen (estavam sem entrada, acima do cap 1000). Atualizacao pos-medicao (a base avancou durante o ciclo do PR): src/sse/handlers/chat.ts 1857->1877 (#9184 affinity EOF evict) e open-sse/executors/default.ts 1027->1042 (#9005 Kimi K3 tool-name backfill).", - "_rebaseline_2026_08_06b_v3850_sweepreds_drift": "Segunda reconciliacao de 2026-08-06 (/sweep-reds sobre o tip puro 2ddbbc61a6): 3 arquivos voltaram a passar do frozen apos os merges do mesmo dia, com atribuicao 1:1 por commit. (1) src/app/(dashboard)/dashboard/providers/page.tsx 1928->1944 e (2) open-sse/executors/base.ts 1635->1640, ambos do #9515 (feat(radar): flag-gated signed free-model catalog overlay, commit e7f6b1d130) — o overlay do Radar entra por wiring nos chokepoints ja existentes (a resolucao/verificacao do catalogo assinado mora fora destes dois arquivos); +16 e +5 linhas liquidas nao sao extraiveis sem inventar um leaf por callsite. (3) open-sse/services/accountFallback.ts 1966->1972 do #8704 (commit c4527f97bd), +6 linhas de dados em CREDITS_EXHAUSTED_SIGNALS ('has been exhausted', fixes #8631). src/sse/handlers/chat.ts 1880>1877 tambem estava violando e NAO entra aqui de proposito: e drenado por encolhimento na PR #9598, sem rebaseline. Crescimento proprio DESTA PR: src/lib/db/migrationRunner.ts 1077->1084 (+7) — o guard retroativo em isSchemaAlreadyApplied para os arquivos renumerados 137/138, exigido pela propria mensagem de erro de colisao do runner (ambas as migracoes sao ALTER TABLE ADD COLUMN puro, nao idempotente). Dois `case` + dois `return hasColumn(...)` + 3 linhas de comentario dentro do switch existente; nao extraivel." + "_rebaseline_2026_08_06b_v3850_sweepreds_drift": "Segunda reconciliacao de 2026-08-06 (/sweep-reds sobre o tip puro 2ddbbc61a6): 3 arquivos voltaram a passar do frozen apos os merges do mesmo dia, com atribuicao 1:1 por commit. (1) src/app/(dashboard)/dashboard/providers/page.tsx 1928->1944 e (2) open-sse/executors/base.ts 1635->1640, ambos do #9515 (feat(radar): flag-gated signed free-model catalog overlay, commit e7f6b1d130) — o overlay do Radar entra por wiring nos chokepoints ja existentes (a resolucao/verificacao do catalogo assinado mora fora destes dois arquivos); +16 e +5 linhas liquidas nao sao extraiveis sem inventar um leaf por callsite. (3) open-sse/services/accountFallback.ts 1966->1972 do #8704 (commit c4527f97bd), +6 linhas de dados em CREDITS_EXHAUSTED_SIGNALS ('has been exhausted', fixes #8631). src/sse/handlers/chat.ts 1880>1877 tambem estava violando e NAO entra aqui de proposito: e drenado por encolhimento na PR #9598, sem rebaseline. Crescimento proprio DESTA PR: src/lib/db/migrationRunner.ts 1077->1084 (+7) — o guard retroativo em isSchemaAlreadyApplied para os arquivos renumerados 137/138, exigido pela propria mensagem de erro de colisao do runner (ambas as migracoes sao ALTER TABLE ADD COLUMN puro, nao idempotente). Dois `case` + dois `return hasColumn(...)` + 3 linhas de comentario dentro do switch existente; nao extraivel.", + "_rebaseline_2026_08_06c_v3850_sweepreds_pr2": "Segunda PR do /sweep-reds (fix/release-v3.8.50-basereds-0806b): tests/unit/provider-models-route.test.ts 1784->1787 (medido pelo gate, que conta split(\"\\n\").length) (+2 apos compressao de comentarios) — alinhamento de contrato forcado por dois merges do dia: #9106 tornou gemini-3.1-pro-high user-callable (a entry do alias entra na lista esperada do teste de discovery-retry, +1 linha de dado + 1 de comentario) e ff012ff420 adicionou onboardUser como bootstrap hop (exclusao no mock, ja comprimida a 1 linha). Nao ha o que encolher sem apagar o comentario que explica o porque." } diff --git a/open-sse/services/antigravityProjectPersist.ts b/open-sse/services/antigravityProjectPersist.ts index 8a0d1070a5..1068c4d3ec 100644 --- a/open-sse/services/antigravityProjectPersist.ts +++ b/open-sse/services/antigravityProjectPersist.ts @@ -22,6 +22,43 @@ import { updateProviderConnection } from "@/lib/db/providers"; * Best-effort / non-fatal by design: a persistence failure must never block * the in-flight request, which already has the discovered id in hand. */ +/** + * Selection-side companion of the persistence write path (#8894): given a pool + * of Antigravity/AGY connections, prefer the ones that already carry a stored + * projectId — they can serve a request without the `loadCodeAssist` discovery + * round-trip. "Prefer", not "require": when NO connection has a stored project + * the pool is returned unchanged, so a fresh install never empties its + * candidate list. + * + * Sync on purpose (called inside the quota-strategy connection expansion, which + * builds candidate lists without awaiting per-connection work). Tolerates + * `providerSpecificData` arriving either parsed or as the raw DB JSON string. + */ +export function preferAntigravityConnectionsWithStoredProject>( + connections: T[] +): T[] { + if (!Array.isArray(connections) || connections.length === 0) return connections; + const hasStoredProject = (connection: T): boolean => { + if (typeof connection.projectId === "string" && connection.projectId) return true; + let psd = connection.providerSpecificData; + if (typeof psd === "string") { + try { + psd = JSON.parse(psd); + } catch { + return false; + } + } + return Boolean( + psd && + typeof psd === "object" && + typeof (psd as Record).projectId === "string" && + (psd as Record).projectId + ); + }; + const withStoredProject = connections.filter(hasStoredProject); + return withStoredProject.length > 0 ? withStoredProject : connections; +} + export async function persistDiscoveredAntigravityProjectId( connectionId: string | undefined | null, discoveredProjectId: string | undefined | null, diff --git a/open-sse/services/combo/quotaStrategies.ts b/open-sse/services/combo/quotaStrategies.ts index 2e117f74fe..cff82c1369 100644 --- a/open-sse/services/combo/quotaStrategies.ts +++ b/open-sse/services/combo/quotaStrategies.ts @@ -45,7 +45,7 @@ import { type QuotaFetchCacheConfig, } from "./quotaScoring.ts"; import { rankByHeadroom, type HeadroomSaturation } from "./headroomRanking.ts"; -import { preferAntigravityConnectionsWithStoredProject } from "../antigravityProjectPersistence.ts"; +import { preferAntigravityConnectionsWithStoredProject } from "../antigravityProjectPersist.ts"; import { isQuotaExhaustedForRequest } from "../../../src/domain/quotaCache.ts"; const RESET_AWARE_CONNECTION_CACHE_TTL_MS = 30_000; diff --git a/src/lib/db/migrations/134_ccr_blocks.sql b/src/lib/db/migrations/139_ccr_blocks.sql similarity index 100% rename from src/lib/db/migrations/134_ccr_blocks.sql rename to src/lib/db/migrations/139_ccr_blocks.sql diff --git a/tests/snapshots/provider/translate-path.json b/tests/snapshots/provider/translate-path.json index cfc4255cd8..5dd9b54f32 100644 --- a/tests/snapshots/provider/translate-path.json +++ b/tests/snapshots/provider/translate-path.json @@ -1488,6 +1488,29 @@ "stream": "devin://acp/stdio" } }, + "devin-cli-agentic": { + "format": "claude", + "headers": { + "apiKey": { + "Accept": "text/event-stream", + "Authorization": "Bearer ", + "Content-Type": "application/json" + }, + "nonStream": { + "Authorization": "Bearer ", + "Content-Type": "application/json" + }, + "oauth": { + "Accept": "text/event-stream", + "Authorization": "Bearer ", + "Content-Type": "application/json" + } + }, + "url": { + "nonStream": "devin://acp/stdio", + "stream": "devin://acp/stdio" + } + }, "dgrid": { "format": "openai", "headers": { @@ -4274,6 +4297,52 @@ "stream": "https://chat.qwen.ai/api/v2/chat/completions" } }, + "raycast": { + "format": "openai", + "headers": { + "apiKey": { + "Accept": "text/event-stream", + "Authorization": "Bearer ", + "Content-Type": "application/json" + }, + "nonStream": { + "Authorization": "Bearer ", + "Content-Type": "application/json" + }, + "oauth": { + "Accept": "text/event-stream", + "Authorization": "Bearer ", + "Content-Type": "application/json" + } + }, + "url": { + "nonStream": "https://backend.raycast.com/api/v1/ai", + "stream": "https://backend.raycast.com/api/v1/ai" + } + }, + "regolo": { + "format": "openai", + "headers": { + "apiKey": { + "Accept": "text/event-stream", + "Authorization": "Bearer ", + "Content-Type": "application/json" + }, + "nonStream": { + "Authorization": "Bearer ", + "Content-Type": "application/json" + }, + "oauth": { + "Accept": "text/event-stream", + "Authorization": "Bearer ", + "Content-Type": "application/json" + } + }, + "url": { + "nonStream": "https://api.regolo.ai", + "stream": "https://api.regolo.ai" + } + }, "reka": { "format": "openai", "headers": { diff --git a/tests/unit/antigravity-prefer-stored-project.test.ts b/tests/unit/antigravity-prefer-stored-project.test.ts new file mode 100644 index 0000000000..dbc663113a --- /dev/null +++ b/tests/unit/antigravity-prefer-stored-project.test.ts @@ -0,0 +1,52 @@ +/** + * Regression guard for the #8894 import break: quotaStrategies.ts imported + * `preferAntigravityConnectionsWithStoredProject` from a module that never + * landed (`antigravityProjectPersistence.ts`), killing the whole combo module + * graph with ERR_MODULE_NOT_FOUND on the release tip. The helper now lives in + * the real persistence module (`antigravityProjectPersist.ts`); these tests pin + * its selection semantics. + * + * Run: node --import tsx/esm --test tests/unit/antigravity-prefer-stored-project.test.ts + */ +import { test } from "node:test"; +import assert from "node:assert/strict"; + +import { preferAntigravityConnectionsWithStoredProject } from "../../open-sse/services/antigravityProjectPersist.ts"; + +const withProject = { id: "a", projectId: "proj-1" }; +const withNestedProject = { id: "b", providerSpecificData: { projectId: "proj-2" } }; +const withStringPsd = { id: "c", providerSpecificData: '{"projectId":"proj-3"}' }; +const withoutProject = { id: "d", projectId: null, providerSpecificData: {} }; +const withBrokenPsd = { id: "e", providerSpecificData: "{not json" }; + +test("prefers connections that already carry a stored projectId", () => { + const pool = [withoutProject, withProject, withNestedProject]; + assert.deepEqual( + preferAntigravityConnectionsWithStoredProject(pool).map((c) => c.id), + ["a", "b"] + ); +}); + +test("reads projectId from a raw JSON-string providerSpecificData", () => { + const pool = [withoutProject, withStringPsd]; + assert.deepEqual( + preferAntigravityConnectionsWithStoredProject(pool).map((c) => c.id), + ["c"] + ); +}); + +test("never empties the pool: no stored project anywhere → unchanged", () => { + const pool = [withoutProject, withBrokenPsd]; + assert.deepEqual(preferAntigravityConnectionsWithStoredProject(pool), pool); +}); + +test("empty input passes through", () => { + assert.deepEqual(preferAntigravityConnectionsWithStoredProject([]), []); +}); + +test("the quota-strategy module graph resolves (the #8894 break shape)", async () => { + // Importing quotaStrategies transitively exercises the fixed import path; the + // pre-fix tip died here with ERR_MODULE_NOT_FOUND before any test could run. + const mod = await import("../../open-sse/services/combo/quotaStrategies.ts"); + assert.ok(mod, "quotaStrategies must be importable"); +}); diff --git a/tests/unit/provider-models-route.test.ts b/tests/unit/provider-models-route.test.ts index e5379d1e77..d46cecf631 100644 --- a/tests/unit/provider-models-route.test.ts +++ b/tests/unit/provider-models-route.test.ts @@ -934,14 +934,8 @@ test("provider models route retries Antigravity discovery endpoints before retur // After PR #2219, the discovery flow calls loadCodeAssist first as a project // bootstrap; treat all bootstrap calls as non-fatal failures so the test // exercises the discovery retry path. - // `onboardUser` joined loadCodeAssist as a bootstrap hop (ff012ff420: fall back - // to onboardUser when loadCodeAssist fails). It must be excluded here too — - // otherwise it consumes the single 503 below and discovery never retries, - // which is a mock-ordering artifact, not a routing change. - if ( - urlString.includes("/v1internal:loadCodeAssist") || - urlString.includes("/v1internal:onboardUser") - ) { + // onboardUser is a bootstrap hop too (ff012ff420) — else it eats the single 503 below. + if (urlString.includes(":loadCodeAssist") || urlString.includes(":onboardUser")) { return new Response("nope", { status: 503 }); } seenUrls.push(urlString); @@ -990,6 +984,8 @@ test("provider models route retries Antigravity discovery endpoints before retur "https://cloudcode-pa.googleapis.com/v1internal:fetchAvailableModels", ]); assert.deepEqual(body.models, [ + // #9106: both alias ids are user-callable now, so the upstream echo survives the filter. + { id: "gemini-3.1-pro-high", name: "Gemini 3.1 Pro (High)" }, { id: "gemini-pro-agent", name: "Gemini 3.1 Pro (High)" }, { id: "gemini-3.6-flash-high", name: "Gemini 3.6 Flash (High)" }, { id: "gemini-3.6-flash-medium", name: "Gemini 3.6 Flash (Medium)" },