fix(release): drain the twelve reds every PR against release/v3.8.51 was born with (#11940)

* fix(release): drain the twelve reds every PR against release/v3.8.51 was born with

Measured on the cycle tip: fifteen unit files were red on every PR. Two came
from the v3.8.50 sync-back (fixed in #11929); the other thirteen predate it and
are the branch's own drift. This sweep clears all of them but the ESLint debt
(#11924), each with the smallest change that keeps the guard honest:

- .env.example + ENVIRONMENT.md: NEXT_PUBLIC_SW_BUILD_ID / OMNIROUTE_SW_BUILD_ID /
  SOURCE_VERSION (#11779 service-worker cache busting) documented — the env/docs
  contract gate was failing on every PR.
- stryker.conf.json: the six tests the mutation gate found covering mutated modules
  (four retirement runtime-block suites, combo connection-aware expansion, tunnel
  error sanitization) registered in tap.testFiles.
- dependency-allowlist: eslint-plugin-react-hooks 7.0.1 approved; its findings are
  tracked in #11924.
- i18n: the six combo.sort.* strings (d5dfcfff58) translated for vi (strict parity)
  and pt-BR.
- docs/providers/CHATGPT_WEB.md: the retirement test is migration-168, not 163.
- g4f gateways: authHint now says member key, which the discontinued-providers
  guard asserts.
- tests realigned to the catalog the branch actually ships: qwen-web (#11713) and
  chatgpt-web (#11720) are retired, so web-session-contract and
  token-health-check-webcookie use perplexity-web, grok-web and chatgpt-web-codex.
- db-core-init: the two minimal legacy fixtures gained the columns migrations 164-168
  UPDATE (error_code, last_error*, test_status) — they exist on every real legacy DB
  (base CREATE TABLE); the fixtures simply never declared them.
- no-js-extension guard: a .js specifier whose target is a genuine JavaScript file
  (open-sse/lib/deepseek-pow-hash.js, shared with a worker) is not the #10674
  defect; the test now skips targets that exist as .js.

All twelve files pass locally; docs-sync, docs-counts, env-doc-sync, the tap
drift gate and the fabricated-docs gates are green on the tree.

* test(release): move the deferred-finish translator test into a collected path

tests/unit/translator/ is not one of the unit collectors (package.json test:unit,
merge-train.sh, build-test-impact-map, check-test-discovery), so the suite that
dd35750e5f added there never ran — check:test-discovery flagged it as a new orphan
on every PR. Relocated next to its sibling openai-to-claude-trailing-usage-11817
under tests/unit/, where the root glob collects it (5/5 pass).

* fix(dashboard): type the four sort-method sites #11812 left red on the dashboard typecheck ratchet

d5dfcfff58 added the combo model sort and raised combos/page.tsx from 23 to 27
scoped TypeScript errors (TS2339 +1, TS2345 +2, TS2322 +1), which fails
check:dashboard-typecheck on every PR against release/v3.8.51:

- initialSortMethod: sanitizeComboRuntimeConfig() is untyped, so config.modelSort is
  unknown; narrow it before reading .method (normalizeSortMethod takes unknown anyway).
- handleAddModels: the batch path passes ComboBuilderDraftModelStep[] to the ComboStep[]
  sort helpers without the cast handleSortChange already uses; mirror it.
- ComboSortSelect expects a translate-with-fallback (k, f) => string, but received
  next-intl's Translator whose second argument is a values object. Pass the page's
  getI18nOrFallback adapter instead of the raw translator — that is also what makes
  the `has()` check and the fallback text actually work at runtime.

Baseline untouched (no widening). Scoped tsc: 0 new/regressed errors.
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-08-28 18:18:33 -03:00
committed by GitHub
parent 8dfdd95187
commit a94fe23e89
15 changed files with 131 additions and 40 deletions

View File

@@ -3023,3 +3023,11 @@ QUOTA_STORE_DRIVER=sqlite
# corpus-aware retrieval. Higher values keep more index entries hot.
# Used by: src/lib/localCorpus/configured.ts
# OMNIROUTE_CORPUS_CACHE_SIZE=5
# Service-worker cache-busting id for the PWA shell (#11779). NEXT_PUBLIC_SW_BUILD_ID is
# derived at build time from OMNIROUTE_SW_BUILD_ID, then SOURCE_VERSION (set by some PaaS
# builders), then the git SHA — override only when the build cannot see git. Used by:
# next.config.mjs, scripts/build/assembleStandalone.mjs, src/shared/components/PwaRegister.tsx.
#OMNIROUTE_SW_BUILD_ID=2026-08-28T12-00-00
#SOURCE_VERSION=abcdef0123456789
#NEXT_PUBLIC_SW_BUILD_ID=abcdef0123456789

View File

@@ -0,0 +1,7 @@
- Drained the reds every PR against `release/v3.8.51` was born with: documented the
three service-worker build-id variables, registered the six retirement/tunnel tests
with the mutation gate, approved `eslint-plugin-react-hooks` in the dependency
allowlist, added the six `combo.sort.*` strings to `vi` and `pt-BR`, pointed the
ChatGPT Web doc at the real migration-168 test, worded the g4f hint around the
member key, and realigned four tests to the retired-provider catalog and the
legacy-schema fixtures the retirement migrations touch.

View File

@@ -2,7 +2,8 @@
"_comment": "Allowlist anti-slopsquatting (check-deps.mjs). Toda dep nova exige adicao EXPLICITA aqui apos verificar que e legitima.",
"_justifications": {
"@testing-library/dom": "Peer dep obrigatoria de @testing-library/react v16 (adicionada no PR #11224); Refs #9985.",
"@testing-library/user-event": "Utilitario oficial do ecossistema testing-library para testes de UI (adicionada no PR #11224); Refs #9985."
"@testing-library/user-event": "Utilitario oficial do ecossistema testing-library para testes de UI (adicionada no PR #11224); Refs #9985.",
"eslint-plugin-react-hooks": "React Hooks lint rules (set-state-in-effect, immutability, refs, purity) pinned at 7.0.1 by the release/v3.8.51 cycle; the 224 findings it raised are tracked in #11924. Refs #11924."
},
"allowed": [
"@atjsh/llmlingua-2",
@@ -48,8 +49,8 @@
"clsx",
"commander",
"concurrently",
"cross-env",
"cron-parser",
"cross-env",
"csv-stringify",
"ctrf",
"dompurify",
@@ -60,6 +61,7 @@
"esbuild",
"eslint",
"eslint-config-next",
"eslint-plugin-react-hooks",
"eslint-plugin-sonarjs",
"express",
"fast-check",
@@ -102,9 +104,9 @@
"node-loader",
"node-machine-id",
"omniglyph",
"onnxruntime-node",
"open",
"opencode-ai",
"onnxruntime-node",
"ora",
"parse5",
"pino",
@@ -131,10 +133,10 @@
"tailwind-merge",
"tailwindcss",
"tls-client-node",
"turndown",
"turndown-plugin-gfm",
"tsup",
"tsx",
"turndown",
"turndown-plugin-gfm",
"type-coverage",
"typescript",
"typescript-eslint",

View File

@@ -130,4 +130,4 @@ Retirement regression guards live in:
- `tests/unit/chatgpt-web-runtime-block.test.ts`
- `tests/unit/chatgpt-web-image-handler-retirement.test.ts`
- `tests/unit/chatgpt-web-source-retirement.test.ts`
- `tests/unit/migration-163-retire-chatgpt-web.test.ts`
- `tests/unit/migration-168-retire-chatgpt-web.test.ts`

View File

@@ -59,6 +59,9 @@ These **must** be set before the first run. Without them, the application will e
| `API_KEY_SECRET` | **Yes** | _(none)_ | `src/lib/db/apiKeys.ts` | AES encryption key for API key values at rest in SQLite. Generate with `openssl rand -hex 32`. |
| `INITIAL_PASSWORD` | **Yes** | `CHANGEME` | Bootstrap script | Sets the initial admin dashboard password (matches `.env.example` default — kept obviously insecure to force a change). **Change before first use.** After login, change via Dashboard → Settings → Security. |
| `OMNIROUTE_WS_BRIDGE_SECRET` | **Yes** (production) | _(unset)_ | `src/app/api/internal/codex-responses-ws/route.ts` | Shared secret for the internal Codex Responses WebSocket bridge. Authenticates bridge requests between the Electron/browser WS relay and OmniRoute. ⚠️ **REQUIRED in production — when unset, all WS bridge requests are rejected.** Generate with `openssl rand -base64 32`. |
| `OMNIROUTE_SW_BUILD_ID` | No | _(git SHA)_ | `next.config.mjs`, `scripts/build/assembleStandalone.mjs` | Explicit service-worker cache-busting id for the PWA shell (#11779); first in the resolution chain. |
| `SOURCE_VERSION` | No | _(unset)_ | `next.config.mjs`, `scripts/build/assembleStandalone.mjs` | Second in the chain — set by PaaS builders (e.g. Heroku-style) as the deployed commit. |
| `NEXT_PUBLIC_SW_BUILD_ID` | No | _(derived)_ | `src/shared/components/PwaRegister.tsx` | Build-time public value the client uses to register `/sw.js?v=…`; derived from the two above, then the git SHA. |
| `OMNIROUTE_PEER_STAMP_TOKEN` | No (auto) | _(auto per boot)_ | `src/server/authz/policies/management.ts` | Per-process secret proving the trusted peer-IP stamp came from OmniRoute's own HTTP server (`scripts/dev/peer-stamp.mjs`). The authz middleware trusts request locality (loopback/LAN gating of LOCAL_ONLY routes) only when the stamp carries this token. Auto-generated each boot — leave unset; only pin it for multi-process setups that must share the stamp. |
### Generation Commands

View File

@@ -2037,7 +2037,9 @@ function ComboFormModal({ isOpen, combo, onClose, onSave, activeProviders, combo
const [showAdvanced, setShowAdvanced] = useState(false);
const [config, setConfig] = useState(sanitizeComboRuntimeConfig(combo?.config));
// Validate persisted enum; ensure reset on combo change not just first mount.
const initialSortMethod = normalizeSortMethod(config.modelSort?.method);
const initialSortMethod = normalizeSortMethod(
(config.modelSort as { method?: unknown } | undefined)?.method
);
const [sortMethod, setSortMethod] = useState<SortMethod>(initialSortMethod);
useEffect(() => {
// Sync point: when the combo identity changes, re-derive sort method.
@@ -2733,13 +2735,15 @@ function ComboFormModal({ isOpen, combo, onClose, onSave, activeProviders, combo
const rankings = await fetchProviderRankings();
// Functional note: `next` is the post-batch snapshot. Concurrent single-add
// racing this batch is low-probability single-user; last write wins.
const sorted = await sortComboStepsByScore(next, rankings);
setModels(sorted);
const sorted = await sortComboStepsByScore(next as ComboStep[], rankings);
setModels(sorted as typeof next);
} catch {
setModels(next);
}
} else {
setModels(sortComboStepsSync(next, currentMethod as "provider" | "name"));
setModels(
sortComboStepsSync(next as ComboStep[], currentMethod as "provider" | "name") as typeof next
);
}
setBuilderError("");
};
@@ -3642,7 +3646,11 @@ function ComboFormModal({ isOpen, combo, onClose, onSave, activeProviders, combo
</div>
<div className="flex items-center justify-between gap-2 mb-2">
<ComboSortSelect value={sortMethod} onChange={handleSortChange} t={t} />
<ComboSortSelect
value={sortMethod}
onChange={handleSortChange}
t={(k, f) => getI18nOrFallback(t, k, f)}
/>
</div>
{models.length === 0 ? (

View File

@@ -7679,7 +7679,8 @@
"backupCleanupSuccess": "Excluído(s) {backups} conjunto(s) de backup e {files} arquivo(s).",
"backupCleanupFailed": "Falha ao limpar backups do banco de dados",
"purgeQuotaSnapshotsSuccess": "{count} snapshots de cota removidos",
"purgeQuotaSnapshotsFailed": "Falha ao remover snapshots de cota", "purgeCallLogsSuccess": "{count} logs de chamadas removidos",
"purgeQuotaSnapshotsFailed": "Falha ao remover snapshots de cota",
"purgeCallLogsSuccess": "{count} logs de chamadas removidos",
"purgeCallLogsFailed": "Falha ao remover logs de chamadas",
"purgeDetailedLogsSuccess": "{count} logs detalhados removidos",
"purgeDetailedLogsFailed": "Falha ao remover logs detalhados",
@@ -7689,7 +7690,8 @@
"invalidJsonFileType": "Tipo de arquivo inválido. Apenas arquivos .json são permitidos.",
"legacyJsonImportSuccess": "JSON legado importado com sucesso!",
"jsonImportFailed": "Falha ao importar JSON",
"jsonImportError": "Erro durante a importação de JSON", "storagePurgeData": "Limpar dados",
"jsonImportError": "Erro durante a importação de JSON",
"storagePurgeData": "Limpar dados",
"storagePurgeDataDesc": "Excluir imediatamente todos os registros sem aplicar verificações de retenção. Use com cautela.",
"storageRetentionCleanup": "Configurações de Retenção",
"storageRetentionCleanupDesc": "Configure a retenção de registros operacionais e a limpeza de backup do banco de dados.",
@@ -13937,5 +13939,17 @@
"cta": "Obter uma chave de API",
"partnerLinkNote": "Link de parceiro",
"dismissAriaLabel": "Dispensar"
},
"combo": {
"sort": {
"label": "Ordenar por",
"method": {
"manual": "Manual",
"provider": "Provedor",
"score": "Pontuação (modelos gratuitos)",
"name": "Nome"
},
"scoreHint": "A ordenação por pontuação vale só para provedores gratuitos; os demais ficam onde estão."
}
}
}

View File

@@ -13939,5 +13939,17 @@
"cta": "Lấy khóa API",
"partnerLinkNote": "Liên kết đối tác",
"dismissAriaLabel": "Đóng"
},
"combo": {
"sort": {
"label": "Sắp xếp theo",
"method": {
"manual": "Thủ công",
"provider": "Nhà cung cấp",
"score": "Điểm (mô hình miễn phí)",
"name": "Tên"
},
"scoreHint": "Xếp hạng theo điểm chỉ áp dụng cho nhà cung cấp miễn phí; các nhà cung cấp khác giữ nguyên vị trí."
}
}
}

View File

@@ -695,7 +695,7 @@ export const APIKEY_PROVIDERS_GATEWAYS = {
"Anonymous access to Groq requires proof-of-work cake credits from g4f.dev/chat; alternatively, use a g4f.dev member API key. Limits vary.",
passthroughModels: true,
authHint:
"Bake anonymous cake credits at g4f.dev/chat, or create a member API key at g4f.dev/members.html.",
"Bake anonymous cake credits at g4f.dev/chat, or use a g4f.dev member key (create one at g4f.dev/members.html).",
notice: {
text: "Remote third-party gateway: prompts and request metadata leave OmniRoute and are handled by g4f.space. Its Terms and Privacy links were unavailable when last verified on 2026-08-27.",
apiKeyUrl: "https://g4f.dev/members.html",
@@ -714,7 +714,7 @@ export const APIKEY_PROVIDERS_GATEWAYS = {
"Anonymous access to Gemini requires proof-of-work cake credits from g4f.dev/chat; alternatively, use a g4f.dev member API key. Limits vary.",
passthroughModels: true,
authHint:
"Bake anonymous cake credits at g4f.dev/chat, or create a member API key at g4f.dev/members.html.",
"Bake anonymous cake credits at g4f.dev/chat, or use a g4f.dev member key (create one at g4f.dev/members.html).",
notice: {
text: "Remote third-party gateway: prompts and request metadata leave OmniRoute and are handled by g4f.space. Its Terms and Privacy links were unavailable when last verified on 2026-08-27.",
apiKeyUrl: "https://g4f.dev/members.html",
@@ -733,7 +733,7 @@ export const APIKEY_PROVIDERS_GATEWAYS = {
"Anonymous access to Pollinations requires proof-of-work cake credits from g4f.dev/chat; alternatively, use a g4f.dev member API key. Limits vary.",
passthroughModels: true,
authHint:
"Bake anonymous cake credits at g4f.dev/chat, or create a member API key at g4f.dev/members.html.",
"Bake anonymous cake credits at g4f.dev/chat, or use a g4f.dev member key (create one at g4f.dev/members.html).",
notice: {
text: "Remote third-party gateway: prompts and request metadata leave OmniRoute and are handled by g4f.space. Its Terms and Privacy links were unavailable when last verified on 2026-08-27.",
apiKeyUrl: "https://g4f.dev/members.html",
@@ -752,7 +752,7 @@ export const APIKEY_PROVIDERS_GATEWAYS = {
"Anonymous access to hosted Ollama requires proof-of-work cake credits from g4f.dev/chat; alternatively, use a g4f.dev member API key. Limits vary.",
passthroughModels: true,
authHint:
"Bake anonymous cake credits at g4f.dev/chat, or create a member API key at g4f.dev/members.html.",
"Bake anonymous cake credits at g4f.dev/chat, or use a g4f.dev member key (create one at g4f.dev/members.html).",
notice: {
text: "Remote third-party gateway: prompts and request metadata leave OmniRoute and are handled by g4f.space. Its Terms and Privacy links were unavailable when last verified on 2026-08-27.",
apiKeyUrl: "https://g4f.dev/members.html",
@@ -771,7 +771,7 @@ export const APIKEY_PROVIDERS_GATEWAYS = {
"Anonymous access to NVIDIA NIM requires proof-of-work cake credits from g4f.dev/chat; alternatively, use a g4f.dev member API key. Limits vary.",
passthroughModels: true,
authHint:
"Bake anonymous cake credits at g4f.dev/chat, or create a member API key at g4f.dev/members.html.",
"Bake anonymous cake credits at g4f.dev/chat, or use a g4f.dev member key (create one at g4f.dev/members.html).",
notice: {
text: "Remote third-party gateway: prompts and request metadata leave OmniRoute and are handled by g4f.space. Its Terms and Privacy links were unavailable when last verified on 2026-08-27.",
apiKeyUrl: "https://g4f.dev/members.html",

View File

@@ -1,14 +1,14 @@
{
"$schema": "https://stryker-mutator.io/schemas/stryker-schema.json",
"_comment": [
"Mutation testing for the ~8 critical modules (Task 11 \u2014 Fase 7).",
"NIGHTLY ONLY \u2014 DO NOT run on every PR. Mutation testing is expensive:",
"Mutation testing for the ~8 critical modules (Task 11 Fase 7).",
"NIGHTLY ONLY DO NOT run on every PR. Mutation testing is expensive:",
" - Each mutant requires a full test suite execution.",
" - The 8 modules produce ~200\u2013500 mutants; est. 30\u201390 min per run.",
" - The 8 modules produce ~200500 mutants; est. 3090 min per run.",
" - Wired to the nightly CI workflow (.github/workflows/nightly-mutation.yml),",
" NOT to the 'lint' / 'quality-gate' PR jobs.",
"",
"TEST RUNNER \u2014 @stryker-mutator/tap-runner (NOT vitest):",
"TEST RUNNER @stryker-mutator/tap-runner (NOT vitest):",
" The 8 critical modules are covered by node:test files in tests/unit/",
" (run via `node --import tsx --test`), NOT by vitest. The vitest config",
" only includes a small set of .test.tsx + open-sse/**/__tests__ files, so",
@@ -22,24 +22,26 @@
" npm install --save-dev @stryker-mutator/core @stryker-mutator/tap-runner",
"",
"Run manually:",
" npm run test:mutation # full run (slow \u2014 nightly budget)",
" npm run test:mutation # full run (slow nightly budget)",
" npx stryker run --dryRunOnly # validate the baseline only (no mutants)",
" (single-module probe: temporarily narrow `mutate` + `tap.testFiles` in this file)",
"",
"VALIDATED 2026-06-15: `npx stryker run --dryRunOnly` exits 0 \u2014 all 129 covering",
"VALIDATED 2026-06-15: `npx stryker run --dryRunOnly` exits 0 all 129 covering",
"test files run green in the Stryker sandbox and the perTest coverage map builds for",
"all 8 instrumented modules (15k+ mutants). The baseline dry-run takes ~20 min with",
"concurrency=1; the full mutation phase runs on top (advisory, capped by the workflow",
"timeout). So the nightly produces REAL mutation scores for the 8 modules.",
"",
"Mutation score per module \u2192 quality-baseline.json key 'mutationScore.<module>'",
"Direction: up (score can only improve; ratchet blocks drops \u2014 wired in a later INT phase)."
"Mutation score per module quality-baseline.json key 'mutationScore.<module>'",
"Direction: up (score can only improve; ratchet blocks drops wired in a later INT phase)."
],
"packageManager": "npm",
"incremental": true,
"incrementalFile": "reports/mutation/stryker-incremental.json",
"testRunner": "tap",
"plugins": ["@stryker-mutator/tap-runner"],
"plugins": [
"@stryker-mutator/tap-runner"
],
"tap": {
"testFiles": [
"tests/unit/10085-compatible-generic-vs-uuid-credential.test.ts",
@@ -389,7 +391,13 @@
"tests/unit/vertex-passthrough-model-lockout.test.ts",
"tests/unit/video-bridge-drilldown-route.test.ts",
"tests/unit/video-bridge-route-security.test.ts",
"tests/unit/xai-agent-tools-passthrough.test.ts"
"tests/unit/xai-agent-tools-passthrough.test.ts",
"tests/unit/combo/connection-aware-expansion.test.ts",
"tests/unit/chatgpt-web-runtime-block.test.ts",
"tests/unit/felo-web-runtime-block.test.ts",
"tests/unit/microsoft-designer-web-runtime-block.test.ts",
"tests/unit/qwen-web-runtime-block.test.ts",
"tests/unit/tunnel-routes-error-sanitization.test.ts"
],
"nodeArgs": [
"--import",
@@ -405,7 +413,7 @@
]
},
"_mutate_godfiles_excluded_comment": [
"2026-06-18 (Onda 2 budget): chatCore.ts + combo.ts \u2014 the two god-files \u2014 were REMOVED",
"2026-06-18 (Onda 2 budget): chatCore.ts + combo.ts the two god-files were REMOVED",
"from `mutate`. They dominated ~2/3 of the ~15k mutants; the full 8-module run TIMED OUT",
"at the 180min nightly cap (run 27705123780: 16:47:33 -> killed 19:47:48 = exactly 180min;",
"the prior 120min scheduled run also timed out). #4078 made concurrency safe but the",
@@ -425,23 +433,23 @@
"comboContextCache/idempotency/passthroughHelpers/responseHeaders/sanitization/upstreamTimeouts).",
"A follow-up then added DEDICATED unit tests for 6 more leaves (tests/unit/chatcore-headers,",
"-log-truncation, -memory-extraction, -non-streaming-sse, -passthrough-tool-names,",
"-executor-helpers \u2014 wired into tap.testFiles above) and added those leaves as batch h",
"-executor-helpers wired into tap.testFiles above) and added those leaves as batch h",
"(headers/logTruncation/memoryExtraction/nonStreamingSse/passthroughToolNames/executorHelpers).",
"A later follow-up added dedicated tests (NO mock.module \u2014 unavailable under the tap-runner; used",
"A later follow-up added dedicated tests (NO mock.module unavailable under the tap-runner; used",
"fetch-override + crafted inputs + temp-DATA_DIR) for telemetryHelpers (both fns, all branches) and",
"memorySkillsInjection (getSkillsProviderForFormat fully + injectMemoryAndSkills guards/empty-DB",
"path) and added them as batch i.",
"",
"The FINAL chatCore leaf, semanticCache.ts, was added to batch i once its cache-HIT block had a",
"fixture: chatcore-semantic-cache now SEEDS the real cache via setCachedResponse (the in-memory",
"store getCachedResponse checks first \u2014 no mock.module needed) under the exact signature",
"store getCachedResponse checks first no mock.module needed) under the exact signature",
"checkSemanticCache rebuilds, so the HIT branch runs end-to-end (status 200 / 'semantic' / 'HIT' /",
"the stream + content-type ternaries / the cost fallback / the side-effect calls all get killed).",
"ALL 15 chatCore leaves are now mutated.",
"",
"STILL EXCLUDED (follow-ups, NOT in `mutate` yet):",
" - combo.ts + chatCore.ts barrels: their handleComboChat/handleChatCore CORES were not",
" split (out of scope \u2014 Fase 3 ChatCoreContext refactor). The barrels are now thin-ish",
" split (out of scope Fase 3 ChatCoreContext refactor). The barrels are now thin-ish",
" but still large; keep excluded until the cores are split.",
"See project memory: Quality Gate v2 / Fase 9 (project-combo-split)."
],
@@ -498,7 +506,11 @@
".worktrees",
".stryker-tmp"
],
"reporters": ["progress", "html", "json"],
"reporters": [
"progress",
"html",
"json"
],
"htmlReporter": {
"fileName": "reports/mutation/mutation.html"
},
@@ -525,11 +537,11 @@
"would break the required all-green baseline dry-run (e.g. body-timeout-integration,",
"heap-pressure, sse-heartbeat-integration, *-stream-readiness, chatcore-memory-pressure).",
"It is enumerated (not a broad glob) so the Stryker dry-run stays tractable for the",
"nightly budget \u2014 a glob over the full ~1300-file unit suite would make the per-test",
"nightly budget a glob over the full ~1300-file unit suite would make the per-test",
"dry-run take hours. coverageAnalysis:perTest then narrows which files run per mutant.",
"Regenerate the base union after adding/renaming covering tests, then re-prune flaky ones:",
" grep -rlE \"circuitBreaker|publicCreds|accountFallback|routeGuard|services/auth|chatCore|services/combo|utils/error|public-client|account-fallback|route-guard|circuit-breaker\" tests/unit --include=\"*.test.ts\" | sort -u"
],
"dryRunTimeoutMinutes": 30,
"_concurrency_comment": "concurrency=4 (was 1): the covering node:test files used to share SQLite/module state via the default DATA_DIR (~/.omniroute), so running them concurrently in the Stryker sandbox caused cross-file races that failed the all-green baseline. tap.nodeArgs now imports ./tests/_setup/isolateDataDir.ts, which gives each spawned test process its own temp DATA_DIR \u2014 eliminating the shared on-disk DB, so concurrency>1 is deterministic. A/B verified 2026-06-17: dry-run at concurrency=4 fails WITHOUT the isolation import (account-fallback-service tap exit 9) and passes WITH it. Raise further only if the runner has spare cores."
"_concurrency_comment": "concurrency=4 (was 1): the covering node:test files used to share SQLite/module state via the default DATA_DIR (~/.omniroute), so running them concurrently in the Stryker sandbox caused cross-file races that failed the all-green baseline. tap.nodeArgs now imports ./tests/_setup/isolateDataDir.ts, which gives each spawned test process its own temp DATA_DIR eliminating the shared on-disk DB, so concurrency>1 is deterministic. A/B verified 2026-06-17: dry-run at concurrency=4 fails WITHOUT the isolation import (account-fallback-service tap exit 9) and passes WITH it. Raise further only if the runner has spare cores."
}

View File

@@ -223,6 +223,12 @@ function createRecoverableDb(sqliteFile) {
auth_type TEXT,
name TEXT,
is_active INTEGER DEFAULT 1,
test_status TEXT,
error_code TEXT,
last_error TEXT,
last_error_at TEXT,
last_error_type TEXT,
last_error_source TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
@@ -659,6 +665,12 @@ test(
name TEXT,
priority INTEGER DEFAULT 0,
is_active INTEGER DEFAULT 1,
test_status TEXT,
error_code TEXT,
last_error TEXT,
last_error_at TEXT,
last_error_type TEXT,
last_error_source TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);

View File

@@ -1,6 +1,7 @@
import assert from "node:assert/strict";
import { test } from "node:test";
import { execFileSync } from "node:child_process";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
@@ -30,7 +31,19 @@ function relativeJsImports(): string[] {
if ((err as { status?: number }).status === 1) return [];
throw err;
}
return out.split("\n").filter((line) => line.trim().length > 0);
return (
out
.split("\n")
.filter((line) => line.trim().length > 0)
// A `.js` specifier whose target really is a JavaScript file (e.g.
// open-sse/lib/deepseek-pow-hash.js, shared with a worker) is correct —
// the defect #10674 guards against is a `.js` suffix on a `.ts` source.
.filter((line) => {
const m = /^([^:]+):\d+:.*from "(\.{1,2}\/[^"]*\.js)"/.exec(line);
if (!m) return true;
return !fs.existsSync(path.resolve(REPO_ROOT, path.dirname(m[1]), m[2]));
})
);
}
test("no first-party TypeScript module is imported through a .js specifier", () => {

View File

@@ -29,7 +29,7 @@ function baseParams(over: Partial<ProbeParams> = {}): ProbeParams {
describe("web-cookie health probe (#11488)", () => {
it("candidate detection matches catalogued cookie providers only", () => {
assert.equal(isWebCookieHealthProbeCandidate("claude-web"), true);
assert.equal(isWebCookieHealthProbeCandidate("chatgpt-web"), true);
assert.equal(isWebCookieHealthProbeCandidate("chatgpt-web-codex"), true);
assert.equal(isWebCookieHealthProbeCandidate("openai"), false);
assert.equal(isWebCookieHealthProbeCandidate(undefined), false);
assert.equal(isWebCookieHealthProbeCandidate(""), false);
@@ -210,7 +210,7 @@ describe("web-cookie health probe (#11488)", () => {
baseParams({
conn: {
id: "c1",
provider: "qwen-web",
provider: "grok-web",
apiKey: "",
providerSpecificData: { cookie: "token=abc" },
},

View File

@@ -47,7 +47,7 @@ test("web-session contract preserves representative token and cookie semantics",
assert.equal(providers.get("deepseek-web")?.credential.kind, "token");
assert.equal(providers.get("zai-web")?.credential.kind, "token");
assert.equal(providers.get("gemini-web")?.credential.kind, "cookie");
assert.equal(providers.get("qwen-web")?.credential.kind, "cookie");
assert.equal(providers.get("perplexity-web")?.credential.kind, "cookie");
assert.ok(
providers