From 7a098a0d853fec21e8ae2a8455690c3e83154435 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza Date: Sat, 4 Jul 2026 14:31:08 -0300 Subject: [PATCH 001/749] chore(release): open v3.8.45 development cycle --- CHANGELOG.md | 4 ++++ docs/i18n/ar/CHANGELOG.md | 4 ++++ docs/i18n/az/CHANGELOG.md | 4 ++++ docs/i18n/bg/CHANGELOG.md | 4 ++++ docs/i18n/bn/CHANGELOG.md | 4 ++++ docs/i18n/cs/CHANGELOG.md | 4 ++++ docs/i18n/da/CHANGELOG.md | 4 ++++ docs/i18n/de/CHANGELOG.md | 4 ++++ docs/i18n/es/CHANGELOG.md | 4 ++++ docs/i18n/fa/CHANGELOG.md | 4 ++++ docs/i18n/fi/CHANGELOG.md | 4 ++++ docs/i18n/fr/CHANGELOG.md | 4 ++++ docs/i18n/gu/CHANGELOG.md | 4 ++++ docs/i18n/he/CHANGELOG.md | 4 ++++ docs/i18n/hi/CHANGELOG.md | 4 ++++ docs/i18n/hu/CHANGELOG.md | 4 ++++ docs/i18n/id/CHANGELOG.md | 4 ++++ docs/i18n/in/CHANGELOG.md | 4 ++++ docs/i18n/it/CHANGELOG.md | 4 ++++ docs/i18n/ja/CHANGELOG.md | 4 ++++ docs/i18n/ko/CHANGELOG.md | 4 ++++ docs/i18n/mr/CHANGELOG.md | 4 ++++ docs/i18n/ms/CHANGELOG.md | 4 ++++ docs/i18n/nl/CHANGELOG.md | 4 ++++ docs/i18n/no/CHANGELOG.md | 4 ++++ docs/i18n/phi/CHANGELOG.md | 4 ++++ docs/i18n/pl/CHANGELOG.md | 4 ++++ docs/i18n/pt-BR/CHANGELOG.md | 4 ++++ docs/i18n/pt/CHANGELOG.md | 4 ++++ docs/i18n/ro/CHANGELOG.md | 4 ++++ docs/i18n/ru/CHANGELOG.md | 4 ++++ docs/i18n/sk/CHANGELOG.md | 4 ++++ docs/i18n/sv/CHANGELOG.md | 4 ++++ docs/i18n/sw/CHANGELOG.md | 4 ++++ docs/i18n/ta/CHANGELOG.md | 4 ++++ docs/i18n/te/CHANGELOG.md | 4 ++++ docs/i18n/th/CHANGELOG.md | 4 ++++ docs/i18n/tr/CHANGELOG.md | 4 ++++ docs/i18n/uk-UA/CHANGELOG.md | 4 ++++ docs/i18n/ur/CHANGELOG.md | 4 ++++ docs/i18n/vi/CHANGELOG.md | 4 ++++ docs/i18n/zh-CN/CHANGELOG.md | 4 ++++ docs/i18n/zh-TW/CHANGELOG.md | 4 ++++ docs/openapi.yaml | 2 +- electron/package-lock.json | 4 ++-- electron/package.json | 2 +- open-sse/package.json | 2 +- package-lock.json | 6 +++--- package.json | 2 +- 49 files changed, 181 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cea29910b9..f879796723 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ --- +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/ar/CHANGELOG.md b/docs/i18n/ar/CHANGELOG.md index 343b956e4a..fd67be1364 100644 --- a/docs/i18n/ar/CHANGELOG.md +++ b/docs/i18n/ar/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/az/CHANGELOG.md b/docs/i18n/az/CHANGELOG.md index 357450b457..50afbca45d 100644 --- a/docs/i18n/az/CHANGELOG.md +++ b/docs/i18n/az/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/bg/CHANGELOG.md b/docs/i18n/bg/CHANGELOG.md index 357450b457..50afbca45d 100644 --- a/docs/i18n/bg/CHANGELOG.md +++ b/docs/i18n/bg/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/bn/CHANGELOG.md b/docs/i18n/bn/CHANGELOG.md index 17b9ff260b..bcc49a1d43 100644 --- a/docs/i18n/bn/CHANGELOG.md +++ b/docs/i18n/bn/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/cs/CHANGELOG.md b/docs/i18n/cs/CHANGELOG.md index 84125dad37..ebff0c0cef 100644 --- a/docs/i18n/cs/CHANGELOG.md +++ b/docs/i18n/cs/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/da/CHANGELOG.md b/docs/i18n/da/CHANGELOG.md index b372e070f9..2e2294a472 100644 --- a/docs/i18n/da/CHANGELOG.md +++ b/docs/i18n/da/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/de/CHANGELOG.md b/docs/i18n/de/CHANGELOG.md index 4a23173c9b..95ab5e1a0e 100644 --- a/docs/i18n/de/CHANGELOG.md +++ b/docs/i18n/de/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/es/CHANGELOG.md b/docs/i18n/es/CHANGELOG.md index 6a1cecb633..fcd34f3c34 100644 --- a/docs/i18n/es/CHANGELOG.md +++ b/docs/i18n/es/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/fa/CHANGELOG.md b/docs/i18n/fa/CHANGELOG.md index 67763cb50f..d609d64c99 100644 --- a/docs/i18n/fa/CHANGELOG.md +++ b/docs/i18n/fa/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/fi/CHANGELOG.md b/docs/i18n/fi/CHANGELOG.md index f67a62375e..041b8b4b96 100644 --- a/docs/i18n/fi/CHANGELOG.md +++ b/docs/i18n/fi/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/fr/CHANGELOG.md b/docs/i18n/fr/CHANGELOG.md index 530b5687c0..da0d19876f 100644 --- a/docs/i18n/fr/CHANGELOG.md +++ b/docs/i18n/fr/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/gu/CHANGELOG.md b/docs/i18n/gu/CHANGELOG.md index 249ecb4009..4fec859bc5 100644 --- a/docs/i18n/gu/CHANGELOG.md +++ b/docs/i18n/gu/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/he/CHANGELOG.md b/docs/i18n/he/CHANGELOG.md index 266fe57538..8af769ce6e 100644 --- a/docs/i18n/he/CHANGELOG.md +++ b/docs/i18n/he/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/hi/CHANGELOG.md b/docs/i18n/hi/CHANGELOG.md index ab9d45d02c..cb32d371d6 100644 --- a/docs/i18n/hi/CHANGELOG.md +++ b/docs/i18n/hi/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/hu/CHANGELOG.md b/docs/i18n/hu/CHANGELOG.md index 34949cde0f..a8f46ec69d 100644 --- a/docs/i18n/hu/CHANGELOG.md +++ b/docs/i18n/hu/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/id/CHANGELOG.md b/docs/i18n/id/CHANGELOG.md index 90386b74bb..7f683f4e25 100644 --- a/docs/i18n/id/CHANGELOG.md +++ b/docs/i18n/id/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/in/CHANGELOG.md b/docs/i18n/in/CHANGELOG.md index e0359a0ec6..7f9cace70b 100644 --- a/docs/i18n/in/CHANGELOG.md +++ b/docs/i18n/in/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/it/CHANGELOG.md b/docs/i18n/it/CHANGELOG.md index 3b03e421ac..6603d6f599 100644 --- a/docs/i18n/it/CHANGELOG.md +++ b/docs/i18n/it/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/ja/CHANGELOG.md b/docs/i18n/ja/CHANGELOG.md index 1ba127ae80..6598e1772d 100644 --- a/docs/i18n/ja/CHANGELOG.md +++ b/docs/i18n/ja/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/ko/CHANGELOG.md b/docs/i18n/ko/CHANGELOG.md index d283a24b58..ae7041112f 100644 --- a/docs/i18n/ko/CHANGELOG.md +++ b/docs/i18n/ko/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/mr/CHANGELOG.md b/docs/i18n/mr/CHANGELOG.md index 69a05e1f90..9b4865b8c1 100644 --- a/docs/i18n/mr/CHANGELOG.md +++ b/docs/i18n/mr/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/ms/CHANGELOG.md b/docs/i18n/ms/CHANGELOG.md index 963802e9bb..17eab69b7e 100644 --- a/docs/i18n/ms/CHANGELOG.md +++ b/docs/i18n/ms/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/nl/CHANGELOG.md b/docs/i18n/nl/CHANGELOG.md index fd624c7e0b..e97cf6d61d 100644 --- a/docs/i18n/nl/CHANGELOG.md +++ b/docs/i18n/nl/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/no/CHANGELOG.md b/docs/i18n/no/CHANGELOG.md index 3ce5ece1fd..39c063287b 100644 --- a/docs/i18n/no/CHANGELOG.md +++ b/docs/i18n/no/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/phi/CHANGELOG.md b/docs/i18n/phi/CHANGELOG.md index be1febd41f..f15b08d683 100644 --- a/docs/i18n/phi/CHANGELOG.md +++ b/docs/i18n/phi/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/pl/CHANGELOG.md b/docs/i18n/pl/CHANGELOG.md index bfeae5075c..52447603f4 100644 --- a/docs/i18n/pl/CHANGELOG.md +++ b/docs/i18n/pl/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/pt-BR/CHANGELOG.md b/docs/i18n/pt-BR/CHANGELOG.md index 7bc628ab00..f5a408df0b 100644 --- a/docs/i18n/pt-BR/CHANGELOG.md +++ b/docs/i18n/pt-BR/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/pt/CHANGELOG.md b/docs/i18n/pt/CHANGELOG.md index e160fbacc7..dd53f77c5f 100644 --- a/docs/i18n/pt/CHANGELOG.md +++ b/docs/i18n/pt/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/ro/CHANGELOG.md b/docs/i18n/ro/CHANGELOG.md index 3e67240b9f..eda5fe9cfd 100644 --- a/docs/i18n/ro/CHANGELOG.md +++ b/docs/i18n/ro/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/ru/CHANGELOG.md b/docs/i18n/ru/CHANGELOG.md index 3244970c3a..d6ddafdf59 100644 --- a/docs/i18n/ru/CHANGELOG.md +++ b/docs/i18n/ru/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/sk/CHANGELOG.md b/docs/i18n/sk/CHANGELOG.md index 32bac15931..4033c72ffa 100644 --- a/docs/i18n/sk/CHANGELOG.md +++ b/docs/i18n/sk/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/sv/CHANGELOG.md b/docs/i18n/sv/CHANGELOG.md index 48cc767aec..107849ddf5 100644 --- a/docs/i18n/sv/CHANGELOG.md +++ b/docs/i18n/sv/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/sw/CHANGELOG.md b/docs/i18n/sw/CHANGELOG.md index 3d432d12f6..b72838a986 100644 --- a/docs/i18n/sw/CHANGELOG.md +++ b/docs/i18n/sw/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/ta/CHANGELOG.md b/docs/i18n/ta/CHANGELOG.md index 860d434f88..02dcf8fae1 100644 --- a/docs/i18n/ta/CHANGELOG.md +++ b/docs/i18n/ta/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/te/CHANGELOG.md b/docs/i18n/te/CHANGELOG.md index ef9318e809..fefb40e53c 100644 --- a/docs/i18n/te/CHANGELOG.md +++ b/docs/i18n/te/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/th/CHANGELOG.md b/docs/i18n/th/CHANGELOG.md index d235e9bc95..be4f09992e 100644 --- a/docs/i18n/th/CHANGELOG.md +++ b/docs/i18n/th/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/tr/CHANGELOG.md b/docs/i18n/tr/CHANGELOG.md index ffa3957144..8eaeb8134e 100644 --- a/docs/i18n/tr/CHANGELOG.md +++ b/docs/i18n/tr/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/uk-UA/CHANGELOG.md b/docs/i18n/uk-UA/CHANGELOG.md index 76e37ff50b..bcd4c5885b 100644 --- a/docs/i18n/uk-UA/CHANGELOG.md +++ b/docs/i18n/uk-UA/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/ur/CHANGELOG.md b/docs/i18n/ur/CHANGELOG.md index 563cbbb5b8..718eefbabb 100644 --- a/docs/i18n/ur/CHANGELOG.md +++ b/docs/i18n/ur/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/vi/CHANGELOG.md b/docs/i18n/vi/CHANGELOG.md index afb689e032..5e37cc493f 100644 --- a/docs/i18n/vi/CHANGELOG.md +++ b/docs/i18n/vi/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/zh-CN/CHANGELOG.md b/docs/i18n/zh-CN/CHANGELOG.md index 03777b317e..2b8a8ed467 100644 --- a/docs/i18n/zh-CN/CHANGELOG.md +++ b/docs/i18n/zh-CN/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/i18n/zh-TW/CHANGELOG.md b/docs/i18n/zh-TW/CHANGELOG.md index d58be68182..65fefe097c 100644 --- a/docs/i18n/zh-TW/CHANGELOG.md +++ b/docs/i18n/zh-TW/CHANGELOG.md @@ -6,6 +6,10 @@ ## [3.8.31] — 2026-06-20 +## [3.8.45] — TBD + +--- + ## [3.8.44] — TBD ### ✨ New Features diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 81a24a89f6..13280554b1 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -1,7 +1,7 @@ openapi: 3.1.0 info: title: OmniRoute API - version: 3.8.44 + version: 3.8.45 description: | OmniRoute is a local-first AI API proxy router. It provides an OpenAI-compatible endpoint that routes requests to multiple AI providers with load balancing, diff --git a/electron/package-lock.json b/electron/package-lock.json index 4bd0d087ca..ab8dfefbca 100644 --- a/electron/package-lock.json +++ b/electron/package-lock.json @@ -1,12 +1,12 @@ { "name": "omniroute-desktop", - "version": "3.8.44", + "version": "3.8.45", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "omniroute-desktop", - "version": "3.8.44", + "version": "3.8.45", "license": "MIT", "dependencies": { "electron-updater": "^6.8.9" diff --git a/electron/package.json b/electron/package.json index 6cca7ee884..c53246c201 100644 --- a/electron/package.json +++ b/electron/package.json @@ -1,6 +1,6 @@ { "name": "omniroute-desktop", - "version": "3.8.44", + "version": "3.8.45", "description": "OmniRoute Desktop Application", "main": "main.js", "author": { diff --git a/open-sse/package.json b/open-sse/package.json index f4e9ce4697..05b24a10a5 100644 --- a/open-sse/package.json +++ b/open-sse/package.json @@ -1,6 +1,6 @@ { "name": "@omniroute/open-sse", - "version": "3.8.44", + "version": "3.8.45", "description": "Express SSE sidecar for OmniRoute — handles streaming, protocol translation, and provider orchestration", "type": "module", "main": "index.js", diff --git a/package-lock.json b/package-lock.json index 882f93cc12..c21becc134 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "omniroute", - "version": "3.8.44", + "version": "3.8.45", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "omniroute", - "version": "3.8.44", + "version": "3.8.45", "hasInstallScript": true, "license": "MIT", "workspaces": [ @@ -28673,7 +28673,7 @@ }, "open-sse": { "name": "@omniroute/open-sse", - "version": "3.8.44", + "version": "3.8.45", "dependencies": { "@toon-format/toon": "^2.3.0", "safe-regex": "^2.1.1" diff --git a/package.json b/package.json index 6d45cfcfe0..9fd05c2d37 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "omniroute", - "version": "3.8.44", + "version": "3.8.45", "description": "Unified AI router with 160+ providers, RTK+Caveman compression, auto fallback, MCP/A2A, desktop, PWA, and OpenAI-compatible APIs.", "type": "module", "bin": { From bb62c2a618d8dd8ab68d096d369643846a6f2a59 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Date: Sat, 4 Jul 2026 15:17:08 -0300 Subject: [PATCH 002/749] =?UTF-8?q?chore(release):=20parallel-cycle=20flow?= =?UTF-8?q?=20=E2=80=94=20sync-next-cycle=20script=20+=20Hard=20Rule=20#21?= =?UTF-8?q?=20semantics=20(#6203)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Integrated into release/v3.8.45 --- CLAUDE.md | 2 +- scripts/release/sync-next-cycle.mjs | 195 ++++++++++++++++++++++++++++ stryker.conf.json | 100 +++++++------- tests/unit/sync-next-cycle.test.ts | 86 ++++++++++++ 4 files changed, 334 insertions(+), 49 deletions(-) create mode 100644 scripts/release/sync-next-cycle.mjs create mode 100644 tests/unit/sync-next-cycle.test.ts diff --git a/CLAUDE.md b/CLAUDE.md index 03519c0eb4..6ec3fd896d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -540,7 +540,7 @@ the stale-enforcement added in Fase 6A.3. 18. Every bug fix must be validated before shipping: a failing-then-passing unit/integration test (TDD) OR a documented live test on the production VPS (192.168.0.15). A fix without either is not merged. See Testing → "Bug fix / issue triage protocol" for the full decision tree. 19. Never develop on the shared main checkout. Every development task runs in its own git worktree on its own dedicated branch, and you MUST confirm the base branch with the operator (e.g. via `AskUserQuestion`) before creating the worktree/branch — never assume `main` or the currently checked-out branch. A `git checkout` in the shared checkout silently destroys other sessions' uncommitted work. Tear down only the worktrees/branches you created (by name, never `fix/*`/`feat/*` wildcards), leave other sessions' worktrees untouched, and end on the branch you started on (the active `release/vX.Y.Z`, never `main`). See Git Workflow → "Worktree isolation". 20. PII redaction/sanitization is **opt-in — never on by default**. OmniRoute proxies for self-hosted/local LLMs where the operator owns the data, so mutating request/response payloads by default would silently corrupt legitimate traffic. The two data-mutating PII feature flags **MUST** keep `defaultValue: "false"` in `src/shared/constants/featureFlagDefinitions.ts`: `PII_REDACTION_ENABLED` (request-side) and `PII_RESPONSE_SANITIZATION` (response + streaming). All three application points — `src/lib/guardrails/piiMasker.ts` (request guardrail), `src/lib/piiSanitizer.ts` (response), `src/lib/streamingPiiTransform.ts` (SSE) — are gated on these flags; with both off the `pii-masker` guardrail still runs but never mutates payloads (data passes through untouched). Flipping either default to `"true"` requires explicit operator approval. The regression guard is `tests/unit/pii-opt-in-default.test.ts` (asserts both definition defaults + behavioral pass-through). Opt-in is per-operator via env or the settings/DB override (`src/lib/db/featureFlags.ts`), never a silent default. See `docs/security/GUARDRAILS.md`. -21. **Release-freeze — the release branch is frozen to campaign merges while a `/generate-release` is running.** `/generate-release` opens a marker issue labeled `release-freeze` at the start of reconciliation (Phase 0a) and closes it once the release PR squash-merges to `main`. Before merging **any** PR into the active `release/vX.Y.Z` branch, every campaign workflow (`/review-issues`, `/review-prs`, `/implement-features`, `/green-prs`, `/port-upstream-*`) **MUST** check `gh issue list --repo diegosouzapw/OmniRoute --label release-freeze --state open` — if a freeze is active, **HOLD the merge** (leave the PR ready and open; do NOT merge to the release branch), tell the operator, and resume once the freeze lifts. This is a **coordination signal, not a permission lock**: the release captain and the campaign sessions share the `diegosouzapw` identity, so a GitHub branch-protection lock cannot distinguish them — only this honored marker prevents the mid-release commit races that forced full CHANGELOG re-reconciliation in v3.8.40/v3.8.41 (a parallel campaign advanced `release/vX.Y.Z` by 34 commits mid-run). The release captain's own reconciliation/cycle-open pushes are exempt — they _are_ the release. Fixes that must land during a freeze (a homologation finding) follow the post-merge read-only rule: land on `main` first via `fix/release-vX.Y.Z-*`. **⛔ ONLY `/generate-release` may raise a release-freeze, and ONLY at its Phase 0a (start of generating a new version) — lifted at Phase 12c after the squash-merge to `main`.** No campaign, session, or agent may open a `release-freeze` marker at any other time — a freeze is **never** a mid-development coordination tool. If a session ever believes a freeze is genuinely, unavoidably necessary outside the `/generate-release` flow, it **MUST first ask the operator (`diegosouzapw`) in chat, explicitly alert "estou criando um freeze" and get an explicit yes** — never open, extend, or re-open a `release-freeze` autonomously. Conversely, do **not** close/lift an active `/generate-release` freeze to unblock campaign merges: it protects the captain's single clean CI run and auto-lifts at Phase 12c — closing it early re-triggers the exact commit race it prevents. Verify a freeze is legitimate before acting on it: an open `release-freeze` whose title/body references an **OPEN** release PR (`gh pr view --json state`) is the authorized captain freeze — hold, don't touch. +21. **Release-freeze — the FROZEN release branch belongs to the release captain; development does NOT stop (parallel-cycle model, 2026-07-04).** `/generate-release` opens a marker issue labeled `release-freeze` at the start of reconciliation (Phase 0a), **immediately cuts the next cycle's branch `release/vX+1` from the frozen tip (Phase 0a.0b — bump + living release PR + re-home of open PRs)**, and closes the freeze once the release PR squash-merges to `main`. Before merging **any** PR, every campaign workflow (`/review-issues`, `/review-prs`, `/implement-features`, `/green-prs`, `/port-upstream-*`) **MUST** check `gh issue list --repo diegosouzapw/OmniRoute --label release-freeze --state open` — if a freeze is active: **NEVER merge into the frozen `release/vX.Y.Z` named in the freeze title**; instead resolve the ACTIVE development branch (the **highest** `release/v*` by semver — normally `release/vX+1`, announced in a freeze-issue comment) and **retarget the PR there** (`gh pr edit --base release/vX+1`, then VERIFY with `gh pr view --json baseRefName` — the edit fails silently) and merge normally. **HOLD only when the highest release/v\* branch IS the frozen one** (the short window before 0a.0b completes, or a pre-parallel-cycle release) — in that case leave the PR ready and open, tell the operator, and resume when the next branch appears or the freeze lifts. The just-shipped fixes reach `release/vX+1` via the Phase 5 sync-back (`scripts/release/sync-next-cycle.mjs`); do not try to sync mid-release. This is a **coordination signal, not a permission lock**: the release captain and the campaign sessions share the `diegosouzapw` identity, so a GitHub branch-protection lock cannot distinguish them — only this honored marker prevents the mid-release commit races that forced full CHANGELOG re-reconciliation in v3.8.40/v3.8.41 (a parallel campaign advanced `release/vX.Y.Z` by 34 commits mid-run). The release captain's own reconciliation/cycle-open pushes are exempt — they _are_ the release. Fixes that must land during a freeze (a homologation finding) follow the post-merge read-only rule: land on `main` first via `fix/release-vX.Y.Z-*`. **⛔ ONLY `/generate-release` may raise a release-freeze, and ONLY at its Phase 0a (start of generating a new version) — lifted at Phase 12c after the squash-merge to `main`.** No campaign, session, or agent may open a `release-freeze` marker at any other time — a freeze is **never** a mid-development coordination tool. If a session ever believes a freeze is genuinely, unavoidably necessary outside the `/generate-release` flow, it **MUST first ask the operator (`diegosouzapw`) in chat, explicitly alert "estou criando um freeze" and get an explicit yes** — never open, extend, or re-open a `release-freeze` autonomously. Conversely, do **not** close/lift an active `/generate-release` freeze to unblock campaign merges: it protects the captain's single clean CI run and auto-lifts at Phase 12c — closing it early re-triggers the exact commit race it prevents. Verify a freeze is legitimate before acting on it: an open `release-freeze` whose title/body references an **OPEN** release PR (`gh pr view --json state`) is the authorized captain freeze — hold, don't touch. 22. **Cross-session safety — this repo is worked by MANY parallel sessions/agents at once; never step on another's in-flight work.** Two absolute bans, both recurring incidents (this rule exists because they keep happening): - **(a) Never `git stash` / `git stash pop` — ANYWHERE in this repo, including inside an isolated worktree, and including inside any subagent you dispatch.** `git stash` operates on the **shared repository object store**, not the per-worktree working tree — so a stash pushed or popped in one session can silently clobber or resurrect another parallel session's uncommitted changes. This is not hypothetical: 2026-07-02 a `#5923` quotaCache change leaked into the unrelated `#2296` worktree via a global `stash pop`, and the same class reincided through a **subagent**. To compare working changes against a base ref **without** stashing, use `git show :` or `git diff -- `; to confirm a typecheck/lint error is pre-existing on the base, inspect the base ref directly (`git show origin/release/vX.Y.Z:`) — never stash your tree away to "get it clean". **Put this ban verbatim in the prompt of every subagent that touches git** (agents don't inherit this file's context — the recurrence was a subagent). - **(b) Never merge, push, rebase, or force-push a PR / branch / worktree that another session is actively working.** An open PR whose head is a live fix worktree in `.claude/worktrees/` you did **not** create (e.g. `fix-5852`/`fix-5923` carrying fresh commits, even when they share your `diegosouzapw` identity), or any branch another session owns, is **off-limits — HOLD**, and let the owning session merge it. **Before** merging or pushing to any PR you did not create *this* session, run `git worktree list` to check for a matching in-flight worktree and re-check `gh pr view --json state,headRefOid`. Only the owning session merges its own in-flight PR; mid-flight merges race the owner and re-trigger the exact commit/CHANGELOG races Rule #19 and Rule #21 guard against. (Reinforces Rule #19.) diff --git a/scripts/release/sync-next-cycle.mjs b/scripts/release/sync-next-cycle.mjs new file mode 100644 index 0000000000..c7867843ae --- /dev/null +++ b/scripts/release/sync-next-cycle.mjs @@ -0,0 +1,195 @@ +#!/usr/bin/env node +// scripts/release/sync-next-cycle.mjs +// +// Parallel-cycle sync-back (generate-release Phase 5, step 20). +// Merges origin/main (which carries the just-shipped release's closing fixes + +// finalized CHANGELOG) into the live next-cycle branch release/v that was +// cut at the freeze (Phase 0a.0b) — resolving CHANGELOG.md with the anti-eat +// protocol: main's CHANGELOG wins VERBATIM, and the next cycle's own +// `## [] — TBD` section (with any bullets it already accumulated) is +// re-inserted on top. Design: _tasks/release-flow/2026-07-04_proposta-ciclo-paralelo-v2.md +// +// Usage: node scripts/release/sync-next-cycle.mjs e.g. 3.8.45 +// +// Idempotent: safe to re-run after resolving any conflicts it could not +// auto-resolve (it detects an in-progress merge in its worktree and resumes). + +import { execFileSync } from "node:child_process"; +import fs from "node:fs"; +import path from "node:path"; +import { pathToFileURL } from "node:url"; + +/** + * Pure: insert (or replace) the next cycle's section into main's CHANGELOG. + * - `mainChangelog`: the full CHANGELOG.md content from origin/main (wins verbatim). + * - `nextSection`: the `## [] — …` section text captured from the cycle + * branch BEFORE the merge (header line through the line before the next `## [` + * heading), or null/empty when the cycle has no section yet (a fresh `— TBD` + * placeholder is synthesized). + * The section lands right after the `## [Unreleased]` block (mirroring the + * layout every cycle-open produces), before the first `## [x.y.z]` heading. + */ +export function insertNextSection(mainChangelog, nextSection, nextVersion) { + const lines = mainChangelog.split("\n"); + const isVersionHeading = (l) => /^## \[\d+\.\d+\.\d+\]/.test(l); + + // Drop any pre-existing copy of the next section from main's content (it + // normally has none — main only learns about at the NEXT release). + const startIdx = lines.findIndex((l) => l.startsWith(`## [${nextVersion}]`)); + if (startIdx !== -1) { + let endIdx = lines.length; + for (let i = startIdx + 1; i < lines.length; i++) { + if (isVersionHeading(lines[i]) || lines[i].startsWith("## [Unreleased]")) { + endIdx = i; + break; + } + } + lines.splice(startIdx, endIdx - startIdx); + } + + const section = + nextSection && nextSection.trim().length > 0 + ? nextSection.replace(/\s+$/, "") + : `## [${nextVersion}] — TBD`; + + // Insert before the first released-version heading. + const firstVersionIdx = lines.findIndex(isVersionHeading); + const insertAt = firstVersionIdx === -1 ? lines.length : firstVersionIdx; + lines.splice(insertAt, 0, ...section.split("\n"), "", "---", ""); + + // Collapse accidental duplicate separators introduced by the splice. + return lines + .join("\n") + .replace(/\n---\n\n---\n/g, "\n---\n") + .replace(/\n{4,}/g, "\n\n\n"); +} + +/** Pure: extract the `## []` section (header included, next heading excluded). */ +export function extractSection(changelog, version) { + const lines = changelog.split("\n"); + const start = lines.findIndex((l) => l.startsWith(`## [${version}]`)); + if (start === -1) return null; + let end = lines.length; + for (let i = start + 1; i < lines.length; i++) { + if (/^## \[/.test(lines[i])) { + end = i; + break; + } + } + // Trim a trailing `---` separator so re-insertion controls its own framing. + let sectionLines = lines.slice(start, end); + while ( + sectionLines.length && + (sectionLines[sectionLines.length - 1].trim() === "---" || + sectionLines[sectionLines.length - 1].trim() === "") + ) { + sectionLines.pop(); + } + return sectionLines.join("\n"); +} + +function git(args, opts = {}) { + return execFileSync("git", args, { encoding: "utf8", ...opts }).trim(); +} + +function main() { + const NEXT = process.argv[2]; + if (!/^\d+\.\d+\.\d+$/.test(NEXT || "")) { + console.error("usage: node scripts/release/sync-next-cycle.mjs (e.g. 3.8.45)"); + process.exit(2); + } + const ROOT = git(["rev-parse", "--show-toplevel"]); + const BRANCH = `release/v${NEXT}`; + const WT = path.join(ROOT, ".claude", "worktrees", `sync-next-${NEXT}`); + + git(["fetch", "origin", "main", BRANCH], { cwd: ROOT, stdio: ["ignore", "pipe", "pipe"] }); + const prevVersion = JSON.parse(git(["show", "origin/main:package.json"], { cwd: ROOT })).version; + console.log(`[sync-next-cycle] main is v${prevVersion} → syncing into ${BRANCH}`); + + // Worktree (idempotent — reuse if a previous run left it for conflict resolution). + if (!fs.existsSync(WT)) { + git(["worktree", "add", WT, BRANCH], { cwd: ROOT, stdio: ["ignore", "pipe", "pipe"] }); + } + const mergeHeadPath = git(["rev-parse", "--path-format=absolute", "--git-path", "MERGE_HEAD"], { + cwd: WT, + }); + const mergeInProgress = fs.existsSync(mergeHeadPath); + if (!mergeInProgress) { + git(["pull", "--ff-only", "origin", BRANCH], { cwd: WT, stdio: ["ignore", "pipe", "pipe"] }); + } + + // Capture the cycle's own section BEFORE the merge touches the file. + const cycleChangelog = fs.readFileSync(path.join(WT, "CHANGELOG.md"), "utf8"); + const nextSection = extractSection(cycleChangelog, NEXT); + + if (!mergeInProgress) { + try { + execFileSync("git", ["merge", "--no-commit", "--no-ff", "origin/main"], { + cwd: WT, + encoding: "utf8", + stdio: ["ignore", "pipe", "pipe"], + }); + } catch { + // Conflicts are expected (CHANGELOG at minimum) — handled below. + } + } + + // Anti-CHANGELOG-eat: main's CHANGELOG verbatim + the cycle's section on top. + const mainChangelog = git(["show", "origin/main:CHANGELOG.md"], { cwd: WT }); + const merged = insertNextSection(mainChangelog + "\n", nextSection, NEXT); + // Assertions: main's latest section intact + next section present. + if (!merged.includes(`## [${prevVersion}]`)) { + console.error(`[sync-next-cycle] ABORT: main's ## [${prevVersion}] section missing after re-insertion`); + process.exit(1); + } + if (!merged.includes(`## [${NEXT}]`)) { + console.error(`[sync-next-cycle] ABORT: ## [${NEXT}] section missing after re-insertion`); + process.exit(1); + } + fs.writeFileSync(path.join(WT, "CHANGELOG.md"), merged); + git(["add", "CHANGELOG.md"], { cwd: WT }); + + // i18n mirrors: regenerate instead of merging them one by one. + const mirrors = git(["diff", "--name-only", "--diff-filter=U"], { cwd: WT }) + .split("\n") + .filter((f) => f.startsWith("docs/i18n/") && f.endsWith("CHANGELOG.md")); + for (const m of mirrors) { + execFileSync("git", ["checkout", "origin/main", "--", m], { cwd: WT }); + execFileSync("git", ["add", m], { cwd: WT }); + } + try { + execFileSync("npm", ["run", "release:sync-changelog-i18n", "--", NEXT, prevVersion], { + cwd: WT, + encoding: "utf8", + stdio: ["ignore", "pipe", "pipe"], + }); + execFileSync("git", ["add", "-A", "docs/i18n"], { cwd: WT }); + } catch (e) { + console.warn("[sync-next-cycle] i18n mirror resync failed (resolve manually):", e.message); + } + + // Anything still conflicted is for the human (migrations, lockfile, code). + const unresolved = git(["diff", "--name-only", "--diff-filter=U"], { cwd: WT }) + .split("\n") + .filter(Boolean); + if (unresolved.length) { + console.error( + `[sync-next-cycle] ${unresolved.length} conflict(s) need manual resolution in ${WT}:\n` + + unresolved.map((f) => ` ✗ ${f}`).join("\n") + + `\n → resolve + git add, then re-run this script (it resumes the merge).` + + `\n → migrations: renumber per the cross-PR collision precedent; lockfile: npm install.` + ); + process.exit(1); + } + + git(["commit", "-m", `chore(release): sync main (v${prevVersion} close) into ${BRANCH} — parallel-cycle sync-back`], { cwd: WT }); + git(["push", "origin", BRANCH], { cwd: WT }); + + const left = git(["rev-list", "--count", `${BRANCH}..origin/main`], { cwd: WT }); + console.log(`[sync-next-cycle] pushed. origin/main commits not in ${BRANCH}: ${left} (expected 0)`); + + git(["worktree", "remove", "--force", WT], { cwd: ROOT }); + console.log("[sync-next-cycle] done — worktree removed."); +} + +if (import.meta.url === pathToFileURL(process.argv[1] || "").href) main(); diff --git a/stryker.conf.json b/stryker.conf.json index b8dac2b67c..aa085f28dd 100644 --- a/stryker.conf.json +++ b/stryker.conf.json @@ -39,15 +39,20 @@ "incremental": true, "incrementalFile": "reports/mutation/stryker-incremental.json", "testRunner": "tap", - "plugins": ["@stryker-mutator/tap-runner"], + "plugins": [ + "@stryker-mutator/tap-runner" + ], "tap": { "testFiles": [ "tests/unit/account-fallback-anthropic-quota.test.ts", "tests/unit/account-fallback-retry-after-json.test.ts", "tests/unit/account-fallback-route-restriction-403.test.ts", "tests/unit/account-fallback-service.test.ts", + "tests/unit/accountfallback-ratelimit-400-4976.test.ts", + "tests/unit/antigravity-429-quota-tdd.test.ts", "tests/unit/api-key-rotator-health.test.ts", "tests/unit/appearance-widget-settings-schema.test.ts", + "tests/unit/auth-antigravity-account-retry-v2.test.ts", "tests/unit/auth-clear-account-error.test.ts", "tests/unit/auth-disable-cooling-2997.test.ts", "tests/unit/auth-extract-api-key.test.ts", @@ -55,6 +60,9 @@ "tests/unit/auth-ollama-cloud-per-model-403-3027.test.ts", "tests/unit/auth-opencode-zen-noauth-fallback.test.ts", "tests/unit/auth-terminal-status.test.ts", + "tests/unit/authz/discovery-routes-local-only.test.ts", + "tests/unit/authz/route-guard-local-prefix.test.ts", + "tests/unit/authz/route-guard-version-get-exemption.test.ts", "tests/unit/authz/routeGuard.test.ts", "tests/unit/auto-combo-context-advertising.test.ts", "tests/unit/auto-combo-engine.test.ts", @@ -62,6 +70,7 @@ "tests/unit/build/check-circular-deps.test.ts", "tests/unit/cache-sweeps.test.ts", "tests/unit/cc-compatible-provider.test.ts", + "tests/unit/chat-combo-live-test.test.ts", "tests/unit/chat-context-relay.test.ts", "tests/unit/chat-cooldown-aware-retry.test.ts", "tests/unit/chat-helpers.test.ts", @@ -69,6 +78,7 @@ "tests/unit/chat-route-edge-cases.test.ts", "tests/unit/chatcore-compression-integration.test.ts", "tests/unit/chatcore-executor-helpers.test.ts", + "tests/unit/chatcore-executor-proxy.test.ts", "tests/unit/chatcore-extracted-modules-3821.test.ts", "tests/unit/chatcore-headers.test.ts", "tests/unit/chatcore-imports-cleanly.test.ts", @@ -77,15 +87,20 @@ "tests/unit/chatcore-memory-skills-injection.test.ts", "tests/unit/chatcore-non-streaming-sse.test.ts", "tests/unit/chatcore-passthrough-tool-names.test.ts", + "tests/unit/chatcore-request-format.test.ts", "tests/unit/chatcore-sanitization.test.ts", + "tests/unit/chatcore-semantic-cache-store.test.ts", "tests/unit/chatcore-semantic-cache.test.ts", "tests/unit/chatcore-strip-stale-headers.test.ts", "tests/unit/chatcore-telemetry-helpers.test.ts", "tests/unit/chatcore-translation-paths.test.ts", + "tests/unit/chatcore-upstream-timeouts.test.ts", "tests/unit/check-error-helper.test.ts", "tests/unit/check-route-guard-membership.test.ts", "tests/unit/check-test-discovery.test.ts", "tests/unit/circuit-breaker-failure-kind.test.ts", + "tests/unit/circuit-breaker-registry-cap.test.ts", + "tests/unit/circuit-breaker-stream-controller-4602.test.ts", "tests/unit/claude-code-parity.test.ts", "tests/unit/claude-effort-suffix-strip.test.ts", "tests/unit/claude-oauth-provider.test.ts", @@ -99,33 +114,45 @@ "tests/unit/codex-stream-false.test.ts", "tests/unit/collect-metrics-module-coverage.test.ts", "tests/unit/combo-499-abort.test.ts", + "tests/unit/combo-account-allowlist-3266.test.ts", "tests/unit/combo-auto-candidate-expansion.test.ts", "tests/unit/combo-cache-invalidation.test.ts", "tests/unit/combo-config.test.ts", "tests/unit/combo-context-relay.test.ts", + "tests/unit/combo-headroom-strategy.test.ts", "tests/unit/combo-health-autopilot.test.ts", "tests/unit/combo-health-dashboard.test.ts", "tests/unit/combo-health-route.test.ts", "tests/unit/combo-hedging.test.ts", "tests/unit/combo-max-depth-config.test.ts", + "tests/unit/combo-model-lockout-honors-reset-1308.test.ts", "tests/unit/combo-omnimodel-tag-stripping.test.ts", + "tests/unit/combo-param-validation-fallback-4519.test.ts", "tests/unit/combo-prescreen.test.ts", "tests/unit/combo-priority-quota-exhaustion-cutoff-5923.test.ts", "tests/unit/combo-provider-cooldown.test.ts", "tests/unit/combo-provider-diversity-wiring.test.ts", "tests/unit/combo-quality-validator-reasoning.test.ts", - "tests/unit/combo-priority-quota-exhaustion-cutoff-5923.test.ts", + "tests/unit/combo-quota-share-cooldown-wait.test.ts", "tests/unit/combo-quota-soft-penalty.test.ts", "tests/unit/combo-round-robin-streaming-lock-3811.test.ts", "tests/unit/combo-routing-engine.test.ts", "tests/unit/combo-scoring-inspector.test.ts", + "tests/unit/combo-selected-connection-success.test.ts", "tests/unit/combo-sessionless-pin-3825.test.ts", "tests/unit/combo-strategies.test.ts", "tests/unit/combo-strategy-fallbacks.test.ts", + "tests/unit/combo-stream-readiness-fallback.test.ts", "tests/unit/combo-streaming-empty-content-failover.test.ts", + "tests/unit/combo-strict-random-distribution-3959.test.ts", "tests/unit/combo-target-defensive-modelstr.test.ts", + "tests/unit/combo/auto-quota-cutoff.test.ts", + "tests/unit/combo/auto-status-penalty-4540.test.ts", + "tests/unit/combo/combo-exhausted-skip.test.ts", + "tests/unit/combo/effective-max-concurrency.test.ts", "tests/unit/complexity-aware-scoring-wiring.test.ts", "tests/unit/context-pinning-tool-calls.test.ts", + "tests/unit/cooldown-epoch-string-3954.test.ts", "tests/unit/correctness/combo.property.test.ts", "tests/unit/correctness/sanitizers.property.test.ts", "tests/unit/custom-model-target-format.test.ts", @@ -136,17 +163,29 @@ "tests/unit/error-message-sanitization.test.ts", "tests/unit/executor-antigravity.test.ts", "tests/unit/executor-web-cookie-sweep.test.ts", + "tests/unit/format-provider-error-cause.test.ts", "tests/unit/gemini-web-missing-browser-3516.test.ts", + "tests/unit/grok-cli-oauth.test.ts", "tests/unit/guardrails-api-3496.test.ts", + "tests/unit/headroom-proxy-lifecycle.test.ts", + "tests/unit/livews-forward-backoff-4604.test.ts", + "tests/unit/management-auth-hardening.test.ts", + "tests/unit/mark-account-unavailable-numeric-epoch-guard.test.ts", "tests/unit/memory-embedding-remote.test.ts", "tests/unit/memory-embedding-transformers.test.ts", + "tests/unit/middleware-header-strip-5849.test.ts", "tests/unit/model-cooldowns-route-auth.test.ts", "tests/unit/model-cooldowns-route.test.ts", "tests/unit/model-lockout-decay.test.ts", + "tests/unit/model-lockout-max-cooldown.test.ts", + "tests/unit/no-memory-header.test.ts", + "tests/unit/non-streaming-sse-terminal-typescan-4459.test.ts", "tests/unit/oauth-providers-config.test.ts", "tests/unit/oauth-redirect-uri-mismatch.test.ts", "tests/unit/observability-fase04.test.ts", "tests/unit/observability-payloads.test.ts", + "tests/unit/openapi-security-tiers.test.ts", + "tests/unit/persist-429-cooldown-account-fallback.test.ts", "tests/unit/plan3-p0.test.ts", "tests/unit/plugin-sandbox-permissions.test.ts", "tests/unit/plugins-route-error-sanitization.test.ts", @@ -164,6 +203,7 @@ "tests/unit/quota-streaming-consumption-usd.test.ts", "tests/unit/rate-limit-enhanced.test.ts", "tests/unit/rate-limit-manager.test.ts", + "tests/unit/rate-limit-queue-timeout-lockout.test.ts", "tests/unit/responses-handler.test.ts", "tests/unit/route-explainability.test.ts", "tests/unit/route-guard-plugins-local-only.test.ts", @@ -172,11 +212,13 @@ "tests/unit/router-strategies.test.ts", "tests/unit/service-combo-metrics.test.ts", "tests/unit/services-branch-hardening.test.ts", + "tests/unit/services/bifrost-route-guard.test.ts", "tests/unit/services/combo-metrics-memory.test.ts", "tests/unit/settings/authz-bypass.test.ts", "tests/unit/skip-provider-breaker-consumer-2743.test.ts", "tests/unit/sse-auth.test.ts", "tests/unit/strict-random-deck.test.ts", + "tests/unit/strip-reasoning-header.test.ts", "tests/unit/system-role-extraction.test.ts", "tests/unit/t23-t24-fallback-resilience.test.ts", "tests/unit/tag-routing.test.ts", @@ -184,53 +226,11 @@ "tests/unit/token-refresh-race-comprehensive.test.ts", "tests/unit/token-refresh-service.test.ts", "tests/unit/tools-filter-anthropic-format.test.ts", - "tests/unit/usage-service-hardening.test.ts", - "tests/unit/validate-response-quality.test.ts", - "tests/unit/accountfallback-ratelimit-400-4976.test.ts", - "tests/unit/antigravity-429-quota-tdd.test.ts", - "tests/unit/auth-antigravity-account-retry-v2.test.ts", - "tests/unit/middleware-header-strip-5849.test.ts", - "tests/unit/authz/discovery-routes-local-only.test.ts", - "tests/unit/authz/route-guard-local-prefix.test.ts", - "tests/unit/authz/route-guard-version-get-exemption.test.ts", - "tests/unit/chat-combo-live-test.test.ts", - "tests/unit/chatcore-executor-proxy.test.ts", - "tests/unit/chatcore-request-format.test.ts", - "tests/unit/chatcore-semantic-cache-store.test.ts", - "tests/unit/chatcore-upstream-timeouts.test.ts", - "tests/unit/circuit-breaker-registry-cap.test.ts", - "tests/unit/circuit-breaker-stream-controller-4602.test.ts", - "tests/unit/clinepass-provider.test.ts", - "tests/unit/codex-session-affinity-reset-aware-5903.test.ts", - "tests/unit/combo-account-allowlist-3266.test.ts", - "tests/unit/combo-headroom-strategy.test.ts", - "tests/unit/combo-model-lockout-honors-reset-1308.test.ts", - "tests/unit/combo-param-validation-fallback-4519.test.ts", - "tests/unit/combo-priority-quota-exhaustion-cutoff-5923.test.ts", - "tests/unit/combo-quota-share-cooldown-wait.test.ts", - "tests/unit/combo-selected-connection-success.test.ts", - "tests/unit/combo-stream-readiness-fallback.test.ts", - "tests/unit/combo-strict-random-distribution-3959.test.ts", - "tests/unit/combo/auto-quota-cutoff.test.ts", - "tests/unit/combo/auto-status-penalty-4540.test.ts", - "tests/unit/combo/combo-exhausted-skip.test.ts", - "tests/unit/combo/effective-max-concurrency.test.ts", - "tests/unit/cooldown-epoch-string-3954.test.ts", - "tests/unit/format-provider-error-cause.test.ts", - "tests/unit/grok-cli-oauth.test.ts", - "tests/unit/headroom-proxy-lifecycle.test.ts", - "tests/unit/livews-forward-backoff-4604.test.ts", - "tests/unit/management-auth-hardening.test.ts", - "tests/unit/mark-account-unavailable-numeric-epoch-guard.test.ts", - "tests/unit/model-lockout-max-cooldown.test.ts", - "tests/unit/no-memory-header.test.ts", - "tests/unit/non-streaming-sse-terminal-typescan-4459.test.ts", - "tests/unit/openapi-security-tiers.test.ts", - "tests/unit/rate-limit-queue-timeout-lockout.test.ts", - "tests/unit/strip-reasoning-header.test.ts", "tests/unit/tproxy-route.test.ts", "tests/unit/types-barrel-model-cooldown.test.ts", - "tests/unit/upstream-retry-hints-toggle.test.ts" + "tests/unit/upstream-retry-hints-toggle.test.ts", + "tests/unit/usage-service-hardening.test.ts", + "tests/unit/validate-response-quality.test.ts" ], "nodeArgs": [ "--import", @@ -339,7 +339,11 @@ ".worktrees", ".stryker-tmp" ], - "reporters": ["progress", "html", "json"], + "reporters": [ + "progress", + "html", + "json" + ], "htmlReporter": { "fileName": "reports/mutation/mutation.html" }, diff --git a/tests/unit/sync-next-cycle.test.ts b/tests/unit/sync-next-cycle.test.ts new file mode 100644 index 0000000000..b310703a5c --- /dev/null +++ b/tests/unit/sync-next-cycle.test.ts @@ -0,0 +1,86 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { + insertNextSection, + extractSection, +} from "../../scripts/release/sync-next-cycle.mjs"; + +// Pure-function guards for the parallel-cycle sync-back (generate-release +// Phase 5 step 20): main's CHANGELOG must win VERBATIM and the next cycle's +// section must be re-inserted on top without eating any of main's bullets +// (anti-CHANGELOG-eat protocol, design doc +// _tasks/release-flow/2026-07-04_proposta-ciclo-paralelo-v2.md). + +const MAIN = `# Changelog + +## [Unreleased] + +--- + +## [3.8.44] — 2026-07-04 + +### ✨ New Features + +- **feat(a):** shipped feature one. (#1) +- **feat(b):** shipped feature two. (#2) + +### 🙌 Contributors + +| Contributor | PRs / Issues | +| --- | --- | +| [@x](https://github.com/x) | #1 | + +--- + +## [3.8.43] — 2026-07-02 + +- **old:** bullet. (#0) +`; + +const CYCLE_SECTION = `## [3.8.45] — TBD + +### ✨ New Features + +- **feat(new):** cycle bullet accumulated before the sync. (#9)`; + +test("insertNextSection puts the cycle section before main's latest version, keeping main intact", () => { + const out = insertNextSection(MAIN, CYCLE_SECTION, "3.8.45"); + const i45 = out.indexOf("## [3.8.45]"); + const i44 = out.indexOf("## [3.8.44]"); + const i43 = out.indexOf("## [3.8.43]"); + assert.ok(i45 !== -1 && i44 !== -1 && i43 !== -1, "all three sections present"); + assert.ok(i45 < i44 && i44 < i43, "ordering: 3.8.45 → 3.8.44 → 3.8.43"); + // main's content survives verbatim (no bullet eaten) + assert.ok(out.includes("shipped feature one"), "main bullet 1 intact"); + assert.ok(out.includes("shipped feature two"), "main bullet 2 intact"); + assert.ok(out.includes("| [@x](https://github.com/x) | #1 |"), "contributors row intact"); + // the cycle's accumulated bullet survives + assert.ok(out.includes("cycle bullet accumulated before the sync"), "cycle bullet intact"); +}); + +test("insertNextSection synthesizes a TBD placeholder when the cycle has no section yet", () => { + const out = insertNextSection(MAIN, null, "3.8.45"); + assert.ok(out.includes("## [3.8.45] — TBD")); + assert.ok(out.indexOf("## [3.8.45]") < out.indexOf("## [3.8.44]")); +}); + +test("insertNextSection replaces a pre-existing copy of the next section instead of duplicating", () => { + const withNext = insertNextSection(MAIN, CYCLE_SECTION, "3.8.45"); + const again = insertNextSection(withNext, CYCLE_SECTION, "3.8.45"); + const count = (again.match(/## \[3\.8\.45\]/g) || []).length; + assert.equal(count, 1, "exactly one [3.8.45] heading after re-run (idempotent)"); +}); + +test("extractSection returns header through the line before the next heading, trimming the separator", () => { + const withNext = insertNextSection(MAIN, CYCLE_SECTION, "3.8.45"); + const section = extractSection(withNext, "3.8.45"); + assert.ok(section, "section found"); + assert.ok(section.startsWith("## [3.8.45]")); + assert.ok(section.includes("cycle bullet accumulated")); + assert.ok(!section.includes("## [3.8.44]"), "does not bleed into the next section"); + assert.ok(!section.trimEnd().endsWith("---"), "trailing separator trimmed"); +}); + +test("extractSection returns null when the version has no section", () => { + assert.equal(extractSection(MAIN, "9.9.9"), null); +}); From 0757503eaeea5c4795b7c02e76df38ce9266c0f7 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Date: Sat, 4 Jul 2026 21:21:23 -0300 Subject: [PATCH 003/749] =?UTF-8?q?perf(test):=20tsx/esm=20loader=20+=20ts?= =?UTF-8?q?x=204.23=20+=20=C3=B3rf=C3=A3os=20recuperados=20+=20CI=20via=20?= =?UTF-8?q?npm=20scripts=20(plano=20testes+CI,=20Pacote=201)=20(#6214)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * perf(test): tsx/esm loader, tsx 4.23 bump, orphan tests recovered, CI runs npm scripts Pacote 1 (quick wins) do plano mestre testes+CI: - Swap --import tsx -> --import tsx/esm on the 19 test scripts: the repo is pure ESM and the CJS hook costs ~1.3s PER test process (2,462 processes/run). Measured: bootstrap 2.3s -> 1.1s; real-suite A/B (tests/unit/db, 12 files) 22.2s -> 14.1s wall (-36%), 82/82 pass. Non-test scripts keep the full hook. - Bump tsx ^4.22.3 -> ^4.23.0 (fix for privatenumber/tsx#809 startup regression; helps module resolution on big graphs — hook cost unchanged, honest note). - Recover 22 ORPHAN test files (tests/unit/feature-triage/*.test.mjs, 53 cases, 53/53 pass) that matched no glob and ran in NO CI job; drop the dead 'executors' dir from the braces glob. - Single source of truth for the unit-suite invocation: new test:unit:ci:shard (shard via TEST_SHARD env) called by ci.yml test-unit/node24/node26/coverage and quality.yml fast-unit — closing two silent drifts: CI was NOT importing setupPolyfill.ts, and the fast path glob OMITTED tests/unit/memory + usage. quality.yml TIA step + ci.yml test-integration get the tsx/esm swap only. Validation: full unit suite 21,153 tests / 21,135 pass / 13 skip (5 fails = known load-flake family, 10/10 green rerun isolated); vitest 237/237; smoke db+feature-triage 135/135. Record run 2 = ci.yml workflow_dispatch on the stacked pacote-2 branch (clean runners). * fix(test): dashboard UI tests keep full tsx hook; recover 15 more .mjs orphans; extend discovery gate to .mjs Follow-up do dispatch de validacao (run 28720431562), que pegou 2 problemas reais: 1. tests/unit/dashboard/** (11 arquivos, 102 casos) importam componentes React cujo grafo puxa @lobehub/icons — o build es/ dele faz require() interno de arquivos com sintaxe ESM, que so funciona com o patch CJS do tsx (sem ele: SyntaxError 'Unexpected token export' no CI; local vira crawl de ~60s/arquivo). Esses 11 arquivos rodam agora numa 2a invocacao com --import tsx COMPLETO (mesmo shard), e o resto da suite mantem tsx/esm (-50% bootstrap). Validado: 102/102. 2. check:test-discovery falhou porque ancorava textualmente os globs nos workflows — COLLECTORS atualizado p/ o modelo fonte-unica (ancora = nome do script test:unit:ci:shard nos workflows) + varredura ESTENDIDA a .test.mjs, que era o ponto cego que deixou os orfaos apodrecerem. A extensao revelou +15 orfaos .mjs (top-level + db/) alem dos 22 de feature-triage — TODOS religados via glob tests/unit/**/*.test.mjs (171/171 pass). Um deles (encryption-error-handling) codificava o contrato PRE-hardening (decrypt falho retornava ciphertext cru — vazamento); alinhado ao contrato shipped (null + log) com comentario. Gate: [test-discovery] OK — 2892 arquivos, 22 collectors, 60 orfaos congelados (divida rastreada, shrink-only). --- .github/workflows/ci.yml | 26 +++++++++--- .github/workflows/quality.yml | 14 +++---- package-lock.json | 8 ++-- package.json | 42 ++++++++++--------- scripts/check/check-test-discovery.mjs | 39 +++++++++++++---- .../db/encryption-error-handling.test.mjs | 21 +++++++--- 6 files changed, 100 insertions(+), 50 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cf377f2ae3..d43f7759e5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -624,7 +624,11 @@ jobs: cache: npm - uses: ./.github/actions/npm-ci-retry - run: npm run check:node-runtime - - run: node --max-old-space-size=4096 --import tsx --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 --test-shard=${{ matrix.shard }}/8 tests/unit/*.test.ts "tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,executors,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts" + # QW-d (plano mestre): fonte única — o MESMO npm script dos runs locais (adiciona o + # setupPolyfill que o comando inline omitia; o glob canônico vive só no package.json). + - run: npm run test:unit:ci:shard + env: + TEST_SHARD: ${{ matrix.shard }}/8 test-vitest: name: Vitest (MCP / autoCombo / UI components) @@ -676,7 +680,10 @@ jobs: cache: npm - uses: ./.github/actions/npm-ci-retry - run: npm run check:node-runtime - - run: node --max-old-space-size=4096 --import tsx --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 --test-shard=${{ matrix.shard }}/4 tests/unit/*.test.ts "tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,executors,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts" + # QW-d: fonte única — mesmo npm script dos runs locais (inclui setupPolyfill; glob no package.json). + - run: npm run test:unit:ci:shard + env: + TEST_SHARD: ${{ matrix.shard }}/4 node-26-compat-build: name: Node 26 Compatibility Build @@ -729,7 +736,10 @@ jobs: cache: npm - uses: ./.github/actions/npm-ci-retry - run: npm run check:node-runtime - - run: node --max-old-space-size=4096 --import tsx --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 --test-shard=${{ matrix.shard }}/4 tests/unit/*.test.ts "tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,executors,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts" + # QW-d: fonte única — mesmo npm script dos runs locais (inclui setupPolyfill; glob no package.json). + - run: npm run test:unit:ci:shard + env: + TEST_SHARD: ${{ matrix.shard }}/4 test-coverage-shard: name: Coverage Shard (${{ matrix.shard }}/8) @@ -755,6 +765,8 @@ jobs: - uses: ./.github/actions/npm-ci-retry - run: npm run check:node-runtime - name: Run c8 over shard ${{ matrix.shard }}/8 + env: + TEST_SHARD: ${{ matrix.shard }}/8 run: | rm -rf coverage-shard coverage-shard-report # `--temp-directory` (writable via NODE_V8_COVERAGE) is what the merge @@ -763,14 +775,15 @@ jobs: # `coverage/tmp`, so uploading `coverage-shard/` was empty. Pin the temp # dir so the raw coverage files live there and the artifact upload picks # them up regardless of `--test-force-exit` timing. + # QW-d: o comando de teste é o MESMO npm script dos runs locais (c8 propaga + # NODE_V8_COVERAGE aos filhos através do npm; node_modules é excluído por default). npx c8 \ --temp-directory=coverage-shard \ --reports-dir=coverage-shard-report \ --reporter=json \ --exclude=tests/** \ --exclude=**/*.test.* \ - node --max-old-space-size=4096 --import tsx --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 \ - --test-shard=${{ matrix.shard }}/8 tests/unit/*.test.ts "tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,executors,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts" + npm run test:unit:ci:shard - name: Upload raw shard coverage if: always() uses: actions/upload-artifact@v7 @@ -1061,7 +1074,8 @@ jobs: cache: npm - uses: ./.github/actions/npm-ci-retry - run: npm run check:node-runtime - - run: node --import tsx --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 --test-shard=${{ matrix.shard }}/2 tests/integration/*.test.ts + # (tsx/esm = QW-b; o alinhamento de ESCOPO do integration com o npm script fica p/ follow-up) + - run: node --import tsx/esm --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 --test-shard=${{ matrix.shard }}/2 tests/integration/*.test.ts test-security: name: Security Tests diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index e578122d0b..cb8c976d5c 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -100,7 +100,7 @@ jobs: fi echo "Running impacted tests:"; echo "$SEL" mapfile -t FILES <<< "$SEL" - node --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 "${FILES[@]}" + node --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 "${FILES[@]}" fast-vitest: name: Vitest (fast-path) @@ -140,9 +140,9 @@ jobs: node-version: ${{ env.CI_NODE_VERSION }} cache: npm - run: npm ci - - run: > - node --max-old-space-size=4096 --import tsx - --import ./tests/_setup/isolateDataDir.ts - --test --test-force-exit --test-concurrency=4 --test-shard=${{ matrix.shard }}/2 - tests/unit/*.test.ts - "tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,executors,gamification,guardrails,lib,mcp,runtime,security,services,settings,shared,ui}/**/*.test.ts" + # QW-d: fonte única — o mesmo npm script do CI pesado/local. Fecha dois drifts do + # comando inline antigo: os dirs `memory` e `usage` estavam FORA do glob (testes + # silenciosamente não rodavam no fast path) e o setupPolyfill não era importado. + - run: npm run test:unit:ci:shard + env: + TEST_SHARD: ${{ matrix.shard }}/2 diff --git a/package-lock.json b/package-lock.json index c21becc134..a9c4af7c2d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -77,7 +77,7 @@ "sql.js": "^1.14.1", "sqlite-vec": "^0.1.9", "tailwind-merge": "^3.6.0", - "tsx": "^4.22.3", + "tsx": "^4.23.0", "undici": "^8.3.0", "update-notifier": "^7.3.1", "uuid": "^14.0.0", @@ -26762,9 +26762,9 @@ "license": "0BSD" }, "node_modules/tsx": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.4.tgz", - "integrity": "sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.0.tgz", + "integrity": "sha512-eUdUIaCr963q2h5u3+QwvYp0+eqPvn+egeqZUm0hwERCqqx1E3kK5ehbGCvqSE5MQAULr67ww0cA3jKc3YkM1w==", "license": "MIT", "dependencies": { "esbuild": "~0.28.0" diff --git a/package.json b/package.json index 9fd05c2d37..630617b493 100644 --- a/package.json +++ b/package.json @@ -96,17 +96,18 @@ "electron:build:mac": "npm run build && cd electron && npm run build:mac", "electron:build:linux": "npm run build && cd electron && npm run build:linux", "electron:smoke:packaged": "node scripts/dev/smoke-electron-packaged.mjs", - "test": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-concurrency=20 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,executors,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\"", - "test:unit": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=20 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,executors,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\"", - "test:unit:ci": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,executors,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\"", - "test:unit:fast": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-isolation=none tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,executors,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\"", + "test": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-concurrency=20 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-concurrency=20 \"tests/unit/dashboard/**/*.test.ts\"", + "test:unit": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=20 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=20 \"tests/unit/dashboard/**/*.test.ts\"", + "test:unit:ci": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 \"tests/unit/dashboard/**/*.test.ts\"", + "test:unit:ci:shard": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=4096 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 --test-shard=$TEST_SHARD tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=4096 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=4 --test-shard=$TEST_SHARD \"tests/unit/dashboard/**/*.test.ts\"", + "test:unit:fast": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-isolation=none tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-isolation=none \"tests/unit/dashboard/**/*.test.ts\"", "test:unit:shard": "concurrently --kill-others-on-fail -n s1,s2 \"npm:test:unit:shard:1\" \"npm:test:unit:shard:2\"", - "test:unit:shard:1": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=10 --test-shard=1/2 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,executors,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\"", - "test:unit:shard:2": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=10 --test-shard=2/2 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,executors,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\"", - "test:plan3": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test tests/unit/plan3-p0.test.ts", - "test:fixes": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test tests/unit/fixes-p1.test.ts", - "test:security": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test tests/unit/security-fase01.test.ts", - "test:property": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx --test --test-force-exit tests/unit/correctness/*.property.test.ts", + "test:unit:shard:1": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=10 --test-shard=1/2 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=10 --test-shard=1/2 \"tests/unit/dashboard/**/*.test.ts\"", + "test:unit:shard:2": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=10 --test-shard=2/2 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=10 --test-shard=2/2 \"tests/unit/dashboard/**/*.test.ts\"", + "test:plan3": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test tests/unit/plan3-p0.test.ts", + "test:fixes": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test tests/unit/fixes-p1.test.ts", + "test:security": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test tests/unit/security-fase01.test.ts", + "test:property": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx/esm --test --test-force-exit tests/unit/correctness/*.property.test.ts", "check:cycles": "node scripts/check/check-cycles.mjs", "check:route-validation:t06": "node scripts/check/check-route-validation.mjs", "check:any-budget:t11": "node scripts/check/check-t11-any-budget.mjs", @@ -178,22 +179,22 @@ "typecheck:noimplicit:core": "tsc --pretty false -p tsconfig.typecheck-noimplicit-core.json", "backfill-aggregation": "node --import tsx src/scripts/backfillAggregation.ts", "env:sync": "node scripts/dev/sync-env.mjs", - "test:integration": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 tests/integration/*.test.ts \"tests/integration/combo-matrix/*.test.ts\"", - "test:combo:matrix": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 \"tests/integration/combo-matrix/*.test.ts\"", - "test:combo:live": "cross-env RUN_COMBO_LIVE=1 DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 \"tests/integration/combo-live/*.live.test.ts\"", + "test:integration": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 tests/integration/*.test.ts \"tests/integration/combo-matrix/*.test.ts\"", + "test:combo:matrix": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 \"tests/integration/combo-matrix/*.test.ts\"", + "test:combo:live": "cross-env RUN_COMBO_LIVE=1 DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 \"tests/integration/combo-live/*.live.test.ts\"", "test:combo:live:vps": "node scripts/test/combo-live-vps.mjs", "test:combo:live:vps:failover": "node scripts/test/combo-live-vps.mjs --failover", - "test:heap": "node --expose-gc --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit tests/integration/heap-growth.test.ts", - "test:chaos": "cross-env RUN_CHAOS_INT=1 node --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 tests/integration/resilience-chaos.test.ts", + "test:heap": "node --expose-gc --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit tests/integration/heap-growth.test.ts", + "test:chaos": "cross-env RUN_CHAOS_INT=1 node --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 tests/integration/resilience-chaos.test.ts", "test:e2e": "node scripts/dev/run-playwright-tests.mjs test tests/e2e/*.spec.ts", "test:protocols:e2e": "node scripts/dev/run-protocol-clients-tests.mjs", "test:vitest": "vitest run --config vitest.mcp.config.ts", "test:vitest:ui": "vitest run --config vitest.config.ts tests/unit/ui", "test:mutation": "stryker run", "test:ecosystem": "node scripts/dev/run-ecosystem-tests.mjs", - "test:system": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 tests/e2e/system-failover.test.ts", - "test:coverage": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true NODE_OPTIONS=--max-old-space-size=8192 c8 --merge-async --output-dir coverage --exclude=tests/** --exclude=**/*.test.* --reporter=text-summary --reporter=html --reporter=json-summary --reporter=lcov --check-coverage --statements 60 --lines 60 --functions 60 --branches 60 node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=8 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,executors,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\"", - "test:coverage:legacy": "c8 --output-dir coverage --exclude=open-sse --check-coverage --lines 50 --functions 50 --branches 50 node --import tsx --test tests/unit/*.test.ts", + "test:system": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true node --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=1 tests/e2e/system-failover.test.ts", + "test:coverage": "cross-env DISABLE_SQLITE_AUTO_BACKUP=true NODE_OPTIONS=--max-old-space-size=8192 c8 --merge-async --output-dir coverage --exclude=tests/** --exclude=**/*.test.* --reporter=text-summary --reporter=html --reporter=json-summary --reporter=lcov --check-coverage --statements 60 --lines 60 --functions 60 --branches 60 npm run test:coverage:runner", + "test:coverage:legacy": "c8 --output-dir coverage --exclude=open-sse --check-coverage --lines 50 --functions 50 --branches 50 node --import tsx/esm --test tests/unit/*.test.ts", "coverage:report": "cross-env NODE_OPTIONS=--max-old-space-size=8192 c8 report --merge-async --output-dir coverage --exclude=tests/** --exclude=**/*.test.* --reporter=text --reporter=text-summary --reporter=html --reporter=json-summary --reporter=lcov", "coverage:summary": "node scripts/check/test-report-summary.mjs --input coverage/coverage-summary.json --output coverage/coverage-report.md", "check:pr-test-policy": "node scripts/check/check-pr-test-policy.mjs", @@ -208,7 +209,8 @@ "system-info": "node scripts/dev/system-info.mjs", "build:cli-api": "node --import tsx/esm scripts/cli/generate-api-commands.mjs", "release:contributors": "node scripts/release/gen-contributors.mjs", - "release:uncovered": "node scripts/release/list-uncovered-commits.mjs" + "release:uncovered": "node scripts/release/list-uncovered-commits.mjs", + "test:coverage:runner": "node --max-old-space-size=8192 --import tsx/esm --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=8 tests/unit/*.test.ts \"tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts\" \"tests/unit/**/*.test.mjs\" && cross-env DISABLE_SQLITE_AUTO_BACKUP=true NODE_OPTIONS=--max-old-space-size=8192 c8 --merge-async --output-dir coverage --exclude=tests/** --exclude=**/*.test.* --reporter=text-summary --reporter=html --reporter=json-summary --reporter=lcov --check-coverage --statements 60 --lines 60 --functions 60 --branches 60 node --max-old-space-size=8192 --import tsx --import ./open-sse/utils/setupPolyfill.ts --import ./tests/_setup/isolateDataDir.ts --test --test-force-exit --test-concurrency=8 \"tests/unit/dashboard/**/*.test.ts\"" }, "dependencies": { "@aws-sdk/client-bedrock-runtime": "^3.1073.0", @@ -275,7 +277,7 @@ "sql.js": "^1.14.1", "sqlite-vec": "^0.1.9", "tailwind-merge": "^3.6.0", - "tsx": "^4.22.3", + "tsx": "^4.23.0", "undici": "^8.3.0", "update-notifier": "^7.3.1", "uuid": "^14.0.0", diff --git a/scripts/check/check-test-discovery.mjs b/scripts/check/check-test-discovery.mjs index 4c0efe5ba8..144157122b 100644 --- a/scripts/check/check-test-discovery.mjs +++ b/scripts/check/check-test-discovery.mjs @@ -41,20 +41,45 @@ const UPDATE = process.argv.includes("--update"); // Raízes varridas em busca de arquivos de teste. const WALK_ROOTS = ["tests", "src", "open-sse", "electron", "bin"]; const WALK_EXCLUDE = new Set(["node_modules", ".next", "dist", "coverage", ".git"]); -const TEST_FILE_RE = /\.(test|spec)\.(ts|tsx)$/; +const TEST_FILE_RE = /\.(test|spec)\.(ts|tsx|mjs)$/; // Runners REAIS e seus globs. `sources`: arquivos onde `anchor` (default: o próprio // glob) deve aparecer textualmente — se o runner mudar, este gate exige o sync. export const COLLECTORS = [ - // Node native runner — test:unit / test:unit:fast / shards / test:coverage + CI (8 shards, node24, node26) - { glob: "tests/unit/*.test.ts", sources: ["package.json", ".github/workflows/ci.yml"] }, + // Node native runner — test:unit / test:unit:fast / shards / test:coverage. O CI + // (test-unit ×8 + quality.yml fast-unit) agora chama o npm script test:unit:ci:shard + // (fonte única, plano mestre testes+CI QW-d) — o wiring é ancorado pelo NOME do script + // nos workflows (entrada dedicada abaixo), e os globs vivem SÓ no package.json. + { glob: "tests/unit/*.test.ts", sources: ["package.json"] }, // Node native runner — subdiretórios religados pela 6A.1c (2026-06-09). Braces // explícitos para NÃO incluir tests/unit/autoCombo/** (testes vitest — importam - // "vitest" e explodem no node runner). Subdir novo: adicione aqui E nos scripts - // (o drift-check + o gate de órfãos forçam a manutenção em sincronia). + // "vitest" e explodem no node runner) NEM tests/unit/dashboard/** (invocação própria + // abaixo). Subdir novo: adicione aqui E nos scripts (o drift-check + o gate de + // órfãos forçam a manutenção em sincronia). { - glob: "tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,dashboard,db,db-adapters,docs,executors,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts", - sources: ["package.json", ".github/workflows/ci.yml"], + glob: "tests/unit/{api,auth,authz,build,cli,cli-helper,combo,compression,correctness,cors,db,db-adapters,docs,gamification,guardrails,lib,mcp,memory,runtime,security,services,settings,shared,ui,usage}/**/*.test.ts", + sources: ["package.json"], + }, + // Node native runner — tests/unit/dashboard/** roda numa invocação separada com o hook + // COMPLETO do tsx (--import tsx): o grafo dos componentes de dashboard puxa + // @lobehub/icons, cujo build es/ faz require() interno de arquivos com sintaxe ESM — + // sem o patch CJS do tsx isso estoura "Unexpected token 'export'" (visto no Node + // 24.18 do CI; no 24.16 local vira um crawl de ~60s/arquivo). O resto da suíte roda + // sob tsx/esm (~-50% de bootstrap por processo). Plano mestre testes+CI, QW-b. + { glob: "tests/unit/dashboard/**/*.test.ts", sources: ["package.json"] }, + // Órfãos religados (plano mestre QW-c): arquivos .test.mjs (top-level + db/ + feature-triage/) — fora do glob + // *.test.ts histórico, nunca rodava em job nenhum (53 casos recuperados). + { glob: "tests/unit/**/*.test.mjs", sources: ["package.json"] }, + // Wiring CI→npm script (fonte única): os jobs de unit do ci.yml e o fast-unit do + // quality.yml DEVEM invocar o script canônico — se renomearem/inlinarem, este gate + // exige o sync (substitui as âncoras textuais de glob que existiam nos workflows). + { + glob: "tests/unit/*.test.ts", + sources: ["package.json", ".github/workflows/ci.yml", ".github/workflows/quality.yml"], + anchors: { + ".github/workflows/ci.yml": "test:unit:ci:shard", + ".github/workflows/quality.yml": "test:unit:ci:shard", + }, }, // Node native runner — test:integration (top-level only; tests/integration/services/ NÃO roda) { glob: "tests/integration/*.test.ts", sources: ["package.json"] }, diff --git a/tests/unit/db/encryption-error-handling.test.mjs b/tests/unit/db/encryption-error-handling.test.mjs index e40e531ed7..2b6f74cc41 100644 --- a/tests/unit/db/encryption-error-handling.test.mjs +++ b/tests/unit/db/encryption-error-handling.test.mjs @@ -1,20 +1,29 @@ import { test } from "node:test"; import assert from "node:assert"; -import { decrypt } from "../../../src/lib/db/encryption.ts"; -test("decrypt() with invalid auth tag should not crash and return ciphertext", () => { +// Chave determinística p/ exercitar o caminho real de auth-tag (sem ela o decrypt +// curto-circuita no branch "no key"). Definida ANTES do import — a derivação de chave +// é cacheada lazy no primeiro uso. +process.env.STORAGE_ENCRYPTION_KEY = "unit-test-storage-encryption-key-0123456789"; + +const { decrypt } = await import("../../../src/lib/db/encryption.ts"); + +// Contrato atual (hardening): falha de decrypt → null + log, NUNCA o ciphertext cru +// (devolver o blob criptografado vazaria o dado cifrado para UI/API). Este arquivo era +// um órfão de glob (nunca rodou em CI) e codificava o contrato antigo pré-hardening — +// alinhado ao comportamento real/shipped em 2026-07 (plano mestre testes+CI, QW-c). +test("decrypt() with invalid auth tag should not crash and return null", () => { const invalidCiphertext = "enc:v1:0000:0000:0000"; const result = decrypt(invalidCiphertext); - assert.strictEqual(result, invalidCiphertext, "Should return ciphertext unchanged on error"); - assert.strictEqual(typeof result, "string", "Result should be a string"); + assert.strictEqual(result, null, "Failed auth-tag decrypt must return null (never the raw blob)"); }); -test("decrypt() with malformed ciphertext should not crash", () => { +test("decrypt() with malformed ciphertext should not crash and return null", () => { const malformed = "enc:v1:invalid"; const result = decrypt(malformed); - assert.strictEqual(result, malformed, "Should return malformed ciphertext unchanged"); + assert.strictEqual(result, null, "Malformed encrypted value must return null"); }); test("decrypt() with null should return null", () => { From 5a4bde1879ec9d86bc896f92c7fffb58688698c6 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Date: Sat, 4 Jul 2026 21:24:10 -0300 Subject: [PATCH 004/749] =?UTF-8?q?ci:=20dedup=20heavy=20pipeline=20?= =?UTF-8?q?=E2=80=94=20compat=20to=20nightly,=20coverage=20folded=20into?= =?UTF-8?q?=20unit=20shards,=20i18n=20single=20job,=20draft-skip=20(#6215)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pacote 2+3-ci do plano mestre testes+CI (aprovado 2026-07-04). O CI pesado rodava a suite unit 4x por sync da release-PR (95 jobs, 208 min-maquina) e o ciclo v3.8.44 disparou 123 desses runs (88 cancelados, 0 uteis) porque a release-PR viva fica aberta o ciclo inteiro. - D2: matrizes Node 24/26 (build + 8 jobs de teste, ~28% do custo por run) saem do per-sync e viram .github/workflows/nightly-compat.yml (diario, fail-fast off, resolve a release ativa como o nightly-release-green, abre issue de tracking em falha). ci.yml/ci-summary limpos das referencias. - D3: a matrix Coverage Shard x8 (~18% do custo) e eliminada — o job test-unit roda os MESMOS shards sob c8/NODE_V8_COVERAGE e sobe os artifacts coverage-shard-N; o job de merge (test-coverage) so repontou needs (padrao do CI do nodejs/node). timeout test-unit 15->25min pelo overhead de instrumentacao. - D4: a matrix i18n de ~40 jobs de <1min (saturava sozinha os 20 slots de concorrencia da conta Free) vira 1 job que itera os idiomas com ::group:: por idioma e artifact unico com resultados nomeados por idioma (antes 40 result.txt colidiam no merge-multiple do ci-summary). - P3: jobs pesados pulam pull_requests DRAFT (predicado em 10 jobs-raiz; o resto pula pela cadeia de needs; ci-summary segue rodando como sinal unico) — a skill /generate-release ja abre a release-PR viva como draft e flipa ready no 0a.0a (commit eb04fc5 no repo .agents/skills). - C5 (CodeQL schedule) NAO incluido: bloqueado na acao do dono Settings -> CodeQL Default->Advanced (documentado no proprio codeql.yml). Validacao: js-yaml parse ok; check:workflows zizmor 156 < baseline 159 (ratchet verde); validacao de execucao = workflow_dispatch deste ci.yml neste branch ate package-artifact + electron-package-smoke verdes (registrada no PR). --- .github/workflows/ci.yml | 259 ++++++++------------------- .github/workflows/nightly-compat.yml | 126 +++++++++++++ 2 files changed, 202 insertions(+), 183 deletions(-) create mode 100644 .github/workflows/nightly-compat.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d43f7759e5..988f844aaf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,6 +92,9 @@ jobs: lint: name: Lint runs-on: ubuntu-latest + # P3 (plano mestre): a release-PR viva fica DRAFT o ciclo inteiro — jobs pesados pulam + # drafts (ciclo v3.8.44: 123 runs pesados re-disparados por merges na release, 88 cancelados). + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.draft == false }} env: # tsx gates below (known-symbols, route-guard-membership) import modules that # open SQLite on load; provide DB env so a fresh CI DB initializes cleanly. @@ -148,7 +151,7 @@ jobs: # release PR #4854, where the drift cascade only surfaced post-merge in #5029). # The coverage.* metrics degrade gracefully: the download is continue-on-error and # the ratchet runs with --allow-missing, so absent coverage is skipped, not failed. - if: ${{ !cancelled() }} + if: ${{ !cancelled() && (github.event_name != 'pull_request' || github.event.pull_request.draft == false) }} # security-events: read lets the CodeQL ratchet read open code-scanning alerts # via `gh api .../code-scanning/alerts`. contents: read keeps checkout working. permissions: @@ -240,6 +243,9 @@ jobs: quality-extended: name: Quality Gates (Extended) runs-on: ubuntu-latest + # P3 (plano mestre): a release-PR viva fica DRAFT o ciclo inteiro — jobs pesados pulam + # drafts (ciclo v3.8.44: 123 runs pesados re-disparados por merges na release, 88 cancelados). + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.draft == false }} steps: # fetch-depth: 0 — the OpenAPI breaking-change gate (oasdiff) reads the base # spec via `git show :docs/openapi.yaml`; a shallow clone @@ -347,6 +353,9 @@ jobs: docs-sync-strict: name: Docs Sync (Strict) runs-on: ubuntu-latest + # P3 (plano mestre): a release-PR viva fica DRAFT o ciclo inteiro — jobs pesados pulam + # drafts (ciclo v3.8.44: 123 runs pesados re-disparados por merges na release, 88 cancelados). + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.draft == false }} steps: - uses: actions/checkout@v7 with: @@ -376,6 +385,9 @@ jobs: docs-lint: name: Docs Lint (prose — advisory) runs-on: ubuntu-latest + # P3 (plano mestre): a release-PR viva fica DRAFT o ciclo inteiro — jobs pesados pulam + # drafts (ciclo v3.8.44: 123 runs pesados re-disparados por merges na release, 88 cancelados). + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.draft == false }} # Advisory (warning-first): prose/markdown style must not block merges while the # existing doc corpus is brought up to style. Promote to blocking once it converges. continue-on-error: true @@ -402,6 +414,9 @@ jobs: i18n-ui-coverage: name: i18n UI Coverage runs-on: ubuntu-latest + # P3 (plano mestre): a release-PR viva fica DRAFT o ciclo inteiro — jobs pesados pulam + # drafts (ciclo v3.8.44: 123 runs pesados re-disparados por merges na release, 88 cancelados). + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.draft == false }} steps: - uses: actions/checkout@v7 with: @@ -413,30 +428,18 @@ jobs: - uses: ./.github/actions/npm-ci-retry - run: node scripts/i18n/check-ui-keys-coverage.mjs --threshold=65 - i18n-matrix: - name: Build language matrix - runs-on: ubuntu-latest - outputs: - langs: ${{ steps.langs.outputs.langs }} - steps: - - uses: actions/checkout@v7 - with: - persist-credentials: false - - id: langs - run: | - LANG_DIR="src/i18n/messages" - LANGS=$(ls "$LANG_DIR"/*.json | xargs -n1 basename | sed 's/.json$//' | grep -v '^en$' | jq -R . | jq -s . | jq -c .) - echo "langs=${LANGS}" >> "$GITHUB_OUTPUT" - + # D4 (plano mestre testes+CI): a matrix de ~40 jobs de <1min por idioma saturava sozinha + # a concorrência de jobs da conta (Free = 20 slots, compartilhados entre TODOS os repos) + # e pagava spin-up + arredondamento de billing por idioma. Um único job itera os idiomas + # (mesmo script), com grupo de log por idioma e artifact único de resultados nomeados por + # idioma (a matrix antiga subia 40 artifacts cujo result.txt colidia no merge-multiple). i18n: - name: i18n Validation + name: i18n Validation (all languages) runs-on: ubuntu-latest + # P3 (plano mestre): a release-PR viva fica DRAFT o ciclo inteiro — jobs pesados pulam + # drafts (ciclo v3.8.44: 123 runs pesados re-disparados por merges na release, 88 cancelados). + if: ${{ github.event_name != 'pull_request' || github.event.pull_request.draft == false }} continue-on-error: true - strategy: - fail-fast: false - matrix: - lang: ${{ fromJson(needs.i18n-matrix.outputs.langs) }} - needs: i18n-matrix steps: - uses: actions/checkout@v7 with: @@ -444,27 +447,35 @@ jobs: - uses: actions/setup-python@v6 with: python-version: "3.12" - - - name: Validate ${{ matrix.lang }} - env: - # Pass the matrix value via env (never interpolate ${{ ... }} straight - # into the run: script body) so the shell receives a variable, not - # inlined text — zizmor template-injection mitigation. Named MATRIX_LANG - # to avoid clobbering the POSIX `LANG` locale variable. - MATRIX_LANG: ${{ matrix.lang }} + - name: Validate all languages run: | - python3 scripts/i18n/validate_translation.py quick -l "$MATRIX_LANG" > result.txt - - - name: Upload result + set -uo pipefail + mkdir -p i18n-results + FAIL=0 + for f in src/i18n/messages/*.json; do + lang=$(basename "$f" .json) + [ "$lang" = "en" ] && continue + echo "::group::i18n $lang" + if python3 scripts/i18n/validate_translation.py quick -l "$lang" > "i18n-results/$lang.txt" 2>&1; then + echo "OK $lang" + else + FAIL=1 + echo "FAIL $lang" + cat "i18n-results/$lang.txt" + fi + echo "::endgroup::" + done + exit "$FAIL" + - name: Upload results if: always() uses: actions/upload-artifact@v7 with: - name: i18n-${{ matrix.lang }} - path: result.txt + name: i18n-results + path: i18n-results/ pr-test-policy: name: PR Test Policy - if: ${{ github.event_name == 'pull_request' }} + if: ${{ github.event_name == 'pull_request' && github.event.pull_request.draft == false }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 @@ -497,7 +508,7 @@ jobs: name: Build runs-on: ubuntu-latest needs: changes - if: ${{ github.event_name != 'pull_request' || needs.changes.outputs.code == 'true' }} + if: ${{ github.event_name != 'pull_request' || (needs.changes.outputs.code == 'true' && github.event.pull_request.draft == false) }} steps: - uses: actions/checkout@v7 with: @@ -604,7 +615,7 @@ jobs: test-unit: name: Unit Tests (${{ matrix.shard }}/8) runs-on: ubuntu-latest - timeout-minutes: 15 + timeout-minutes: 25 needs: build strategy: fail-fast: false @@ -626,9 +637,28 @@ jobs: - run: npm run check:node-runtime # QW-d (plano mestre): fonte única — o MESMO npm script dos runs locais (adiciona o # setupPolyfill que o comando inline omitia; o glob canônico vive só no package.json). - - run: npm run test:unit:ci:shard + # D3 (plano mestre): a coverage é coletada NESTE mesmo run (c8/NODE_V8_COVERAGE propaga + # aos filhos através do npm) — elimina a matrix Coverage Shard ×8, que re-executava a + # suíte inteira só para medir o gate. Padrão usado pelo CI do próprio nodejs/node. + - name: Unit tests (shard ${{ matrix.shard }}/8) with V8 coverage env: TEST_SHARD: ${{ matrix.shard }}/8 + run: | + rm -rf coverage-shard coverage-shard-report + npx c8 \ + --temp-directory=coverage-shard \ + --reports-dir=coverage-shard-report \ + --reporter=json \ + --exclude=tests/** \ + --exclude=**/*.test.* \ + npm run test:unit:ci:shard + - name: Upload raw shard coverage + if: always() + uses: actions/upload-artifact@v7 + with: + name: coverage-shard-${{ matrix.shard }} + path: coverage-shard/*.json + if-no-files-found: error test-vitest: name: Vitest (MCP / autoCombo / UI components) @@ -657,147 +687,16 @@ jobs: - run: npm run test:vitest:ui continue-on-error: true - node-24-compat: - name: Node 24 Compatibility Tests (${{ matrix.shard }}/4) - runs-on: ubuntu-latest - timeout-minutes: 20 - needs: build - strategy: - fail-fast: false - matrix: - shard: [1, 2, 3, 4] - env: - JWT_SECRET: ci-test-secret-with-sufficient-length-for-validation - API_KEY_SECRET: ci-test-api-key-secret-long - DISABLE_SQLITE_AUTO_BACKUP: "true" - steps: - - uses: actions/checkout@v7 - with: - persist-credentials: false - - uses: actions/setup-node@v6 - with: - node-version: ${{ env.CI_NODE_24_VERSION }} - cache: npm - - uses: ./.github/actions/npm-ci-retry - - run: npm run check:node-runtime - # QW-d: fonte única — mesmo npm script dos runs locais (inclui setupPolyfill; glob no package.json). - - run: npm run test:unit:ci:shard - env: - TEST_SHARD: ${{ matrix.shard }}/4 - - node-26-compat-build: - name: Node 26 Compatibility Build - runs-on: ubuntu-latest - timeout-minutes: 25 - needs: build - env: - JWT_SECRET: ci-test-secret-with-sufficient-length-for-validation - API_KEY_SECRET: ci-test-api-key-secret-long - DISABLE_SQLITE_AUTO_BACKUP: "true" - steps: - - uses: actions/checkout@v7 - with: - persist-credentials: false - - uses: actions/setup-node@v6 - with: - node-version: ${{ env.CI_NODE_26_VERSION }} - cache: npm - - uses: ./.github/actions/npm-ci-retry - - run: npm run check:node-runtime - - name: Cache Next.js build cache - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 - with: - path: .build/next/cache - key: nextjs-${{ runner.os }}-node-${{ env.CI_NODE_26_VERSION }}-${{ hashFiles('package-lock.json') }}-${{ hashFiles('src/**/*', 'open-sse/**/*', 'db/**/*', 'next.config.mjs', 'tsconfig*.json', 'postcss.config.*', 'tailwind.config.*') }} - restore-keys: | - nextjs-${{ runner.os }}-node-${{ env.CI_NODE_26_VERSION }}-${{ hashFiles('package-lock.json') }}- - - run: npm run build - - node-26-compat: - name: Node 26 Compatibility Tests (${{ matrix.shard }}/4) - runs-on: ubuntu-latest - timeout-minutes: 20 - needs: node-26-compat-build - strategy: - fail-fast: false - matrix: - shard: [1, 2, 3, 4] - env: - JWT_SECRET: ci-test-secret-with-sufficient-length-for-validation - API_KEY_SECRET: ci-test-api-key-secret-long - DISABLE_SQLITE_AUTO_BACKUP: "true" - steps: - - uses: actions/checkout@v7 - with: - persist-credentials: false - - uses: actions/setup-node@v6 - with: - node-version: ${{ env.CI_NODE_26_VERSION }} - cache: npm - - uses: ./.github/actions/npm-ci-retry - - run: npm run check:node-runtime - # QW-d: fonte única — mesmo npm script dos runs locais (inclui setupPolyfill; glob no package.json). - - run: npm run test:unit:ci:shard - env: - TEST_SHARD: ${{ matrix.shard }}/4 - - test-coverage-shard: - name: Coverage Shard (${{ matrix.shard }}/8) - runs-on: ubuntu-latest - timeout-minutes: 25 - needs: build - strategy: - fail-fast: false - matrix: - shard: [1, 2, 3, 4, 5, 6, 7, 8] - env: - JWT_SECRET: ci-test-secret-with-sufficient-length-for-validation - API_KEY_SECRET: ci-test-api-key-secret-long - DISABLE_SQLITE_AUTO_BACKUP: "true" - steps: - - uses: actions/checkout@v7 - with: - persist-credentials: false - - uses: actions/setup-node@v6 - with: - node-version: ${{ env.CI_NODE_VERSION }} - cache: npm - - uses: ./.github/actions/npm-ci-retry - - run: npm run check:node-runtime - - name: Run c8 over shard ${{ matrix.shard }}/8 - env: - TEST_SHARD: ${{ matrix.shard }}/8 - run: | - rm -rf coverage-shard coverage-shard-report - # `--temp-directory` (writable via NODE_V8_COVERAGE) is what the merge - # job reads with `c8 report --temp-directory ...`. Using `--output-dir` - # only produces the final json *report* and leaves the raw v8 files in - # `coverage/tmp`, so uploading `coverage-shard/` was empty. Pin the temp - # dir so the raw coverage files live there and the artifact upload picks - # them up regardless of `--test-force-exit` timing. - # QW-d: o comando de teste é o MESMO npm script dos runs locais (c8 propaga - # NODE_V8_COVERAGE aos filhos através do npm; node_modules é excluído por default). - npx c8 \ - --temp-directory=coverage-shard \ - --reports-dir=coverage-shard-report \ - --reporter=json \ - --exclude=tests/** \ - --exclude=**/*.test.* \ - npm run test:unit:ci:shard - - name: Upload raw shard coverage - if: always() - uses: actions/upload-artifact@v7 - with: - name: coverage-shard-${{ matrix.shard }} - path: coverage-shard/*.json - if-no-files-found: error - + # Node 24/26 compatibility matrices moved to .github/workflows/nightly-compat.yml + # (plano mestre testes+CI, Eixo D2 — they cost ~28% of every heavy run to catch a + # failure class that rarely originates in a PR; nightly catches it within 24h and + # the release gate can still exercise them via workflow_dispatch when needed). test-coverage: name: Coverage runs-on: ubuntu-latest timeout-minutes: 10 - needs: test-coverage-shard - if: ${{ !cancelled() && needs.test-coverage-shard.result == 'success' }} + needs: test-unit + if: ${{ !cancelled() && needs.test-unit.result == 'success' }} env: JWT_SECRET: ci-test-secret-with-sufficient-length-for-validation API_KEY_SECRET: ci-test-api-key-secret-long @@ -928,7 +827,7 @@ jobs: coverage-pr-comment: name: PR Coverage Comment runs-on: ubuntu-latest - if: ${{ !cancelled() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false && needs.changes.outputs.code == 'true' }} + if: ${{ !cancelled() && github.event_name == 'pull_request' && github.event.pull_request.draft == false && github.event.pull_request.head.repo.fork == false && needs.changes.outputs.code == 'true' }} needs: - changes - pr-test-policy @@ -1112,9 +1011,6 @@ jobs: - package-artifact - electron-package-smoke - test-unit - - node-24-compat - - node-26-compat-build - - node-26-compat - test-coverage - sonarqube - coverage-pr-comment @@ -1164,15 +1060,12 @@ jobs: echo "| Build Matrix | $(status '${{ needs.build.result }}') |" >> "$GITHUB_STEP_SUMMARY" echo "| Package Artifact | $(status '${{ needs.package-artifact.result }}') |" >> "$GITHUB_STEP_SUMMARY" echo "| Electron Package Smoke | $(status '${{ needs.electron-package-smoke.result }}') |" >> "$GITHUB_STEP_SUMMARY" - echo "| Node 26 Compatibility Build | $(status '${{ needs.node-26-compat-build.result }}') |" >> "$GITHUB_STEP_SUMMARY" echo "" >> "$GITHUB_STEP_SUMMARY" echo "## 🧪 Tests" >> "$GITHUB_STEP_SUMMARY" echo "| Suite | Status |" >> "$GITHUB_STEP_SUMMARY" echo "|-------|--------|" >> "$GITHUB_STEP_SUMMARY" echo "| Unit | $(status '${{ needs.test-unit.result }}') |" >> "$GITHUB_STEP_SUMMARY" - echo "| Node 24 Compatibility Tests | $(status '${{ needs.node-24-compat.result }}') |" >> "$GITHUB_STEP_SUMMARY" - echo "| Node 26 Compatibility Tests | $(status '${{ needs.node-26-compat.result }}') |" >> "$GITHUB_STEP_SUMMARY" echo "| Coverage | $(status '${{ needs.test-coverage.result }}') |" >> "$GITHUB_STEP_SUMMARY" echo "| PR Coverage Comment | $(status '${{ needs.coverage-pr-comment.result }}') |" >> "$GITHUB_STEP_SUMMARY" echo "| E2E | $(status '${{ needs.test-e2e.result }}') |" >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/nightly-compat.yml b/.github/workflows/nightly-compat.yml new file mode 100644 index 0000000000..2abbb149d9 --- /dev/null +++ b/.github/workflows/nightly-compat.yml @@ -0,0 +1,126 @@ +name: Nightly Node Compat + +# Plano mestre testes+CI (Eixo D2, aprovado 2026-07-04): as matrizes de compatibilidade +# Node 24/26 custavam ~28% de CADA run do CI pesado (2 execuções completas da suíte por +# sync da release-PR) para pegar uma classe de quebra que raramente nasce num PR típico. +# Elas rodam aqui 1×/dia contra o tip da release ativa (mesmo alvo do nightly-release-green) +# e continuam obrigatórias no gate de release via workflow_dispatch do ci.yml se preciso. +# fail-fast desligado: numa quebra queremos saber TODAS as versões afetadas de uma vez. + +on: + schedule: + - cron: "47 6 * * *" # 06:47 UTC diário — slot distinto dos demais nightlies + workflow_dispatch: + inputs: + branch: + description: "Branch to validate (default: highest release/vX.Y.Z)" + required: false + type: string + +permissions: + contents: read + issues: write + +concurrency: + group: nightly-compat + cancel-in-progress: true + +jobs: + resolve-branch: + name: Resolve active release branch + runs-on: ubuntu-latest + outputs: + target: ${{ steps.branch.outputs.target }} + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 + persist-credentials: false + - name: Resolve active release branch + id: branch + env: + INPUT_BRANCH: ${{ github.event.inputs.branch }} + run: | + set -euo pipefail + if [ -n "${INPUT_BRANCH:-}" ]; then + TARGET="$INPUT_BRANCH" + else + TARGET=$(git for-each-ref --format='%(refname:short)' 'refs/remotes/origin/release/v*' \ + | sed 's#origin/##' \ + | sort -t/ -k2 -V \ + | tail -1) + fi + case "$TARGET" in + release/v[0-9]*.[0-9]*.[0-9]*) ;; + *) echo "Refusing non-canonical branch name: $TARGET"; exit 1 ;; + esac + echo "target=$TARGET" >> "$GITHUB_OUTPUT" + + compat-build-26: + name: Node 26 Compatibility Build + runs-on: ubuntu-latest + timeout-minutes: 25 + needs: resolve-branch + steps: + - uses: actions/checkout@v7 + with: + ref: ${{ needs.resolve-branch.outputs.target }} + persist-credentials: false + - uses: actions/setup-node@v6 + with: + node-version: "26" + cache: npm + - uses: ./.github/actions/npm-ci-retry + - run: npm run build + + compat-tests: + name: Node ${{ matrix.node }} Compat Tests (${{ matrix.shard }}/4) + runs-on: ubuntu-latest + timeout-minutes: 25 + needs: resolve-branch + strategy: + fail-fast: false + matrix: + node: [24, 26] + shard: [1, 2, 3, 4] + env: + JWT_SECRET: ci-nightly-secret-with-sufficient-length-for-validation + API_KEY_SECRET: ci-nightly-api-key-secret-long + DISABLE_SQLITE_AUTO_BACKUP: "true" + TEST_SHARD: ${{ matrix.shard }}/4 + steps: + - uses: actions/checkout@v7 + with: + ref: ${{ needs.resolve-branch.outputs.target }} + persist-credentials: false + - uses: actions/setup-node@v6 + with: + node-version: ${{ matrix.node }} + cache: npm + - uses: ./.github/actions/npm-ci-retry + - run: npm run check:node-runtime + - run: npm run test:unit:ci:shard + + report: + name: Open / update tracking issue on failure + runs-on: ubuntu-latest + if: ${{ !cancelled() && (needs.compat-tests.result == 'failure' || needs.compat-build-26.result == 'failure') }} + needs: [resolve-branch, compat-build-26, compat-tests] + permissions: + issues: write + steps: + - name: Open or update issue + env: + GH_TOKEN: ${{ github.token }} + TARGET: ${{ needs.resolve-branch.outputs.target }} + RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + run: | + set -euo pipefail + TITLE="🌙 nightly-compat: Node 24/26 failures on $TARGET" + EXISTING=$(gh issue list --repo "$GITHUB_REPOSITORY" --state open --search "$TITLE in:title" --json number --jq '.[0].number') + BODY="Nightly Node-compat run failed on \`$TARGET\`: $RUN_URL — triage which Node version/shard broke (fail-fast off, all versions reported)." + if [ -n "$EXISTING" ]; then + gh issue comment "$EXISTING" --repo "$GITHUB_REPOSITORY" --body "$BODY" + else + gh issue create --repo "$GITHUB_REPOSITORY" --title "$TITLE" --body "$BODY" + fi From 059dbe9f13d8c2df0eb91fb4f16d0d01a7e72c84 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Date: Sat, 4 Jul 2026 21:25:17 -0300 Subject: [PATCH 005/749] =?UTF-8?q?feat(quality):=20no-new-warnings=20por?= =?UTF-8?q?=20PR=20=E2=80=94=20ESLint=20bulk=20suppressions=20+=20lint-gua?= =?UTF-8?q?rd=20fork-condicional=20(Pacote=204)=20(#6218)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(quality): no-new-warnings per PR via native ESLint bulk suppressions Pacote 4 do plano mestre testes+CI (aprovado 2026-07-04). O ratchet de eslintWarnings so rodava no CI pesado (release-PR) -> o drift acumulava invisivel e explodia na release (+41/+37/+88 por ciclo, rebaselinado as cegas — historico no proprio quality-baseline.json). Modelo novo (SonarSource Clean-as-You-Code + ESLint bulk suppressions nativo >=9.24): - config/quality/eslint-suppressions.json congela a divida existente por arquivo+regra: 476 arquivos / 4.273 violacoes. - npm run lint + lint-staged (pre-commit) + novo job lint-guard no quality.yml rodam suppressions-aware: violacao NOVA fica vermelha NO PR que a introduz (bulk suppressions ainda eleva estouros de baseline por arquivo a error). - 3 regras warn promovidas a error em src/** (react-hooks/exhaustive-deps, @next/next/no-img-element, import/no-anonymous-default-export) — divida existente congelada, ocorrencia nova = erro imediato. - collect-metrics mede sob o baseline congelado -> a metrica eslintWarnings vira 'divida liquida nova' (~0 em regime); baseline apertado 4279->0 no mesmo PR (exigencia do require-tighten). Aperto do ESTOQUE congelado: npx eslint . --prune-suppressions na reconciliacao da release. - Principio Zero: lint-guard usa continue-on-error para PR de FORK (report-only; a campanha /green-prs aplica o fix via co-autoria) — bloqueante so para branches internas, a origem real do drift. Validacao: negativo (any novo em tests/) exit 1; negativo (img em src/, regra promovida) exit 1; positivo escopado exit 0; baseline gerado por --suppress-all no tip (tree inteiro passa por construcao); YAML js-yaml ok. * fix(quality): clear the 6 residual warnings so lint-guard runs clean at --max-warnings 0 The committed baseline still let 6 warnings through the lint-guard gate: 5 now-unused inline eslint-disable directives (the file-level suppressions made them redundant — removed via eslint --fix, suppressions regenerated to absorb the re-exposed occurrences) and 1 anonymous default export in tests/load/k6-soak.js (outside the src/** severity-override scope — named the k6 scenario function instead). Verified on the clean tree: lint-guard exit=0; any-canary (new 'const x: any' in open-sse) exit=1 — the gate bites on NEW violations while the 4,273 frozen ones stay suppressed (476 files). * fix(ci): lint-guard continue-on-error must be boolean on non-PR events github.event.pull_request is undefined on workflow_dispatch — the bare property expression made the job fail at plan time (run 28722888456: 4 jobs green, run red, lint-guard never materialized). Guard with event_name check so the expression is always boolean: PR de fork = report-only (Principio Zero), resto = blocking. --- .github/workflows/quality.yml | 28 + config/quality/eslint-suppressions.json | 2385 +++++++++++++++++ config/quality/quality-baseline.json | 5 +- eslint.config.mjs | 21 +- open-sse/utils/streamHelpers.ts | 2 +- package.json | 4 +- scripts/quality/collect-metrics.mjs | 14 +- .../advanced/StreamTransformerAccordion.tsx | 20 +- src/lib/agentSkills/openapiParser.ts | 9 +- tests/load/k6-soak.js | 2 +- ...o-streaming-empty-content-failover.test.ts | 24 +- tests/unit/quota-redis-store.test.ts | 62 +- 12 files changed, 2507 insertions(+), 69 deletions(-) create mode 100644 config/quality/eslint-suppressions.json diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index cb8c976d5c..2d13fe3dc4 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -146,3 +146,31 @@ jobs: - run: npm run test:unit:ci:shard env: TEST_SHARD: ${{ matrix.shard }}/2 + + # ── Pacote 4 (plano mestre testes+CI, aprovado 2026-07-04) ───────────────────────── + # No-new-warnings por PR via ESLint bulk suppressions nativo (>=9.24). O baseline + # config/quality/eslint-suppressions.json congela as violações EXISTENTES por + # arquivo+regra; qualquer warning NOVO aparece e o --max-warnings 0 falha o job — o + # drift de +41/+88 warnings por ciclo passa a morrer no PR que o introduz, em vez de + # ser rebaselinado às cegas na release. Aperto do baseline (na reconciliação da + # release): npx eslint . --prune-suppressions --suppressions-location config/quality/eslint-suppressions.json + # + # Princípio Zero: bloqueante SÓ para branches internas (as campanhas/sessões são a + # origem do drift). PR de FORK roda em modo report (continue-on-error → o job fica + # verde com anotação; a campanha /green-prs aplica o fix via co-autoria — o + # contribuidor NUNCA é bloqueado nem cobrado). + lint-guard: + name: No new ESLint warnings + runs-on: ubuntu-latest + continue-on-error: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true }} + steps: + - uses: actions/checkout@v7 + with: + persist-credentials: false + - uses: actions/setup-node@v6 + with: + node-version: ${{ env.CI_NODE_VERSION }} + cache: npm + - run: npm ci + - name: ESLint (baseline congelado — warning novo = vermelho) + run: npx eslint . --suppressions-location config/quality/eslint-suppressions.json --max-warnings 0 diff --git a/config/quality/eslint-suppressions.json b/config/quality/eslint-suppressions.json new file mode 100644 index 0000000000..0cab8df64d --- /dev/null +++ b/config/quality/eslint-suppressions.json @@ -0,0 +1,2385 @@ +{ + "open-sse/executors/blackbox-web.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "open-sse/executors/claude-web-with-auto-refresh.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "open-sse/executors/claude-web.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "open-sse/executors/claudeIdentity.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "open-sse/executors/cliproxyapi.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "open-sse/executors/deepseek-web.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 12 + } + }, + "open-sse/executors/gemini-web.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "open-sse/executors/github.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 14 + } + }, + "open-sse/executors/opencode.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "open-sse/executors/pollinations.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 7 + } + }, + "open-sse/executors/puter.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "open-sse/executors/t3-chat-web.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 11 + } + }, + "open-sse/executors/vertex.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "open-sse/handlers/imageGeneration.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "open-sse/handlers/musicGeneration.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "open-sse/handlers/search.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 34 + } + }, + "open-sse/handlers/videoGeneration.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "open-sse/lib/deepseek-pow.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 5 + } + }, + "open-sse/mcp-server/__tests__/a2aLifecycle.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "open-sse/mcp-server/__tests__/dbHealthTool.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "open-sse/mcp-server/__tests__/toolSearch.tool.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "open-sse/mcp-server/server.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "open-sse/mcp-server/tools/gamificationTools.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "open-sse/mcp-server/tools/pluginTools.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "open-sse/services/batchProcessor.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 17 + } + }, + "open-sse/services/claudeWebAutoRefresh.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "open-sse/services/compression/engines/headroom/gcf/decode_generic.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 18 + } + }, + "open-sse/services/compression/engines/headroom/gcf/scalar.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "open-sse/services/compression/resolveCompressionPlan.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "open-sse/services/credentialGate.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "open-sse/services/inAppLoginService.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "open-sse/services/taskAwareRouter.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "open-sse/services/toolLatencyTracker.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "open-sse/services/usage/codebuddy-cn.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "open-sse/translator/helpers/claudeHelper.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "open-sse/utils/setupPolyfill.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 5 + } + }, + "src/app/(dashboard)/dashboard/HomePageClient.tsx": { + "react-hooks/exhaustive-deps": { + "count": 1 + } + }, + "src/app/(dashboard)/dashboard/agent-skills/AgentSkillsPageClient.tsx": { + "no-restricted-syntax": { + "count": 3 + } + }, + "src/app/(dashboard)/dashboard/api-manager/ApiManagerPageClient.tsx": { + "no-restricted-syntax": { + "count": 2 + }, + "react-hooks/exhaustive-deps": { + "count": 1 + } + }, + "src/app/(dashboard)/dashboard/cli-agents/CliAgentsPageClient.tsx": { + "no-restricted-syntax": { + "count": 4 + } + }, + "src/app/(dashboard)/dashboard/cli-code/components/AntigravityToolCard.tsx": { + "react-hooks/exhaustive-deps": { + "count": 1 + } + }, + "src/app/(dashboard)/dashboard/cli-code/components/DefaultToolCard.tsx": { + "react-hooks/exhaustive-deps": { + "count": 2 + } + }, + "src/app/(dashboard)/dashboard/costs/costExplorerUtils.ts": { + "no-restricted-syntax": { + "count": 1 + } + }, + "src/app/(dashboard)/dashboard/media-providers/components/ImageExampleCard.tsx": { + "@next/next/no-img-element": { + "count": 1 + } + }, + "src/app/(dashboard)/dashboard/playground/components/tabs/ApiTab.tsx": { + "react-hooks/exhaustive-deps": { + "count": 1 + } + }, + "src/app/(dashboard)/dashboard/providers/[id]/ProviderDetailPageClient.tsx": { + "react-hooks/exhaustive-deps": { + "count": 1 + } + }, + "src/app/(dashboard)/dashboard/settings/components/AppearanceTab.tsx": { + "@next/next/no-img-element": { + "count": 4 + } + }, + "src/app/(dashboard)/dashboard/settings/components/AuthzSection.tsx": { + "no-restricted-syntax": { + "count": 1 + } + }, + "src/app/(dashboard)/dashboard/settings/components/ComboDefaultsTab.tsx": { + "no-restricted-syntax": { + "count": 1 + } + }, + "src/app/(dashboard)/dashboard/settings/components/ModelLockoutCard.tsx": { + "react-hooks/exhaustive-deps": { + "count": 1 + } + }, + "src/app/(dashboard)/dashboard/settings/components/SessionInfoCard.tsx": { + "react-hooks/exhaustive-deps": { + "count": 1 + } + }, + "src/app/(dashboard)/dashboard/tools/agent-bridge/components/AgentList.tsx": { + "no-restricted-syntax": { + "count": 3 + } + }, + "src/app/(dashboard)/dashboard/tools/agent-bridge/components/ModelSelectorModal.tsx": { + "no-restricted-syntax": { + "count": 2 + } + }, + "src/app/api/services/[name]/logs/route.ts": { + "no-restricted-syntax": { + "count": 1 + } + }, + "src/app/api/v1/vscode/[token]/models/route.ts": { + "no-restricted-syntax": { + "count": 1 + } + }, + "src/hooks/useLiveDashboard.ts": { + "react-hooks/exhaustive-deps": { + "count": 2 + } + }, + "src/shared/components/CursorAuthModal.tsx": { + "react-hooks/exhaustive-deps": { + "count": 1 + } + }, + "src/shared/components/KiroSocialOAuthModal.tsx": { + "react-hooks/exhaustive-deps": { + "count": 1 + } + }, + "src/shared/components/LanguageSelector.tsx": { + "@next/next/no-img-element": { + "count": 1 + } + }, + "src/shared/components/ProxyConfigModal.tsx": { + "react-hooks/exhaustive-deps": { + "count": 1 + } + }, + "src/shared/components/RequestLoggerV2.tsx": { + "react-hooks/exhaustive-deps": { + "count": 6 + } + }, + "src/shared/components/Sidebar.tsx": { + "@next/next/no-img-element": { + "count": 1 + } + }, + "tests/e2e/api.spec.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/e2e/ecosystem.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 17 + } + }, + "tests/e2e/helpers/dashboardAuth.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/e2e/protocol-clients.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 6 + } + }, + "tests/e2e/system-failover.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/integration/_chatPipelineHarness.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 8 + } + }, + "tests/integration/_comboRoutingHarness.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/integration/api-keys.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 23 + } + }, + "tests/integration/api-routes-critical.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 39 + } + }, + "tests/integration/chat-pipeline.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 20 + } + }, + "tests/integration/chatcore-compression-integration.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 9 + } + }, + "tests/integration/combo-live/_liveHarness.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 10 + } + }, + "tests/integration/combo-live/auto.live.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 22 + } + }, + "tests/integration/combo-live/cost-and-fusion.live.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 29 + } + }, + "tests/integration/combo-live/ordered.live.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/integration/combo-matrix/context-relay-handoff.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/integration/combo-provider-exhaustion.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 19 + } + }, + "tests/integration/combo-routing-e2e.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 7 + } + }, + "tests/integration/live-gemini-nonstream.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/integration/live-gemini-workload.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/integration/liveGeminiShared.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/integration/llama-cpp-provider.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/integration/memory-pipeline.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/integration/memory-reindex.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/integration/memory-route-put.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/integration/memory-summarize.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/integration/mimocode-proxy.integration.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 13 + } + }, + "tests/integration/obsidian-plugin-e2e.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 12 + } + }, + "tests/integration/performance-regression.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/integration/proxy-context-passthrough.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 9 + } + }, + "tests/integration/proxy-registry-flow.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 9 + } + }, + "tests/integration/qdrant-routes.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 19 + } + }, + "tests/integration/resilience-http-e2e.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/integration/skills-pipeline.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 14 + } + }, + "tests/integration/v1-contracts-behavior.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 10 + } + }, + "tests/live/deepseek-web-live.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/theoldllm-stress.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 10 + } + }, + "tests/translator/testFromFile.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/account-fallback-service.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/acp-agents-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/admin-audit-events.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 11 + } + }, + "tests/unit/adversarialPii.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/agy-usage-quota.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/anthropic-compat-validation-v1-messages.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/antigravity-429-quota-cooldown.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/antigravity-local-usage-fallback-3821.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/antigravity-model-aliases.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/antigravity-tool-cloaking.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/api-key-reveal-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 8 + } + }, + "tests/unit/apikey-connection-health-check.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/audio-speech-handler.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 9 + } + }, + "tests/unit/audio-transcription-handler.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 9 + } + }, + "tests/unit/audio-translations-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 6 + } + }, + "tests/unit/auggie-executor.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 9 + } + }, + "tests/unit/auth-clear-account-error.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 5 + } + }, + "tests/unit/auth-disable-cooling-2997.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 11 + } + }, + "tests/unit/auth-login-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/auth-ollama-cloud-per-model-403-3027.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 11 + } + }, + "tests/unit/auth-terminal-status.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 15 + } + }, + "tests/unit/autocombo-unification.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/bailian-quota-fetcher.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 21 + } + }, + "tests/unit/base-executor-sanitize-effort.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 45 + } + }, + "tests/unit/batch_api.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 7 + } + }, + "tests/unit/batch_results.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/blackbox-web.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 10 + } + }, + "tests/unit/build/check-licenses.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 5 + } + }, + "tests/unit/bypass-handler.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/call-log-cap.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 43 + } + }, + "tests/unit/call-log-file-rotation.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 5 + } + }, + "tests/unit/call-log-startup.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/call-logs-correlation-sort.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/cc-bridge-transforms.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 31 + } + }, + "tests/unit/cc-compatible-model-catalog.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/cc-compatible-provider.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 37 + } + }, + "tests/unit/chat-combo-live-test.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 6 + } + }, + "tests/unit/chat-context-relay.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 10 + } + }, + "tests/unit/chat-cooldown-aware-retry.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 10 + } + }, + "tests/unit/chat-helpers.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 13 + } + }, + "tests/unit/chat-rate-limit-body-lock.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/chat-route-coverage.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 15 + } + }, + "tests/unit/chat-route-edge-cases.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/chat-safetynet-reqid-6097.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/chatcore-compression-integration.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/chatcore-translation-paths.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 40 + } + }, + "tests/unit/chatgpt-web-tools-5240.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 6 + } + }, + "tests/unit/chatgpt-web.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 6 + } + }, + "tests/unit/chipotle-executor.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/claude-code-compatible-helpers.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/claude-code-compatible-request.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 27 + } + }, + "tests/unit/claude-empty-stream-error-3685.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/claude-helper-minimax-output-config.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/claude-to-openai-image-toolresult.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 8 + } + }, + "tests/unit/claude-to-openai-think-close-5123.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/cli-a2a-invoke-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 16 + } + }, + "tests/unit/cli-audit-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 15 + } + }, + "tests/unit/cli-batches-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 16 + } + }, + "tests/unit/cli-chat.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 17 + } + }, + "tests/unit/cli-cloud-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 18 + } + }, + "tests/unit/cli-combo-suggest-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 16 + } + }, + "tests/unit/cli-completion-dynamic.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 11 + } + }, + "tests/unit/cli-compression-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 32 + } + }, + "tests/unit/cli-context-eng-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 19 + } + }, + "tests/unit/cli-cost.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 16 + } + }, + "tests/unit/cli-eval-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 22 + } + }, + "tests/unit/cli-expanded-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/cli-files-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 12 + } + }, + "tests/unit/cli-helper/config-generator.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/cli-lang-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/cli-mcp-call-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 16 + } + }, + "tests/unit/cli-memory-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 18 + } + }, + "tests/unit/cli-nodes-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 19 + } + }, + "tests/unit/cli-oauth-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 18 + } + }, + "tests/unit/cli-oneproxy-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 22 + } + }, + "tests/unit/cli-openapi-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 6 + } + }, + "tests/unit/cli-policy-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 20 + } + }, + "tests/unit/cli-pricing-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 16 + } + }, + "tests/unit/cli-process-supervisor.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 6 + } + }, + "tests/unit/cli-program.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/cli-providers-metrics.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 15 + } + }, + "tests/unit/cli-remote-mode.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 5 + } + }, + "tests/unit/cli-resilience-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 16 + } + }, + "tests/unit/cli-runtime-extended.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/cli-sessions-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 12 + } + }, + "tests/unit/cli-simulate.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 13 + } + }, + "tests/unit/cli-skills-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 22 + } + }, + "tests/unit/cli-stream.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 13 + } + }, + "tests/unit/cli-sync-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 21 + } + }, + "tests/unit/cli-tags-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 17 + } + }, + "tests/unit/cli-telemetry-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 6 + } + }, + "tests/unit/cli-translator-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 16 + } + }, + "tests/unit/cli-usage.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 17 + } + }, + "tests/unit/cli-webhooks-commands.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 23 + } + }, + "tests/unit/cloud-agent-cursor-4227.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/cloudflare-models-uuid-4259.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/cloudflaredTunnel-extended.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/codebuddy-cn-provider.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/codex-banked-reset-credits-5199.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 7 + } + }, + "tests/unit/codex-connection-defaults.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 16 + } + }, + "tests/unit/codex-session-affinity-reset-aware-5903.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/codex-stream-false.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/combo-builder-options-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 5 + } + }, + "tests/unit/combo-cache-invalidation.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 5 + } + }, + "tests/unit/combo-context-length.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/combo-health-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/combo-provider-cooldown.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/combo-provider-wildcard.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 18 + } + }, + "tests/unit/combo-routes-composite-tiers.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 10 + } + }, + "tests/unit/combo-routing-engine.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 261 + } + }, + "tests/unit/combo-same-provider-cascade.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/combo-sessionless-pin-3825.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 17 + } + }, + "tests/unit/combo-strategy-fallbacks.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 35 + } + }, + "tests/unit/combo-stream-readiness-fallback.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 17 + } + }, + "tests/unit/combo-target-timeout-runner.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/combo-test-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 10 + } + }, + "tests/unit/combos-quota-protected.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/command-code-maxtokens-negative-5166.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/compliance-audit-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/compliance-index.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 5 + } + }, + "tests/unit/compression/adaptive-select-plan-wiring.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/compression/db.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 16 + } + }, + "tests/unit/compression/eval-runner.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/context-manager.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 10 + } + }, + "tests/unit/context-pinning-tool-calls.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 5 + } + }, + "tests/unit/copilot-free-quota-not-inverted.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/copilot-gemini-claude-route-no-responses.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 9 + } + }, + "tests/unit/cursor-usage-fetcher.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/custom-headers-provider-nodes.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 6 + } + }, + "tests/unit/db-agent-bridge-bypass.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/db-agent-bridge-mappings.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/db-agent-bridge-state.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/db-combos-crud.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 9 + } + }, + "tests/unit/db-core-extended.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 5 + } + }, + "tests/unit/db-core-migration.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 7 + } + }, + "tests/unit/db-core.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/db-detailed-logs.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/db-domainState-crud.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 5 + } + }, + "tests/unit/db-gamification-federation-3500.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/db-health-check.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 29 + } + }, + "tests/unit/db-health-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 7 + } + }, + "tests/unit/db-inspector-custom-hosts.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/db-inspector-sessions.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/db-migration-runner.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 12 + } + }, + "tests/unit/db-model-aliases-cascade.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/db-models-crud.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/db-models-extended.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 5 + } + }, + "tests/unit/db-playground-presets.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/db-provider-plans.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/db-providers-crud.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 18 + } + }, + "tests/unit/db-proxies-crud.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 6 + } + }, + "tests/unit/db-quota-consumption.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/db-quota-pools.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/db-read-cache.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/db-registeredKeys-crud.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 8 + } + }, + "tests/unit/db-secrets.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/db-settings-crud.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 20 + } + }, + "tests/unit/db-settings-extended.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 7 + } + }, + "tests/unit/deepseek-quota-fetcher.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 8 + } + }, + "tests/unit/deepseek-web-autorefresh-401-response.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/display-and-error-utils.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/domain-branch-hardening.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/domain-cost-rules.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/domain-fallback-policy.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/domain-lockout-policy.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/duckduckgo-web-executor.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 8 + } + }, + "tests/unit/electron-main.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/embeddings-proxy-forwarding.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/empty-tool-name-loop.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 8 + } + }, + "tests/unit/endpoint-restrictions-policy.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/error-message-sanitization.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 19 + } + }, + "tests/unit/executor-agy.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/executor-base-utils.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 5 + } + }, + "tests/unit/executor-claude-identity.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/executor-cloudflare-ai.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/executor-default-base.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 43 + } + }, + "tests/unit/executor-github.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 7 + } + }, + "tests/unit/executor-gitlab.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/executor-kimi-web.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/executor-kiro.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/executor-nlpcloud.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/executor-pollinations.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/executor-qwen-web.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 40 + } + }, + "tests/unit/fetch-timeout.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 7 + } + }, + "tests/unit/file-expiration-policy.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/fix-tool-adjacency.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/fixes-p1.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 20 + } + }, + "tests/unit/gamification/events.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/gamification/leaderboard.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/gemini-finish-reason-normalization.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/gemini-tool-params-object-3357.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/gemini-web.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 8 + } + }, + "tests/unit/github-claude-reasoning-effort-granular.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 9 + } + }, + "tests/unit/gitlab-duo-toolcalls-6051.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 6 + } + }, + "tests/unit/gitlawb-provider.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/glm-executor.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 19 + } + }, + "tests/unit/glm-provider-model-import-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/grok-web.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 25 + } + }, + "tests/unit/guide-settings-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/i18n-nest-dotted-keys.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/image-generation-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 11 + } + }, + "tests/unit/inspector-agent-bridge-hook.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/kiro-tool-args-streaming.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/live-ws-public-url.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 5 + } + }, + "tests/unit/lmarena-provider.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 9 + } + }, + "tests/unit/lmarena-split-cookie-4271.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/log-retention.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 12 + } + }, + "tests/unit/login-bootstrap-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 10 + } + }, + "tests/unit/management-password.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/memory-cache-safe-injection.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/memory-glm-injection.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/memory-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/memory-store.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/memory-summarization.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/messages-count-tokens-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/mimocode-executor.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 58 + } + }, + "tests/unit/minimax-tts-1043.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 6 + } + }, + "tests/unit/model-alias-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 6 + } + }, + "tests/unit/model-aliases-settings-route-selfheal.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/model-lockout-max-cooldown.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/model-sync-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 16 + } + }, + "tests/unit/model-sync-scheduler.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/model-test-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/models-catalog-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 79 + } + }, + "tests/unit/modelsDevSync-extended.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/moderations-handler.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 7 + } + }, + "tests/unit/nanobanana-image-generation.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/next-config.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/nvidia-nim-validator.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/oauth-providers-config.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/observability-fase04.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/ocr-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 8 + } + }, + "tests/unit/openai-tool-opaque-object-schema.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/openapi-coverage.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/openapi-security-tiers.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 8 + } + }, + "tests/unit/openapi-spec-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/openapi-try-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 6 + } + }, + "tests/unit/opencode-executor.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/opencode-proxy-rotation-4954.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 6 + } + }, + "tests/unit/opencode-quota-fetcher.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/openrouter-vision-sync-4264.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/orphaned-tool-filter.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/payload-rules-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 7 + } + }, + "tests/unit/payload-rules.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/perplexity-web-streaming-tools-5927.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/perplexity-web.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 11 + } + }, + "tests/unit/persist-429-cooldown-account-fallback.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 7 + } + }, + "tests/unit/plan3-p0.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 20 + } + }, + "tests/unit/plugins-config-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/plugins-index.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 9 + } + }, + "tests/unit/plugins-welcome-banner-e2e.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/pollinations-jsonmode-3981.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/prompt-injection-guard.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/prompt-required-routes.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/provider-connection-apikey-dedup.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/provider-connection-healthcheck-interval-zero.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 6 + } + }, + "tests/unit/provider-connections-quota-threshold.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/provider-limits-proxy-fail-closed.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/provider-metadata-5461-5470-5534.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/provider-models-management-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/provider-models-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 58 + } + }, + "tests/unit/provider-models-token-limits.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/provider-models-v1-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/provider-node-icon-url.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 6 + } + }, + "tests/unit/provider-nodes-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 13 + } + }, + "tests/unit/provider-nodes-validate-modelid.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 8 + } + }, + "tests/unit/provider-request-failure-pipeline.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 6 + } + }, + "tests/unit/provider-scoped-models-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 7 + } + }, + "tests/unit/provider-validation-hardening.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 6 + } + }, + "tests/unit/provider-validation-qwen-web-5855.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/provider-validation-specialty.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 8 + } + }, + "tests/unit/providers-route-managed-catalog.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/providers-validate-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/proxy-egress-visibility.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 10 + } + }, + "tests/unit/proxy-fetch.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/proxy-management-v1-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 22 + } + }, + "tests/unit/proxy-pool-sync-4878.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/proxy-registry.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 55 + } + }, + "tests/unit/proxy-resolution-status-filter.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/qoder-executor.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/qoder-unwrap-error-envelope.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/quota-cache-is-exhausted-per-window-5923.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 6 + } + }, + "tests/unit/quota-concept-card-i18n.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/quota-groups-crud.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/quota-groups-migration.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/quota-helpers.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/quota-pool-connections.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/quota-pool-delete-prune.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/quota-pool-single-provider.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 9 + } + }, + "tests/unit/qwen-web-cookie-validation-3958.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/rate-limit-queue-timeout-lockout.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/regional-provider-cn-notices-5462.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/remaining-tasks.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/request-log-payloads.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/request-logger-signature.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 8 + } + }, + "tests/unit/resolve-proxy-family.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 10 + } + }, + "tests/unit/response-sanitizer.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 84 + } + }, + "tests/unit/responses-parse-once-4041.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 6 + } + }, + "tests/unit/responses-translation-fixes.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 35 + } + }, + "tests/unit/route-edge-coverage.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 57 + } + }, + "tests/unit/safe-outbound-fetch.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 9 + } + }, + "tests/unit/save-call-log-persistence.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 7 + } + }, + "tests/unit/schema-coercion.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 12 + } + }, + "tests/unit/search-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 7 + } + }, + "tests/unit/security/cloud-sync-hmac.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 5 + } + }, + "tests/unit/service-context-handoff.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/service-context-manager.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/service-system-transforms.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/services-branch-hardening.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/services/logs-sse.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 7 + } + }, + "tests/unit/session-pool-rest-api.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/settings-route-password.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/shared-api-utils.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 8 + } + }, + "tests/unit/sidebar-customization.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 11 + } + }, + "tests/unit/sidebar-visibility.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/signature-cache.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/skills-routes.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 9 + } + }, + "tests/unit/skills-skillssh.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 7 + } + }, + "tests/unit/spend-batch-writer.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/sse-auth.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 13 + } + }, + "tests/unit/sse-parser.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 15 + } + }, + "tests/unit/startup-stale-cooldown-recovery.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/stream-payload-collector.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/stream-utils.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 14 + } + }, + "tests/unit/streamingPiiTransform.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 18 + } + }, + "tests/unit/sync-bundle.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/sync-routes.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 5 + } + }, + "tests/unit/t19-codex-responses-empty-content.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/t23-t24-fallback-resilience.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/t3chat-web-cookie-hint-5465.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/t43-gemini-tool-call-no-thought-signature.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/tag-routing.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/telemetry-summary-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/token-health-check-circuit-breaker.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/token-health-check.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 10 + } + }, + "tests/unit/token-limits.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/token-refresh-route-service.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 10 + } + }, + "tests/unit/token-refresh-service.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 17 + } + }, + "tests/unit/tool-request-sanitization.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 12 + } + }, + "tests/unit/trae-executor.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 7 + } + }, + "tests/unit/transform-stream-hwm.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 5 + } + }, + "tests/unit/translator-antigravity-to-openai.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/translator-claude-helper-thinking.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 21 + } + }, + "tests/unit/translator-claude-to-gemini.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 14 + } + }, + "tests/unit/translator-claude-to-openai.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/translator-helper-branches.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 25 + } + }, + "tests/unit/translator-openai-responses-req.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 76 + } + }, + "tests/unit/translator-openai-to-gemini.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 70 + } + }, + "tests/unit/translator-openai-to-kiro.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 17 + } + }, + "tests/unit/translator-redacted-thinking-model-aware-4479.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 6 + } + }, + "tests/unit/translator-resp-antigravity-thinking-boundary.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 6 + } + }, + "tests/unit/translator-resp-claude-to-openai.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 17 + } + }, + "tests/unit/translator-resp-gemini-to-openai.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 75 + } + }, + "tests/unit/translator-resp-openai-responses.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 5 + } + }, + "tests/unit/translator-resp-openai-to-claude.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 14 + } + }, + "tests/unit/translator-thinking-provider-compat-2043.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 12 + } + }, + "tests/unit/translator-tool-call-shim.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/translator-tools-anthropic-shape.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 9 + } + }, + "tests/unit/universal-handoff.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 16 + } + }, + "tests/unit/usage-analytics.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 5 + } + }, + "tests/unit/usage-migrations.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 17 + } + }, + "tests/unit/usage-providers.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/usage-service-hardening.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 59 + } + }, + "tests/unit/v1-ws-bridge.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/v1-ws-route.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 4 + } + }, + "tests/unit/version-manager.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/versionManager-orchestrator.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/vertex-express-apikey.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/vertex-functioncall-id-3440.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/vertex-media.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 3 + } + }, + "tests/unit/vscode-token-routes.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 76 + } + }, + "tests/unit/web-cookie-providers-new.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 18 + } + }, + "tests/unit/web-runtime-env.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/web-search-fallback-format.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 1 + } + }, + "tests/unit/web-session-pool-health.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 2 + } + }, + "tests/unit/webhook-abort-timer-cleanup.test.ts": { + "@typescript-eslint/no-explicit-any": { + "count": 5 + } + } +} diff --git a/config/quality/quality-baseline.json b/config/quality/quality-baseline.json index 119970b766..3b49d0d0b4 100644 --- a/config/quality/quality-baseline.json +++ b/config/quality/quality-baseline.json @@ -2,7 +2,7 @@ "_comment": "Catraca de qualidade. 'down' = nao pode aumentar; 'up' = nao pode cair. Atualize via 'npm run quality:ratchet -- --update' (somente quando melhora). Cada valor e um numero REAL medido, nunca um chute. Cobertura entra na Fase 4 a partir de um run de cobertura mergeada no CI.", "metrics": { "eslintWarnings": { - "value": 4279, + "value": 0, "_rebaseline_2026_07_03_v3844_residual_release_green": "4270->4279 (+9). v3.8.44 residual drift on release tip 716041223 (moving target: eslint 4270->4279 as the branch advanced past the prior rebaseline). Inherited from parallel-session merges (Quality Ratchet not on PR->release fast-gates).", "_rebaseline_2026_07_03_v3844_ipfilter_release_green": "4256->4270 (+14). v3.8.44 cycle drift measured on release tip 32e4c906e during the #6131/#5975 release-green rebaseline. Inherited from the merge burst (Quality Ratchet does not run on PR->release fast-gates). route-edge-coverage +7 is my #5975 test comment; the rest is parallel-session drift. Tighten via --update next cycle.", "_rebaseline_2026_07_03_v3844_review_prs_fix_batch": "4199->4256 (+57). Inherited v3.8.44 cycle drift surfaced by the release-green pre-flight (the Quality Ratchet does NOT run on PR->release fast-gates, so warnings accrue unmeasured across the cycle). 4256 = measured by `node scripts/quality/collect-metrics.mjs` on the release tip 72ee80649 during the /review-prs fix-batch round. The round's own merges (#5958 SSE-accept, #5988 deepseek-web, #6013/#5974 retry-after-json, #5975 embeddings-proxy, #5973 non-json-guard) plus the parallel-session merge burst into release/v3.8.44 account for the delta; all `any`-warn-allowed in open-sse/ + tests/. Cyclomatic is already green (2012 < baseline 2015) and needs no bump. Tighten via --require-tighten next cycle.", @@ -18,7 +18,8 @@ "_rebaseline_2026_06_23_v3835_release": "v3.8.35 cycle drift surfaced by the release-green pre-flight (the Quality Ratchet does NOT run on PR→release fast-gates, so warnings accrued across this cycle's parallel-session merges — Compression Phase 4 #4694/#4707/#4716/#4720, chatCore #3501 leaf extractions, contributor PRs #4726/#4753/#4774/#4781/#4783/#4793, etc.). 3907→3912 (+5). Verified my release-finalize working tree touches ONLY docs/*.md (THREAT_MODEL), CHANGELOG.md, baselines, and 1 string line in scripts/check/check-fabricated-docs.mjs — 0 production-code change, so all +5 is inherited contributor drift. No coverage/openapi/i18n regressions.", "_rebaseline_2026_06_22_v3834_release": "v3.8.34 cycle drift surfaced by the release-green pre-flight (the Quality Ratchet does NOT run on PR→release fast-gates, so warnings accrued across this cycle's parallel-session merges — #4583-4586/#4588-4593/#4606-4621/#4644/#4647/#4696/etc.). 3900→3907 (+7). Verified my release-finalize working tree touches ONLY CHANGELOG.md (git status: 0 code changes), so all +7 is inherited contributor drift. No coverage/openapi/i18n regressions.", "_rebaseline_2026_06_22_v3833_release": "Cumulative cycle drift surfaced by the release PR full CI. 3867→3900 (+33).", - "_rebaseline_2026_06_26_v3837_release": "3970->3987. v3.8.37 cycle drift surfaced by the release-green pre-flight (the Quality Ratchet does NOT run on PR->release fast-gates, so warnings/complexity accrued unmeasured across this cycle's 76 commits — provider adds DGrid/Pioneer/xAI, headroom proxy lifecycle #4649, ~50 SSE/translator fixes, Engine Combos #5062). Trust-but-verify: this release-finalize working tree touches ONLY CHANGELOG.md, docs/i18n/*/CHANGELOG.md mirrors, and these baselines — 0 production-code change, so all drift is inherited cycle drift (`any` warn-allowed in open-sse/ + tests/). Tighten via --require-tighten next cycle." + "_rebaseline_2026_06_26_v3837_release": "3970->3987. v3.8.37 cycle drift surfaced by the release-green pre-flight (the Quality Ratchet does NOT run on PR->release fast-gates, so warnings/complexity accrued unmeasured across this cycle's 76 commits — provider adds DGrid/Pioneer/xAI, headroom proxy lifecycle #4649, ~50 SSE/translator fixes, Engine Combos #5062). Trust-but-verify: this release-finalize working tree touches ONLY CHANGELOG.md, docs/i18n/*/CHANGELOG.md mirrors, and these baselines — 0 production-code change, so all drift is inherited cycle drift (`any` warn-allowed in open-sse/ + tests/). Tighten via --require-tighten next cycle.", + "_rebaseline_2026_07_04_pacote4_no_new_warnings": "4279->0. Pacote 4 do plano mestre testes+CI: a divida pre-existente (4279 warnings + violacoes das 3 regras promovidas a error em src/**) foi CONGELADA em config/quality/eslint-suppressions.json (ESLint bulk suppressions nativo) e passa a ser bloqueada NO PR que a introduziria (job lint-guard no quality.yml + npm run lint + lint-staged, todos suppressions-aware; fork = report-only, Principio Zero). collect-metrics agora mede sob o baseline congelado -> a metrica vira 'divida liquida NOVA' (~0 em regime). O aperto do ESTOQUE congelado acontece via `npx eslint . --prune-suppressions --suppressions-location config/quality/eslint-suppressions.json` na reconciliacao da release. Fim das rebaselines-surpresa de +41/+88 por ciclo." }, "eslintErrors": { "value": 0, diff --git a/eslint.config.mjs b/eslint.config.mjs index 3a99309a10..10af3f1154 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -4,6 +4,23 @@ import tseslint from "typescript-eslint"; /** @type {import("eslint").Linter.Config[]} */ const eslintConfig = [ ...nextVitals, + // Pacote 4 (plano mestre testes+CI, 2026-07-04) — zero-warning policy: TODA regra roda + // como "error" e a dívida pré-existente vive congelada por arquivo+regra em + // config/quality/eslint-suppressions.json (ESLint bulk suppressions nativo). Violação + // NOVA = vermelho no ato (lint-staged no pre-commit + job lint-guard no fast path); + // o drift de +41/+88 warnings/ciclo que era rebaselinado às cegas na release morre no + // PR que o introduz. Aperto do baseline: npx eslint . --prune-suppressions + // --suppressions-location config/quality/eslint-suppressions.json (na release). + { + // Escopo = onde os presets do next registram estes plugins (bloco global sem `files` + // atingiria scripts/*.mjs sem o plugin react-hooks e explodiria o flat config). + files: ["src/**/*.{ts,tsx,js,jsx}"], + rules: { + "react-hooks/exhaustive-deps": "error", + "@next/next/no-img-element": "error", + "import/no-anonymous-default-export": "error", + }, + }, // FASE-02: Security rules (strict everywhere) { rules: { @@ -32,7 +49,7 @@ const eslintConfig = [ files: ["src/app/**/*.{ts,tsx}", "src/components/**/*.{ts,tsx}"], rules: { "no-restricted-syntax": [ - "warn", + "error", { selector: "CallExpression[callee.property.name='includes'][callee.object.callee.property.name='toLowerCase']", @@ -49,7 +66,7 @@ const eslintConfig = [ "@typescript-eslint": tseslint.plugin, }, rules: { - "@typescript-eslint/no-explicit-any": "warn", + "@typescript-eslint/no-explicit-any": "error", "@next/next/no-assign-module-variable": "off", "react-hooks/rules-of-hooks": "off", }, diff --git a/open-sse/utils/streamHelpers.ts b/open-sse/utils/streamHelpers.ts index 03e0b66f5c..1f4b5f9198 100644 --- a/open-sse/utils/streamHelpers.ts +++ b/open-sse/utils/streamHelpers.ts @@ -65,7 +65,7 @@ function isRecord(value: unknown): value is Record { * The pattern is strictly bounded (no unbounded quantifiers over overlapping * alternatives) so it runs in linear time on untrusted input — ReDoS-safe. */ -// eslint-disable-next-line no-control-regex + const ANSI_ESCAPE_RE = /\x1b(?:\[[0-9;?]*[A-Za-z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[A-Z\[\]\\^_`])|[\x00-\x08\x0b\x0c\x0e-\x1f]/g; diff --git a/package.json b/package.json index 630617b493..4b5e40b5f3 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "build:release": "rm -rf .build dist && OMNIROUTE_BUILD_SHA=$(git rev-parse --short HEAD) npm run build && npm run build:cli && node scripts/build/write-build-sha.mjs", "build:native:tproxy": "cd src/mitm/tproxy/native && npx --yes node-gyp rebuild", "start": "node scripts/dev/run-next.mjs start", - "lint": "eslint .", + "lint": "eslint . --suppressions-location config/quality/eslint-suppressions.json", "lint:md": "npx --yes markdownlint-cli2 \"docs/**/*.md\" \"*.md\" \"!docs/i18n\" \"!docs/research\"", "lint:prose": "vale docs", "electron:dev": "concurrently \"npm run dev\" \"wait-on http://localhost:20128 && cd electron && npm run dev\"", @@ -346,7 +346,7 @@ "lint-staged": { "*.{js,jsx,ts,tsx,mjs}": [ "prettier --write", - "eslint --fix --no-error-on-unmatched-pattern" + "eslint --fix --no-error-on-unmatched-pattern --suppressions-location config/quality/eslint-suppressions.json" ], "*.{json,md,yml,yaml,css}": [ "prettier --write" diff --git a/scripts/quality/collect-metrics.mjs b/scripts/quality/collect-metrics.mjs index eaa7b6bc8d..a1535cbcb0 100644 --- a/scripts/quality/collect-metrics.mjs +++ b/scripts/quality/collect-metrics.mjs @@ -15,11 +15,21 @@ import * as yaml from "js-yaml"; const cwd = process.cwd(); const out = {}; -// 1) ESLint: contagem de warnings (errors devem ser 0; o lint já gata isso) +// 1) ESLint: contagem de warnings/errors NOVOS além do baseline congelado. +// Pacote 4 (plano mestre testes+CI, 2026-07-04): a dívida pré-existente vive em +// config/quality/eslint-suppressions.json (ESLint bulk suppressions nativo) e é +// bloqueada no PR que a introduziria (job lint-guard + lint-staged). A métrica do +// ratchet passa a medir a dívida LÍQUIDA nova — em regime, ~0 — em vez do estoque +// bruto (que driftava +41/+88 por ciclo e era rebaselinado às cegas na release). +// O aperto do estoque acontece via --prune-suppressions na release. function eslintCounts() { let stdout; + const args = ["eslint", ".", "--format", "json"]; + if (fs.existsSync(path.join(cwd, "config/quality/eslint-suppressions.json"))) { + args.push("--suppressions-location", "config/quality/eslint-suppressions.json"); + } try { - stdout = execFileSync("npx", ["eslint", ".", "--format", "json"], { + stdout = execFileSync("npx", args, { encoding: "utf8", maxBuffer: 256 * 1024 * 1024, }); diff --git a/src/app/(dashboard)/dashboard/translator/components/advanced/StreamTransformerAccordion.tsx b/src/app/(dashboard)/dashboard/translator/components/advanced/StreamTransformerAccordion.tsx index 63a7aca1e8..0c1e966b68 100644 --- a/src/app/(dashboard)/dashboard/translator/components/advanced/StreamTransformerAccordion.tsx +++ b/src/app/(dashboard)/dashboard/translator/components/advanced/StreamTransformerAccordion.tsx @@ -150,7 +150,6 @@ export default function StreamTransformerAccordion({ const prev = prevForceOpen.current; prevForceOpen.current = Boolean(forceOpen); if (!prev && forceOpen) { - // eslint-disable-next-line react-hooks/set-state-in-effect -- syncing deep-link prop into local state setOpen(true); setHasOpened(true); onOpenChange?.(true); @@ -344,10 +343,7 @@ export default function StreamTransformerAccordion({ onClick={() => handleCopy(rawSse, "input")} aria-label={translateOrFallback("copyInput", "Copy input")} > -