Validado numa worktree combinada com as 16 PRs desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-file-size e check-changelog-integrity OK, complexity 2788/3218 e cognitive 1261/1437, ESLint 0 erros nos 152 arquivos alterados, e a suíte vitest:ui completa (2149) verde.
Sobre esta PR especificamente: rodei os **23 arquivos de teste** que ela toca sobre o tip final, depois do merge da base — **392/392**. A migration `174_server_tool_executions.sql` não colide (o tip está em 173, e você já a renumerou em `c35f0fd7`).
O dono foi consultado antes do merge, porque o loop está atrás da flag `SERVER_OWNED_TOOL_LOOP_ENABLED` mas o primeiro send não-streaming mudou de dono sem flag, e a verificação manual em combo com Memory continuava desmarcada. A condição dele foi: entra se os testes focados passarem aqui. Passaram.
O lock de passthrough (`fetchCalls.length === 1`) é a parte que mais me convenceu — o double-dispatch que um `if (stream)` em volta do send existente causaria é exatamente o tipo de regressão que não aparece em teste de comportamento, só em contagem de chamada.
**Três ajustes meus na sua branch:**
1. `tests/unit/chatcore-stream-error-result.test.ts` procurava `"const legResult = await runNonStreamingProviderLeg"`, mas o seu commit final `6077b9dd` passou a reatribuir `legResult` e trocou para `let`. O guard falhava na sua própria branch (confirmei que o arquivo e o `chatCore.ts` eram byte-idênticos ao head da PR, então não era efeito da leva). Passou a aceitar `const|let` — a intenção do guard é o try/catch em volta da chamada, não a palavra-chave.
2. `tests/integration/skills-pipeline.test.ts` foi de 1156 para 1338 linhas e estourou o `testCap` de 1200. Segui o mesmo caminho que você já tinha tomado em `a1d2d20d` para os testes unitários: extraí os três casos do server-owned tool loop para `tests/integration/server-owned-tool-loop-pipeline.test.ts` (259 linhas), com instância própria do harness. O glob `tests/integration/*.test.ts` pega o arquivo novo sem registro adicional. 3/3 verdes isolados.
3. O arquivo novo herdou cinco `any` do original — que só passavam por estarem congelados no `eslint-suppressions.json` sob o nome antigo. Tipei como `Record<string, unknown>`. E `tests/unit/non-streaming-finalization.test.ts` tinha dois argumentos não usados em `trackPendingRequest`, agora prefixados com `_`.
Nada disso toca produção nem enfraquece asserção.
Validado numa worktree combinada com as 16 PRs desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-file-size e check-changelog-integrity OK, complexity 2788/3218 e cognitive 1261/1437, ESLint 0 erros nos 152 arquivos alterados, 771 testes unitários focados, 49 de integração e a suíte vitest:ui completa (2149) verdes.
Guardar os contratos com testes ANTES de levantar o bloco (`05059880` no irmão, e o `287658f5` marcando os `executeTargetGates` como lift-as-is) é o que torna um refactor deste tamanho auditável. Sem essa ordem, um extract de 3 mil linhas é indistinguível de uma reescrita.
Revalidei depois do merge da base: `combo-attempt-loop`, `execute-target-attempt`, `execute-target-gates` e `combo-loop-safety-timer-leak-11804` — 20/20 — mais typecheck:core limpo e o cap de arquivo OK.
O #12811 entra na sequência logo em seguida, com os três commits do round-robin sobre este.
Validado numa worktree combinada com as 16 PRs desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-file-size e check-changelog-integrity OK, complexity 2788/3218 e cognitive 1261/1437, ESLint 0 erros nos 152 arquivos alterados, 771 testes unitários focados, 49 de integração e a suíte vitest:ui completa (2149) verdes.
Reservar o sorteio antes do próximo `await` (`2cf74acc`) é a parte não-óbvia e a que mais importa: sem isso dois pipelines no mesmo processo observam `inflight=0` na mesma conta e convergem para ela. O comentário no código explica isso melhor do que o commit message.
**Um ajuste meu na sua branch.** O `tests/unit/combo/quota-weighted-strategy.test.ts` era intermitente — falhava em cerca de 1 a cada 5 execuções, alternando entre `A/B isolation: 7 hard-empty…` e `floor=0 puts 0.5% in the main pool`, sempre com dois pares de mesma faixa trocando de posição. A causa é o helper de fixture:
```ts
const iso = (ms = 86_400_000) => new Date(Date.now() + ms).toISOString();
```
Como `iso()` é chamado a cada invocação do fetcher, dois peers que deveriam empatar recebiam `resetAt` com um milissegundo de diferença sempre que o relógio virava entre as duas chamadas. Pressão de reset entra no score, então esse epsilon quebrava o empate e `sortByScoreThenIndex` nunca chegava ao fallback por índice de inserção.
Fixei a base do relógio uma vez só (`CLOCK_BASE`). Nenhuma asserção foi tocada — as garantias de ordem, tamanho e exclusão continuam idênticas. 10/10 execuções verdes depois, e mais 6/6 após o merge da base nesta branch.
Também mergeei a base para resolver `file-size-baseline.json` (aditivo) e `src/domain/quotaCache.ts`, onde o seu placeholder `_providerSpecificData` cedeu lugar à implementação do #12803, que usa o parâmetro de fato.
Validado numa worktree combinada com as 16 PRs desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-file-size e check-changelog-integrity OK, complexity 2788/3218 e cognitive 1261/1437 (ambos sob a baseline), ESLint 0 erros nos 152 arquivos alterados, 771 testes unitários focados, 49 de integração e a suíte vitest:ui completa (2149) verdes. Após o merge da base nesta branch, os 11/11 do `combo-pin-implicit-allowlist` foram revalidados.
A distinção entre pin de step de combo e pin forçado por header (`x-omniroute-connection`) é o que salva a PR de virar uma restrição ampla demais — o header continua permitindo fallback para conexões irmãs, o step não.
Apontar que o `a11930ec4` para a rotação dentro do `handleSingleModel` mas não popula `allowedConnectionIds` no resolve foi a peça que explicou por que os dois são complementares e não redundantes. Sem isso a PR pareceria duplicar um gate que já existia.
Validado numa worktree combinada com as 16 PRs desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-file-size e check-changelog-integrity OK, complexity 2788/3218 e cognitive 1261/1437 (ambos sob a baseline), ESLint 0 erros nos 152 arquivos alterados, 771 testes unitários focados, 49 de integração e a suíte vitest:ui completa (2149) verdes.
`blockExtraUsage: false` significa "pode usar crédito extra", nunca "esconda a conta antes de despachar" — a conta saía da rota justamente quando o crédito extra existia para ser usado. Os 32/32 cobrem os quatro pontos onde a mesma decisão era tomada, e revalidei após o merge da base (32/32 de novo).
Nota de integração: o seu `isQuotaExhaustedForRequest` colidiu com o placeholder `_providerSpecificData` do #12789 na worktree combinada. Ficou a sua implementação, que é a que de fato usa o parâmetro. A base foi mergeada na branch para resolver o `file-size-baseline.json` (aditivo, JSON revalidado).
Validado numa worktree combinada com as 16 PRs desta leva sobre `release/v3.8.51`: typecheck:core limpo, check-file-size e check-changelog-integrity OK, complexity 2788/3218 e cognitive 1261/1437 (ambos sob a baseline), ESLint 0 erros nos 152 arquivos alterados, 771 testes unitários focados, 49 de integração e a suíte vitest:ui completa (2149) verdes.
O `nodeMap` lido do closure de `RuntimePageClient` por uma função de nível de módulo é uma bomba-relógio silenciosa: só explode quando um monitor entra em error/exhausted/alerting, e o teste existente só alimentava listas vazias. Tirar o arquivo do exclude do vitest vale tanto quanto o fix — confirmei aqui que `tests/unit/ui/runtime-page-client.test.tsx` agora roda na `test:vitest:ui` e passa.
A anotação sobre o "内部服务器错误" ser o catálogo RSC da página, e não o crash, poupou o próximo a caçar fantasma.
Merged. This removes the cause that #12607 had to freeze.
`react-hooks/set-state-in-effect` on this file was living in `config/quality/eslint-suppressions.json` as a frozen count of 1 — the lint was green because the violation was suppressed, not because it was gone. `useSyncExternalStore` is the sanctioned shape for exactly this problem: `getServerSnapshot` supplies the SSR-safe default, `getSnapshot` reads localStorage after hydration, and the tree commits once instead of twice. The `storage` listener keeping other tabs in sync is a real bonus.
The detail that makes this correct rather than merely lint-clean: you kept "hide for now" and "hide forever" as separate concepts — `usageGuideHiddenForNow` stays per-mount local state while only the persisted dismissal goes through the store. A naive conversion would have collapsed them and made the temporary hide survive a reload.
Three things I added before merging:
1. **Dropped the `react-hooks/set-state-in-effect` entry from the suppressions file.** With the cause gone it becomes a stale allowlist entry, which is what the Fase 6A.3 stale-enforcement is built to flag. Verified: `eslint` on the file now reports only the 6 pre-existing `no-unused-vars`, which stay frozen.
2. **Updated the rationale comment above the hook** — it still described "correct it client-only, after hydration, in an effect", which is the shape you just removed.
3. **Rebaselined `combos/page.tsx` 5018 → 5066** in `file-size-baseline.json` with a dated annotation. The +48 lines are the module-scope store helpers; the cap is pre-authorized for legitimate growth and this is as legitimate as it gets.
Validated on `release/v3.8.51`: `check-file-size` OK, `lint` clean, `typecheck:core` and `check:dashboard-typecheck` clean (207 pre-existing, all within baseline).
Validado em lote numa worktree combinada com os 4 PRs desta leva sobre o tip de `release/v3.8.51`: `typecheck:core` limpo e **119/119** nos 9 arquivos de teste que trazem.
Três dos quatro conflitavam apenas no `config/quality/file-size-baseline.json`, todos de forma aditiva (chaves `_rebaseline_` distintas que devem coexistir); resolvidos com validação de JSON a cada passo.
Registro que o **#12637 não é duplicata do #12566**, apesar do título quase idêntico: o autor documenta que aquele escopou o cooldown de preflight por família e este cobre o `genericQuotaFetcher`, que é o que o roteamento reset-aware efetivamente chama. Traz também validação ao vivo em VPS (imagem X500, `onmi-gemini3.6` → HTTP 200), satisfazendo a Hard Rule #18.
Obrigado, @HouMinXi.
Validado em lote numa worktree combinada com os 4 PRs desta leva sobre o tip de `release/v3.8.51`: `typecheck:core` limpo e **119/119** nos 9 arquivos de teste que trazem.
Três dos quatro conflitavam apenas no `config/quality/file-size-baseline.json`, todos de forma aditiva (chaves `_rebaseline_` distintas que devem coexistir); resolvidos com validação de JSON a cada passo.
Registro que o **#12637 não é duplicata do #12566**, apesar do título quase idêntico: o autor documenta que aquele escopou o cooldown de preflight por família e este cobre o `genericQuotaFetcher`, que é o que o roteamento reset-aware efetivamente chama. Traz também validação ao vivo em VPS (imagem X500, `onmi-gemini3.6` → HTTP 200), satisfazendo a Hard Rule #18.
Obrigado, @HouMinXi.
Validado em lote numa worktree combinada com os 10 PRs desta leva sobre o tip de `release/v3.8.51`: `typecheck:core` limpo, `check-file-size` OK e **241/242** nos 29 arquivos de teste que os PRs tocam.
A única "falha" não é falha: `tests/unit/autoCombo/strict-zero-cost-filter.test.ts` é um teste em estilo Vitest que eu incluí por engano na invocação do runner nativo do Node — ele quebra no import (`@vitest/runner`), não numa asserção. Ao investigar, descobri que esse arquivo não roda em nenhum dos dois runners hoje (o glob do `test:unit` não lista `autoCombo` e o `include` do Vitest só pega `.tsx` nessa pasta); é um problema pré-existente do repositório, sem relação com esta leva, e vou registrá-lo separadamente.
O #12636 conflitava apenas na lista de testes do `@omniroute/opencode-plugin/package.json`, de forma aditiva: o tip já tinha `models-fetcher.test.ts` (do #12607, irmão desta mesma leva) e o #12636 acrescenta `telemetry.test.ts`. Fiz a união dos dois lados (25 arquivos contra 24 de cada) em vez de escolher um, o que teria removido um arquivo da suíte do plugin em silêncio.
Obrigado, @RaviTharuma.
* test(catalog): pin the 2026-09-02 free-tier re-audit facts for gemini, ollama-cloud, groq, nara and mistral
* fix(catalog): re-audit gemini, ollama-cloud, groq, nara and mistral against official pages
* fix(catalog): restore the console-verified Mistral 1B pool and harden its regression test
* docs(free-tiers): move headline to the re-audited ~1.50B and refresh pool counts
* chore(free-tiers): retire stale Groq free-tier text and preset model; fix catalog header
* docs(free-tiers): state the evidence-comment rule honestly and retire the last "14.4K RPD" Groq texts
* docs(free-tiers): retire the stale Gemini onboarding quota text
* docs(free-tier): refresh catalog-entry counts to 442 after base sync
* docs(free-tiers): restore README spacing lost in the merge and re-sync the guide counts
* docs(free-tiers): re-sync numbers after merging release/v3.8.51 (Cerebras reclassified upstream)
* fix(docs): keep the NaraRouter plans endpoint out of the API-path checker; rebaseline gateways.ts (+3)
---------
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
Rebaseline medido no tip com os 14 PRs da campanha mergeados. A anotação registra que 4 das 6 linhas do codex.ts são drift anterior à campanha, não crescimento dela. Não toca stream.ts.
17 de 18 checks verdes, **zero falhas** — incluindo os quatro shards de unit, Vitest, CodeQL, semgrep, Docs Gates, Merge integrity e o **Fast Quality Gates**, que era exatamente o que o rebaseline do `RoutingTab.tsx` (1606→1607, a linha do seletor que acompanha a nova versão de identidade) veio consertar.
O único check restante, `No new ESLint warnings`, ficou enfileirado sem iniciar (duração 0) atrás da saturação de runners de hoje. Cobri os dois comandos que ele executa, localmente e sobre este HEAD:
- `npm run check:codeql-ratchet` → **0 alertas abertos** contra baseline 6 (sem regressão).
- `npx eslint --max-warnings 0` nos 9 arquivos de código que esta branch altera → **exit 0**, nenhum warning.
Conteúdo: os dois commits do #12402 que não estavam subsumidos, com autoria do @ggiak preservada pelo cherry-pick `-x`, mais o rebaseline e o fragmento de changelog que faltava. `typecheck:core` limpo e **138/138** nos testes focados. As três versões (`claudeCodeClient.ts`, `Dockerfile`, `compose.yml`) conferem em 2.1.258, e `npm view @anthropic-ai/claude-code@2.1.258` resolve — o Dockerfile instala esse pin exato.
Rebaseline medido no tip com os 9 PRs da leva mergeados. Desfaz o vermelho de file-size que os PRs empilhados deixaram; não toca codex.ts nem stream.ts, que já violavam antes da leva.
Validado em lote numa worktree combinada com os 9 PRs desta leva sobre o tip de `release/v3.8.51`: `typecheck:core` limpo, `check:provider-consistency` OK (273 entradas REGISTRY, **356** providers canônicos), `check-docs-counts-sync` exit 0 e **300/300** nos testes que a leva toca.
O crescimento de arquivo que os PRs empilham uns sobre os outros foi rebaselinado num único registro datado (`_rebaseline_2026_09_03_houminxi_batch`), com a decomposição por arquivo: `providers/page.tsx` +18 (import CSV do #12504 + busca do #12495 no mesmo painel), `accountFallback.ts` +6 (o #12566 sobre o rebaseline que o #12590 já registrou — os dois tocam `checkFallbackError`) e `chatCore.ts` +3 (invalidez de cache de quota no 429 do #12325). As violações restantes (`codex.ts`, `stream.ts`) foram medidas também no tip puro e são drift da base, não desta leva.
Validado em lote numa worktree combinada com os 9 PRs desta leva sobre o tip de `release/v3.8.51`: `typecheck:core` limpo, `check:provider-consistency` OK (273 entradas REGISTRY, **356** providers canônicos), `check-docs-counts-sync` exit 0 e **300/300** nos testes que a leva toca.
O crescimento de arquivo que os PRs empilham uns sobre os outros foi rebaselinado num único registro datado (`_rebaseline_2026_09_03_houminxi_batch`), com a decomposição por arquivo: `providers/page.tsx` +18 (import CSV do #12504 + busca do #12495 no mesmo painel), `accountFallback.ts` +6 (o #12566 sobre o rebaseline que o #12590 já registrou — os dois tocam `checkFallbackError`) e `chatCore.ts` +3 (invalidez de cache de quota no 429 do #12325). As violações restantes (`codex.ts`, `stream.ts`) foram medidas também no tip puro e são drift da base, não desta leva.
Validado em lote numa worktree combinada com os 9 PRs desta leva sobre o tip de `release/v3.8.51`: `typecheck:core` limpo, `check:provider-consistency` OK (273 entradas REGISTRY, **356** providers canônicos), `check-docs-counts-sync` exit 0 e **300/300** nos testes que a leva toca.
O crescimento de arquivo que os PRs empilham uns sobre os outros foi rebaselinado num único registro datado (`_rebaseline_2026_09_03_houminxi_batch`), com a decomposição por arquivo: `providers/page.tsx` +18 (import CSV do #12504 + busca do #12495 no mesmo painel), `accountFallback.ts` +6 (o #12566 sobre o rebaseline que o #12590 já registrou — os dois tocam `checkFallbackError`) e `chatCore.ts` +3 (invalidez de cache de quota no 429 do #12325). As violações restantes (`codex.ts`, `stream.ts`) foram medidas também no tip puro e são drift da base, não desta leva.
Validado numa worktree sobre o tip de `release/v3.8.51`. Verifiquei as premissas em vez de aceitar a justificativa:
**Exceções de licença** — todas as afirmações batem. Os três pacotes estão instalados exatamente nas versões travadas citadas (`@eloqnt/config@0.0.2`, `@eloqnt/format-json@0.0.3`, `@eloqnt/format-po@0.0.3`), os três **omitem `package.json#license` e não têm arquivo LICENSE**, e `npm ls` confirma que são transitivos de `next-intl@4.14.1` (MIT). No registry, o SPDX das três é MIT e o `@eloqnt/config@0.1.0` existe, como o texto diz. Uso de `exceptions` (com `risk`/`reviewAt`) em vez de `allowed: UNKNOWN` é o mecanismo certo.
`check:licenses` verde: **0 violações de política**, 948 permitidos, as três novas entradas aparecendo como exceções sinalizadas não bloqueantes ao lado das duas já existentes. Teste-guarda `tests/unit/build/check-licenses.test.ts`: **36/36**.
**Vitest do A2A lifecycle** — as duas seams usadas já existiam antes deste PR: `constructor(ttlMinutes = 5, persistence: A2APersistence = defaultPersistence)` e o 4º parâmetro opcional `deps?: MemoryHitsDeps` de `executeA2ATaskWithState`. O padrão é idêntico ao que `tests/unit/a2a-task-persistence.test.ts` já fazia. Nenhuma asserção foi removida ou enfraquecida — e como o comportamento de persistência tem suíte dedicada, injetar no-ops aqui tira acoplamento incidental, não cobertura.
Ganho medido nos dois lados: corpo dos testes **784ms no tip → 233ms com o PR**, sem nenhuma linha `[DB] SQLite database ready`. Registro honesto: **no tip o arquivo passa localmente** (4/4) — o red era do CI, sob o thread pool com as 167 migrações; localmente dá para comprovar o mecanismo e a aceleração, não a falha em si.
Suíte vitest completa: **51/51 arquivos, 465/465 testes**.
Validado em lote numa worktree combinada com #12524, #12538, #12277 e #12367 sobre o tip de release/v3.8.51: os quatro boardaram sem conflito (áreas disjuntas — zai-web, nvidia, clova, cursor/devin/fable). typecheck:core limpo, check:provider-consistency OK (272 entradas REGISTRY, 355 providers canônicos), check:known-symbols OK, e 305/305 nos testes tocados pelos quatro PRs. Os IDs de modelo adicionados foram conferidos individualmente. Obrigado, @backryun.
Migrates the Claude, Grok, LMArena, Notion and Perplexity web-cookie transports from the tls-client-node/Koffi sidecar to the exactly pinned wreq-js 3.2.0 runtime, keeping the per-provider browser/OS profiles, making request cookies ephemeral, bounding and generation-protecting the shared native transport pool, removing the legacy downloader and repair path, and carrying the native binding and license evidence through the npm, standalone, Electron, Docker and Bun packaging surfaces.
This is the consolidation of the two competing migrations, and the consolidation was decided by evidence rather than by preference. #11753's six suites were installed over this implementation and run as an independent specification: 31 of 36 passed. All five failures are artefacts of #11753 being the older design, not coverage gaps —
- two hardcode the 3.0.0 pin in their assertions (this branch pins 3.2.0, which is what the release tip already resolves; #11753's 3.0.0 would have conflicted);
- one reads open-sse/services/chatgptTlsClient.ts, deleted when #11754 retired ChatGPT Web, so the test is stale against the current tip;
- two import WREQ_JS_NATIVE_BINARY_NAMES / resolveWreqJsNativeBinaryName, which this branch redesigned into WREQ_JS_NATIVE_BINDINGS / resolveWreqJsNativeBinding plus WREQ_JS_VERSION — a rename from modelling natives as file names to modelling them as package bindings, verified as an API difference rather than a lost capability (the linux-x64-gnu .node is present and serviceable).
This branch is also the strict superset by scope: 7 files exclusive to it, including the wreq-js Rust license inventory and notices, .trivyignore, open-sse/utils/tlsClient.ts and assembleStandalone.mjs. #11753 had one exclusive file, its changelog fragment. Nothing needed porting, so #11753 is superseded rather than merged, and the changelog entry credits both.
Reconciled on merge: clean against the tip. The new migration suite (tests/unit/tls-client-wreq-migration.test.ts, 1374 lines, 31 cases) is frozen at its exact LOC with the rationale — it shares one native-transport harness, so splitting it mid-merge would duplicate that harness for no coverage gain. Verified that no existing cap moves.
Verified: 182/182 across the eight TLS, native-manifest, postinstall, standalone-bundle, pack-artifact and provider-validation suites, typecheck:core clean, check:cycles OK, check-changelog-integrity OK, check-file-size OK, and every changed TypeScript file parses.
Restores ChatGPT Web on a clean-room browser transport, merged on the operator's explicit decision.
Worth stating precisely, because this touches a provenance decision: the PR does not revert #11754. It narrows RETIRED_COMMON_CHATGPT_WEB_PROVIDER_IDS to the single GPL-derived alias cgpt-web and registers chatgpt-web as a separate clean-room id. The old implementation stays retired and blocked; the retirement machinery, its error code and its 410 contract are untouched. All four retirement suites agree with that distinction and pass unchanged.
The 44 protected agent-instruction surfaces this PR touches (AGENTS.md, llm.txt and its 42 mirrors, README) were verified rather than trusted: masking digits and comparing the removed and added line sets gives 264 lines on each side, identical — every change is a provider-count substitution, with no sentence added, removed or reworded.
Reconciled on merge: clean against the tip, with the two chat chokepoints this PR grows (src/sse/handlers/chat.ts +40, open-sse/handlers/chatCore.ts +30) recorded in the file-size baseline under an annotation. Verified that exactly those two caps move and nothing else, so the #12411 ratchet holds. The rebaseline is carried on this branch rather than left in a validation worktree — the propagation mistake that put the 2026-09-02 merge waves base-red in #12434.
Verified: 504/504 across the PR's 44 test files plus all four chatgpt-web retirement suites, check:provider-consistency OK (272 REGISTRY entries, 355 canonical providers), check-file-size OK, and every changed TypeScript file parses.
Thanks @backryun — separating the clean-room id from the retired alias, instead of reopening the old one, is what made this reviewable.
Applies the repository Prettier style to tests/unit/grok-web.test.ts. No production code, no assertion changes.
The AST-identical claim was verified independently rather than taken on trust: minifying both sides through esbuild produces byte-identical output.
The reformat expands the file from 2436 to 2713 lines, past its 2437 cap, so the baseline carries a new annotated entry at 2985 — the real LOC plus ~10% headroom, per the operator's instruction, so routine additions to this suite do not re-trip the gate on formatting alone. It is recorded as a deliberate exception to the down-only ratchet #12411 re-tightened; no other entry moves.
Verified: 68/68 in the reformatted suite, check-file-size OK.
Closes the two remaining unresolved provider-asset records without weakening the provenance boundary: public/providers/nimble-search.svg is removed because no sufficient redistribution evidence is recorded, and Nimble Search renders with the internal generic icon before any CDN tier while staying fully functional; Opper is registered in the local SVG resolver with its existing bytes proven against opper-ai/provider-omniroute at immutable commit 9aacef7d6a, recording the MIT repository license from that same commit while keeping trademarkClearance null.
Validated in a combined worktree with the batch's ready set boarded onto the current tip: parse sweep clean on every changed TypeScript file, typecheck:core clean, check:dashboard-typecheck OK (207 pre-existing errors, all within baseline), check:cycles OK, check-file-size OK, 203/205 focused node tests and 94/94 vitest — the two failures belong to #12427, which is held back.
The current release dependencies already resolve the ChatGPT Web vendor diagnostics the allowance covered, so the stale open-sse typecheck allowance is removed and any future diagnostic becomes a blocking regression again. No vendor source, package manifest, checker script or runtime behaviour changes.
Validated in a combined worktree with the batch's ready set boarded onto the current tip: parse sweep clean on every changed TypeScript file, typecheck:core clean, check:dashboard-typecheck OK (207 pre-existing errors, all within baseline), check:cycles OK, check-file-size OK, 203/205 focused node tests and 94/94 vitest — the two failures belong to #12427, which is held back.
The service operator asked in writing (2026-08-30) that their service be
removed from OmniRoute entirely: executor, registry entry, no-auth catalog
entry and alias, icon mapping, env var, docs rows, dedicated tests and
snapshots, and every passing mention in comments, fixtures and CHANGELOG
entries. Provider count drops from 355 to 354 on every canonical surface.
Co-authored-by: Markus Hartung <diegosouzapw@users.noreply.github.com>
Drains the two remaining Fast Quality Gates reds the #11513 (UC) merge left
on the tip:
- error-helper: ucTts.ts and uc/ws.ts built error payloads from raw
err.message (Hard Rule #12) — now wrapped in sanitizeErrorMessage(),
behavior otherwise identical (uc suites 51/51).
- model-lifecycle: the UC catalog registers the vendor-retired gpt-5.2-codex
(bare id; only the prefixed openai/gpt-5.2-codex was allowlisted). Added to
allowedRetiredInCatalog per its policy — forwarding globally would rewrite
the just-approved provider's model. Tracking: Refs #12436.
file-size, the third red of this window, was already drained by #12434.
check-file-size was red on release/v3.8.51 with nine violations — seven source files and two test files that the 2026-09-02 merge waves grew at existing chokepoints (#12359-#12404, #11461, #11513, #12423).
The growth itself was reviewed: each file was measured and justified while validating those batches. What went wrong is the propagation — the rebaseline was computed in the throwaway combined validation worktree, and the PRs were then merged individually through their own branches, so the code landed and the caps did not. A shared-file edit made only in the validation tree reaches nothing.
This records the caps against the merged state, each entry attributed to the PR that grew it, under one _rebaseline annotation. Verified mechanically: 9 caps recorded, 0 raised beyond the file's real merged LOC, 0 unrelated entries moved — the ratchet #12411 re-tightened is intact.
Verified: check-file-size OK (135 frozen source entries across 4515 files; 39 frozen test entries across 5365), prettier clean.
* fix(memory): point the rerank-providers dynamic import at the real db module
#11390 landed with a dynamic import of the localDb barrel, which #12052 had
already removed from the base (and which Hard Rule #2 forbids) — the API
Route Typecheck gate reds on the tip with TS2307. getCachedProviderNodes
lives in src/lib/db/readCache.
* chore(quality): ratchet the api-typecheck baseline down (163 stale entries gone)
Regenerated with --update on a faithful npm ci environment (the .113 box)
against the current tip plus the rerank-providers import fix — the gate now
reads OK at 289 pre-existing errors, all baselined. No new entries added.
Adds uncensored.com as two OpenAI-compatible providers mirroring UC's own surfaces: uc, the persona/subscription side over WebSocket with a durable Clerk credential minting a short-lived per-connect token (no API key, un-metered), as a full multimodal port — chat, tools, vision, doc-RAG, image, video, TTS; and uc-direct, the metered Developer API over REST with X-api-key. Same underlying models, two billing surfaces.
Reconciled on merge. 57 files conflicted; only seven carried UC content, the rest was drift from the older release line and took the tip's side.
- executors/index.ts: the tip has since refactored the executor map to lazy dynamic imports, so uc is registered in that shape. uc-direct needs no entry — it routes through the default OpenAI-compatible executor.
- imageGeneration.ts: the branch still carried the retired designerWeb import alongside ucImage; kept only the UC one.
- config/providers/index.ts, webSessionCredentials.ts and web-cookie.ts resolved additively against the MaxAI entries #11461 put on the tip an hour earlier.
- web-cookie.ts: the uc entry declared no serviceKinds, required since #11392, so provider validation would have thrown at load. Declared ["llm"]. uc-direct already declared it at the end of its own entry — an earlier pass of mine added a second one after id and TypeScript caught the duplicate (TS1117); the author's placement is what shipped.
Every count was measured against the merged tree rather than taken from the branch, and all three would have been wrong: reserved prefixes are 406, not 399; APIKEY_PROVIDERS is 237, not 234; providers are 355. PROVIDER_REFERENCE.md regenerated, the count updated across README/AGENTS.md/llm.txt and its 42 mirrors, package.json and 6 SVGs — every changed line in the protected surfaces is a digit substitution and nothing else, verified by masking digits and comparing the removed and added sets (90 lines each, identical). The executor-map golden snapshot went 134 -> 135.
The branch's file-size-baseline.json predates #12411's ratchet re-tightening and was discarded rather than merged; imageGeneration.ts (+12 for the uc-image format branch) was entered against the current baseline under a _rebaseline annotation, and no other cap moves.
Verified: typecheck:core clean, check:provider-consistency OK (271 REGISTRY entries, 355 canonical providers), check:docs-counts exit 0, check-file-size OK, check:cycles OK, 119/119 across the PR's test files, and 2/2 executor-map-golden.
Thanks @arminanton — two providers for two real billing surfaces, rather than one entry pretending to be both, is the right modelling.
MaxAI joins as a first-class signed provider: 13 chat models discovered live from /models/get_config plus 6 image models, routed through the standard /v1 endpoints with per-request X-Authorization signing, browserless onboarding, prompted tool-calling, vision input, image generation and document RAG.
Reconciled on merge — worth reading, because the branch forked 227 commits back and 77 files conflicted. Only five carried MaxAI content; the rest was drift from the older release line and took the tip's side, taking the diff from 113 files to 37 (then 93 as counted against the current base).
- executors/index.ts: the tip has since refactored the executor map to lazy dynamic imports, so MaxAI is registered in that shape rather than the branch's static import.
- imageRegistry.ts: kept only the maxai block. The branch still carried microsoft-designer-web, which #11754 retired.
- models/route.ts: the conflicting hunk was an unrelated Vertex/Anthropic URL change, not MaxAI — tip's side.
- volcengine agent-plan/coding-plan registries: git auto-merged both sides and produced a duplicated supportsVision key, which TypeScript rejects (TS1117). Removed.
One real integration break that only the combined state shows: the MaxAI entry declared no serviceKinds, which #11392 made required a few hours ago. Provider validation threw at load time and check:provider-consistency crashed outright. Declared ["llm"] — the image kinds derive from imageRegistry, per the convention in that PR's backfill.
Every count was measured rather than taken from the branch, and each would have been wrong: reserved prefixes are 402, not the 397 the branch computed from its stale 395 base; providers are 353, not 354. PROVIDER_REFERENCE.md regenerated, the count updated across README/AGENTS.md/llm.txt and its 42 mirrors, package.json and 6 SVGs — every changed line in those files is a digit substitution and nothing else, verified by masking digits and comparing the removed and added sets (90 lines, identical). The executor-map golden snapshot was regenerated: keyCount 133 -> 134.
The branch's file-size-baseline.json predates #12411's ratchet re-tightening, so it was discarded rather than merged — taking it would have silently undone that. The three files this PR grows (proxyFetch.ts +20 for the Windows/firefox_150 TLS profile, imageGeneration.ts +12, models/route.ts +48) were entered against the current baseline under one _rebaseline annotation; no other cap moves.
Verified: typecheck:core clean, check:provider-consistency OK (269 REGISTRY entries, 353 canonical providers), check:docs-counts exit 0, check-file-size OK, check:cycles OK, and 79/79 across the MaxAI suites plus 21/21 reserved-prefix and 2/2 executor-map-golden.
Thanks @arminanton — the provider work itself is thorough; it was the 227 commits of base that needed the attention.
The +30% loosening of 2026-08-10 (fbbef4eaaf) left this gate inert: combo.ts carried a 5,691-line cap against 4,023 real lines and chatCore.ts 7,895 against 5,946. Both god-files grew roughly 600 lines in two weeks without the gate ever firing.
Mechanical check:file-size --update against the tip. No source touched. combo.ts 5,691 -> 4,023, chatCore.ts 7,895 -> 5,946, frozen source entries 178 -> 135 (43 already fit the 1,200 cap), frozen test entries 49 -> 39. From here every 3.8.52 decomposition slice lowers the cap again.
Reconciled on merge, and worth recording because neither PR could see it alone: #11460 (flat-rate cost estimates) landed first and grew CostOverviewTab.tsx from 1,282 to 1,319 lines. This PR had frozen that entry at 1,283 — measured before #11460 existed — so the two together would have turned the tip red while each was green on its own. --update correctly refuses to raise a cap, so the entry was set to the real post-merge LOC with a _rebaseline_2026_09_02_11460_flat_rate_estimates annotation naming #11460 as the growth, following the own-growth precedent already in the file (_rebaseline_2026_08_20_10531_freebuff_provider).
The ratchet invariant is intact and was checked rather than assumed: across the whole baseline, 45 caps decrease and 0 increase; CostOverviewTab.tsx still falls 2,002 -> 1,319.
Verified: check-file-size OK (135 frozen source entries, 4,481 files checked; 39 frozen test entries, 5,338 checked), and prettier clean on the baseline.
* feat(ui): enable React Compiler (#67)
Enable reactCompiler: true in next.config.mjs (Next 16 + React 19.2.8).
This automates memoization at build time, removing manual useCallback/useMemo
debt (591 + 283 instances respectively) and preventing stale-closure bugs.
Test results (pre-existing failures unchanged):
vitest UI: 282/295 files pass (13 fail = missing router/ReactFlow mocks)
vitest: 1805/1857 tests pass (52 fail = same pre-existing mock issues)
node:test: api/services/db all pass (except platform-specific
serviceSupervisorSpawnError — Windows spawn("ls") issue)
No new failures introduced by the compiler transform.
Optional cleanup: remove now-redundant useCallback/useMemo in hot components.
* fix(build): add babel-plugin-react-compiler peer dependency (#67)
React Compiler (reactCompiler: true in next.config.mjs) requires
babel-plugin-react-compiler as an explicit peer dependency — Next.js
declares it as optional ("*") and does not auto-install it.
Installed babel-plugin-react-compiler@1.0.0 as a devDependency.
Resolves correctly from both the project root and the next package
context (Turbopack resolution path).
* fix(ci): allowlist babel-plugin-react-compiler for React Compiler
The React Compiler peer is a real npm package (facebook/react, MIT) required
by Next 16 `reactCompiler: true`. Adding it to the anti-slopsquat allowlist
unblocks check:deps and the 6A.8 unit-test gate.
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
* fix(ci): drop unused collectSSE helper that trips ESLint
The helper was leftover from #12151 and fails the absolute
lint:json --max-warnings 0 gate on every PR that includes it.
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
---------
Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: WebPerson <jonlwheat2-gif@users.noreply.github.com>
Closes the src/ side of the campaign (no eslint-disable, no new suppressions;
the 37 matching react-hooks/* entries are removed from
config/quality/eslint-suppressions.json — only the 5 CI-divergent entries in
tests/unit/ui remain, frozen by design, see #12144):
- set-state-in-effect (30×): fetch-on-mount and sync-setter effects wrapped in
the async-continuation pattern (await Promise.resolve() for pure-sync
bodies), preserving semantics exactly.
- refs/purity (ResilienceConnectionsClient): render now reads stopReason state
instead of stoppedRef; the receivedAt fallback Date.now() in JSX was dead
(every setData stamps receivedAt) and became 0.
- exhaustive-deps (ApiTab, SessionInfoCard, useLiveDashboard): clearResults
wrapped in useCallback; missing t dep added; channels array stabilized via
channelsKey + useMemo so connect deps are statically checkable.
- global-error: locale/messages load moved into one async continuation (also
renames the import binding to mod per @next/next/no-assign-module-variable).
Refs #12146
* docs(api): document every implemented route in openapi.yaml (276 -> 692 paths)
Follow-up nº 3 of the 2026-08-31 docs audit: 416 implemented routes had no
OpenAPI entry (gamification, radar, skills, webhooks, mcp, a2a, tunnels,
version-manager and plugins were absent entirely). Adds a minimal, honest
entry for each — real methods parsed from every route.ts's exports, a group
tag and a neutral path-derived summary; no invented semantics. Rich schemas
remain hand-curated in the existing entries.
Generated by scripts/ad-hoc/gen-openapi-missing-paths.mjs, which enumerates
routes with the same lib check:api-docs-refs uses — the spec now covers
692/692 real routes and the gate verifies every spec path has a real route.
* docs(api): security tiers on generated paths, regenerated API skills, size baseline
The first CI round caught three real contract gaps in the generated coverage:
- Generated operations on LOCAL_ONLY routes now carry x-loopback-only (and
x-always-protected for ALWAYS_PROTECTED_API_PATHS), resolved through the real
src/server/authz/routeGuard.ts at generation time. The
openapi-security-tiers guard now also accepts LOCAL_ONLY_API_PATTERNS —
param-shaped routes (/api/providers/{id}/login) are classified by regex in
the runtime and were invisible to the prefix-only check.
- The API agent skills are generated FROM the spec: 18 SKILL.md files
regenerated via generate-agent-skills --apply so the generator stays 46/46.
- src/app/docs/lib/openapi.generated.ts grew with the spec (171 -> 1347 lines,
emitted by gen-openapi-module): frozen in file-size-baseline.json with a
_rebaseline justification — shrink by slimming the spec, never by editing
the generated module.
Refresh the existing MIT-licensed miuuyy/codex-chatgpt-web vendor snapshot and its OmniRoute integration as one reviewable change.
Co-authored-by: backryun <backryun@daonlab.local>
45 violations across 27 files fixed at the source (no eslint-disable, no new
suppressions; the 45 matching react-hooks/* entries are removed from
config/quality/eslint-suppressions.json):
- set-state-in-effect (fetch-on-mount effects): async continuation wrapper.
- Prop/state sync effects (EditMemoryModal, radar/setup, EvalsTab): adjust
during render with prev tracking.
- purity/refs (ActivityFeedClient, ProviderQuotaWidget, ReasoningCacheTab):
Date.now() snapshots moved to state set from the fetch path; rendered refs
converted to state.
- immutability (useCodexResetCreditRedemption): ref-store writes extracted to
module-level helpers.
- exhaustive-deps (RequestLoggerV2, HomePageClient): COLUMN_SORT_MAP hoisted to
module scope; openDetail/closeDetail wrapped in useCallback and added to the
dependent hooks; versionInfo destructured to locals; baseUrl now reads
location.origin via useSyncExternalStore (hydration-safe, no effect).
Refs #12146
* chore(lint): batch 5 of #12146 — resolve the react-hooks compiler violations in combos, endpoint, provider-stats, api-manager and costs
40 violations across 14 files, all real refactors (no eslint-disable, no new
suppressions; the areas' react-hooks entries are deleted from the freeze):
- set-state-in-effect (30): fetch-on-mount effects moved behind an async
continuation (usePools, usePoolUsage, useApiKeyUsageLimits, Notion/Obsidian
source cards, A2A/MCP dashboards, ComboControlCenterClient, provider-stats,
combos modal loaders, ApiManager initial load); prop/state sync converted to
state adjustment during render with prev tracking (ApiKeyUsageLimitCard,
PoolWizard dimensions/reset/group snap, combos sortMethod, builder reset,
builder stage guard, single-provider default, stale intelligent selection);
localStorage reads became lazy useState initializers (combos usage guide).
- immutability / TDZ (8): effects that scheduled fetchers declared below them
moved after the declarations (EndpointPageClient, ApiManagerPageClient,
combos mount load); fetchData relocated below the per-key fetchers it calls.
- static-components (7): provider-stats SortIcon hoisted to module level.
- preserve-manual-memoization (2): ApiManager blockedModels dep destructured to
a local; provider-scope derivation memoized so downstream memos see a stable
dependency.
Validation: eslint (CI command with suppressions, --max-warnings 0) clean on the
14 files; dashboard typecheck within baseline; mutation gate no drift; area
tests 208/208 (node) + 29/29 (vitest).
Refs #12146
* chore(lint): batch 5 follow-up — hoist the render-adjustment predicates so the new-code complexity gates stay flat
The render adjustments added one cyclomatic branch to combos/page.tsx and one
cognitive point to PoolWizard (caught by the new-code gate on the committed
work); the compound conditions now live in pure module-level predicates.
* chore(lint): batch 5 follow-up 2 — PoolWizard render adjustments live in two small hooks
One consolidated hook tripped max-lines-per-function (>80) and the cognitive
budget; the dimensions and open/close adjustments now live in two focused hooks
with a shared WizardSetters type, and the group snap stays inline (one branch).
complexityNewCode=0, cognitiveComplexityNewCode=0.
* test(quota): repoint the two PoolWizard structural pins at the render-adjustment hook
quota-edit-opens-wizard anchored the pre-fill block on the old '} else if (editPool)'
effect literal and quota-pool-wizard-edit expected a bare 'if (editPool)' that only
existed there; both now anchor on the batch-5 structure (submit still branches via
if (!editPool)).
Resolves all 28 react-hooks/* compiler violations (24 set-state-in-effect,
4 refs) across the 18 dashboard/providers files of batch 2 and removes their
suppression entries — no eslint-disable, no new suppressions.
Techniques per file:
- Fetch-on-mount loaders (CustomModelsSection, ProviderCcAliasSection,
ProviderInterceptionSection, ProviderParamFilterSection, page.tsx,
useProviderConnections, useProviderSettings, CliproxyAccountHealthCard,
DarioAccountPanel, NinerouterModelList): network/parse/error concerns
extracted to module-level helpers returning error-as-value; the async glue is
defined INSIDE each effect with every setState after the await. Loaders that
handlers still need (refresh/retry buttons, exposed hook API) remain as
callbacks; spinner flags moved into the button handlers.
- Loading flags for provider-keyed sections derived from a loadedProviderId
marker instead of synchronous setLoading(true) resets.
- Modal init/reset effects (EditConnectionModal, EditCompatibleNodeModal,
AddCompatibleProviderModal, VolcengineConnectModal state reset,
useProviderUrlFilters hydration, page.tsx display-mode fallback,
useProviderSettings per-provider flag reset): converted to render-phase
adjustments guarded by the previously-seen prop/marker (react.dev "adjusting
state when a prop changes").
- VolcengineConnectModal: phone prefill via localStorage lazy initializer;
server-side session cancel + poll stop moved to the cleanup of an
open-scoped effect reading a session ref mirror.
- ModelCompatPopover refs: render-time ref mirrors removed — headerRowsRef is
maintained by an applyHeaderRows writer used by all handlers, paramTargetRef
is mirrored in an effect, and blockText/allowText mirrors were already kept
in sync by their single writer (applyParamFields).
- ModelCompatPopover state: header-row loading and value-visibility resets
moved from [open, protocol] effects into the open/protocol/outside-click
gesture handlers; the closed-popover rect reset was dropped (render is gated
on open and the rect is recomputed pre-paint on reopen).
- useRiskAcknowledged: localStorage mirrored via useSyncExternalStore with a
module-level listener set notified by acknowledgeProviderRisk.
- useProviderModels: loading for the empty-providerId case derived at the
return site instead of a synchronous setLoading in the effect.
Validation: scoped eslint with the suppressions file passes with 0 problems;
check-dashboard-typecheck.mjs OK; node --test batch (14 files) and vitest
batch (9 files, 47 tests) green.
Refs #12146
* chore(lint): batch 3 of #12146 — resolve the react-hooks compiler violations in dashboard/settings
Resolves the 25 react-hooks/* React Compiler violations frozen in
config/quality/eslint-suppressions.json for the dashboard/settings area
(24 set-state-in-effect, 1 immutability), plus the adjacent
react-hooks/exhaustive-deps in ProviderAccountRoutingCard, and removes
their suppression entries. No eslint-disable added anywhere; one
pre-existing eslint-disable-line (AccessTokensTab) removed.
Techniques used:
- ResilienceTab (8×): the "sync draft state from prop via useEffect"
cards now use the documented adjust-state-during-render pattern
(prevValue state + conditional setState in render) instead of an
effect.
- PricingTab visibleCount reset: same render-adjustment pattern keyed
on the filters tuple, replacing the reset effect.
- Fetch-on-mount loaders only used by the effect (IPFilterSection,
ModelCapabilityOverridesTab, PayloadRulesTab*, RoutingStrategyCard):
loader inlined into the effect as an async IIFE with a cancelled
flag; every setState now happens after the first await.
- Loaders reused by handlers/intervals (AccessTokensTab, AuthzSection,
FallbackChainsEditor, MitmProxyTab, ModelsDevSyncTab, OneproxyTab,
PayloadRulesTab, PoliciesPanel, PricingTab,
ProviderAccountRoutingCard, SystemStorageTab, GlobalConfigTab,
SubscriptionTab): split into a module-level pure fetcher + a
useCallback applier; the effect awaits the fetcher and applies after
the await (cancellation-guarded), while handlers keep the original
named loader (sync setState is fine there) built from the same
fetcher/applier — no logic duplication, identical error-message and
loading semantics.
- OneproxyTab keeps the spinner-on-filter-change behavior via the same
render-adjustment pattern (filtersKey → setLoading(true)).
- AccessTokensTab: the L() fallback helper is now memoized with
useCallback([t]), which also let the old
eslint-disable-line react-hooks/exhaustive-deps be removed.
- ProviderAccountRoutingCard: save's dependency array now includes
load (the frozen exhaustive-deps violation).
Suppressions: all react-hooks/* entries for the 17 batch files removed
(19 rule entries, 26 violation counts). Entries for other rules/files
untouched.
Refs #12146
* chore(lint): batch 3 follow-up — extract useOneproxyData so the cyclomatic gate stays flat
The first pass grew OneproxyTab past the complexity threshold (caught by the
new-code gate on the PR); the data-loading state now lives in a dedicated
useOneproxyData hook. Also registers search-432-plan-limit-cooldown in
stryker tap.testFiles (base drift the gate flagged on every batch).
* chore(lint): batch 1 of #12146 — resolve the react-hooks compiler violations in dashboard/cli-code
Real refactors, no suppressions — the 42 frozen react-hooks/* entries for the
12 dashboard/cli-code files (plus Antigravity's exhaustive-deps one) are
removed from config/quality/eslint-suppressions.json and the files now lint
clean under the React Compiler rules.
Techniques, per pattern:
- set-state-in-effect ("default API key" effects — Antigravity, Claude, Cline,
Codex, Droid, GrokBuild, Kilo, OpenClaw): the setState-in-effect that copied
apiKeys[0].id into the selection state is deleted; an `effective*` value is
derived during render (`selected || apiKeys[0]?.id`) and used by the select
and the submit handlers. Behavior identical, one less render pass.
- immutability ("accessed before declared") + set-state-in-effect on the
expand-time loaders (all tool cards): the fetchers (checkXStatus,
fetchModelAliases, fetchBackups, fetchProfiles, loadSavedMappings) are
hoisted above the effect as useCallback with correct deps, listed in the
effect deps, and invoked through an async continuation
(`void (async () => { await Promise.all([...]) })()`) so no setState runs
synchronously in the effect body.
- set-state-in-effect ("init form from fetched status" effects — Claude,
Cline, Codex, Droid, OpenClaw): the status-parsing effects are deleted and
their logic now runs inside checkXStatus right after the fetch resolves
(setState after await), keeping the same one-time ref guards. Codex's config
parser became syncFormFromStatus(), called on both success and error paths.
- HermesAgentToolCard: Date.now() in render (purity) is snapshotted once via a
lazy useState initializer; the batchStatus seeding effect is replaced by a
derived `displayRoles` (useMemo over batchStatus with currentRoles taking
precedence); the collapse-reset effect moved into the header toggle handler.
- ClaudeClassifierCompatToggle / CliProfileAutoSyncToggles / Cliproxyapi /
GrokBuild: mount/expand loads wrapped in the same async continuation.
- DroidToolCard's isOmniRouteEntry helper hoisted to module scope (pure).
Validation: eslint with suppressions --max-warnings 0 on the 12 files (clean),
scripts/check/check-dashboard-typecheck.mjs (OK, within frozen baseline),
vitest UI suites for the touched cards (15 files / 57 tests green, plus the 3
quarantined #8618 files run explicitly: 27 tests green), and the node-native
cli-code tests (61 tests green).
Refs #12146
* chore(lint): batch 1 follow-up — hoist the settings-init helpers so the cognitive gate stays flat
The first pass folded the one-time form init into the status fetchers, which pushed
sonarjs/cognitive-complexity to 1 in Claude/Cline/OpenClaw tool cards (caught by the
new-code gate on the PR). The init logic now lives in module-level helpers
(initXFormFromSettings + defaultKeyId); complexityNewCode=-1, cognitiveComplexityNewCode=0.
* chore(lint): batch 4 of #12146 — resolve the react-hooks compiler violations in shared/components
Real refactors (no suppressions, no eslint-disable) for the 21 react-hooks/*
violations across the 11 src/shared/components files of this batch:
- set-state-in-effect (prop/state mirror or modal open/close reset):
replaced with guarded render-time adjustments (react.dev "You Might Not
Need an Effect" prev-tracking pattern) — KiroAuthModal,
ModelSelectModal, ProxyConfigModal, OAuthModal (provider-change, close
and open resets; ref invalidation split into ref-only effects),
RequestLoggerDetail.sections (liveDetail mirror),
ComboCompressionModeSelect (initialCompressionMode mirror).
- set-state-in-effect (fetch+set effects calling component-scope
functions): moved the async loader inside the effect (ModelSelectModal
fetchCombos/fetchProviderNodes/fetchCustomModels, PricingModal
loadPricing, useProviderDailyUsage fetchRows — now with a cancelled
guard) or wrapped the call in an effect-local async runner
(ReasoningRoutingRules load, UsageStats fetchStats, OAuthModal
startOAuthFlow) with every setState on the async path.
- OAuthModal device-code countdown: deviceCodeSecondsRemaining state
deleted and derived from deviceCodeExpiresAt plus a `now` tick state
updated by the interval (re-anchored when polling starts).
- Sidebar localStorage hydration: reads moved into useSyncExternalStore
snapshots (server snapshot null) applied via render-time adjustment;
skipInitialActiveExpansion ref converted to state; the active-section
expansion effect became a render-time adjustment keyed on the old
effect deps; persistence consolidated into one saveToStorage effect
(removes the saves that ran inside setState updaters and drops a
pre-existing eslint-disable for exhaustive-deps).
- immutability (use-before-declare): PricingModal loadPricing inlined
into its effect; ProxyConfigModal resetFields hoisted above the load
effect as a dependency-free useCallback.
- exhaustive-deps (ProxyConfigModal): effect now depends on the stable
resetFields and on hoisted translated strings (socks5HiddenError,
levelGlobalLabel) instead of the `t` identity.
- preserve-manual-memoization (UsageStats sortedAccounts): optional
chains destructured into locals so the memo deps match the usage.
config/quality/eslint-suppressions.json: removed every react-hooks/*
entry for the 11 files (other-rule entries preserved).
Validation: eslint gate (--suppressions-location, --max-warnings 0) green
on all 11 files; typecheck:core clean; node unit sweep 373/373; vitest
sweep 547/550 with the 3 fails being 5s-timeout flakes under parallel
load (all pass isolated 8/8, one in an untouched file).
Refs #12146
* test(mutation): register search-432-plan-limit-cooldown in tap.testFiles
The test (merged with the DuckDuckGo cooldown fix) covers accountFallback.ts and
auth.ts but was not listed, so check:mutation-test-coverage --strict reds any PR
whose merge ref includes it. Base also merged in.
Typed CallLogRow / PayloadEnvelope views over the raw rows and payload envelopes;
(assert as any).equal back to assert.equal. Suppression entry for the file removed —
the gate now watches it for real. eslint (CI command) clean, suite 15/15.
Refs #12146