mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-21 06:32:16 +03:00
e45e6c3e346a3fbb75f04e0feba7c0cf02259391
1316 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e45e6c3e34 |
feat: add TinyFish Fetch support to web-fetch provider and update related documentation (#6349)
add TinyFish web-fetch/search provider + tool (#6349). Tests green (tinyfish suites + count-guard 170->171). Integrated into release/v3.8.46. |
||
|
|
95708051a2 |
fix(codex): isolate Spark quota and stabilize quota UI (#6336)
isolate Spark quota + stabilize quota UI (#6336). Tests 44/44, no file-size drift from its files. Integrated into release/v3.8.46. |
||
|
|
b681308259 |
feat(api): add hidePaidModels setting to filter paid-only models from /v1/models catalog (#6495)
feat(api): add hidePaidModels setting (net +1/-0, test OK). Integrated into release/v3.8.46. |
||
|
|
17da3b6e09 |
fix(api-manager): preserve combos in model fallback (#6443)
fix(api-manager): preserve combos in fallback model picker (net +1/-0, test OK). Integrated into release/v3.8.46. |
||
|
|
a03c22e5ed |
fix(api): accept mode:'caveman' + stacked default pipeline yields 0% (#6425) (#6439)
fix(api): /api/compression/preview accepts mode caveman + stacked-zero (#6425) (net +1/-0, test OK). Integrated into release/v3.8.46. |
||
|
|
3cc48edb35 |
fix(oauth): preserve Kiro IDC region in SSO-cache auto-import (#6113)
preserve Kiro IDC region in SSO-cache auto-import (port PR #2314). VPS-validated live by operator (Hard Rule #18); kiro-auto-import-idc 9/9. Integrated into release/v3.8.46. |
||
|
|
58f53e3a35 |
feat(proxy): native proxy-pool round-robin / egress IP rotation (#6365) (#6395)
native proxy-pool round-robin / egress IP rotation (#6365) (net +1/-0, tests OK). Integrated into release/v3.8.46. |
||
|
|
18fa0b2651 |
feat(providers): Gemini tool-calling end-to-end on /v1beta (#6222) (#6394)
Gemini tool-calling end-to-end on /v1beta (#6222) (net +1/-0, tests OK). Integrated into release/v3.8.46. |
||
|
|
f5147d00f9 |
feat(providers): copilot-m365-web enterprise (work) tier support (#6334) (#6392)
copilot-m365-web enterprise (work) tier support (#6334) (net +1/-0, tests OK). Integrated into release/v3.8.46. |
||
|
|
62b1bc9d05 |
feat(api): standardize effort + thinking request params (#6241) (#6398)
standardize effort + thinking request params (#6241) (net +1/-0, tests OK). Integrated into release/v3.8.46. |
||
|
|
b7ac5261a5 |
feat(dashboard): 'Open <host>' link in Add session cookie modal (#6268) (#6391)
'Open <host>' link in Add session cookie modal (#6268) (net +1/-0, tests OK). Integrated into release/v3.8.46. |
||
|
|
b67f2c58da |
fix(dashboard): disambiguate colliding passthrough model aliases (port from 9router#1850) (#6431)
disambiguate colliding passthrough model aliases (port #1850) (net +1/-0, test OK). Integrated into release/v3.8.46. |
||
|
|
a7e0dddac4 |
fix(chat): validate scalar params before provider lookup (#6412) (#6437)
JSON 404 for unknown /api/* (#6424) + early scalar-param validation before provider lookup (#6412, 10/10). Integrated into release/v3.8.46. |
||
|
|
d12ac37d4d |
fix(api): reject non-JSON Content-Type on /v1/chat/completions with 415 (#6414) (#6434)
reject non-JSON Content-Type on /v1/chat/completions (#6414, 4/4). Integrated into release/v3.8.46. |
||
|
|
fa3a09cb43 |
fix(api): echo X-OmniRoute-Compression response header (#6422) (#6441)
echo X-OmniRoute-Compression header on completions routes (#6422, 6/6). Reconciled with #6429 body.model echo. Integrated into release/v3.8.46. |
||
|
|
d11bf528af |
fix(api): coalesce concurrent GET /v1/models to one builder run (#6408) (#6440)
coalesce concurrent GET /v1/models (#6408, 3/3). Integrated into release/v3.8.46. |
||
|
|
ac96c0dd02 |
fix(completions): echo requested body.model on /v1/completions to match x-omniroute-model header (#6429)
echo body.model on /v1/completions (5/5). Integrated into release/v3.8.46. |
||
|
|
6ea7c68e6e |
fix(api): env-var master keys see full /v1/models catalog (#6406) (#6436)
env-key master sees full /v1/models catalog (#6406, 2/2). Integrated into release/v3.8.46. |
||
|
|
a2fabdde8f |
fix(api): return JSON 404 for unknown /v1/* routes (#6405) (#6435)
JSON 404 for unknown /v1/* routes (#6405). Test tests/unit/api/v1-catchall-json-404.test.ts (3/3). Integrated into release/v3.8.46. |
||
|
|
2f4b793c3d | fix(api): provider-models route — redirect→local-catalog fallback + custom-model merge (#6267, #6247) (#6449) | ||
|
|
0086f1772b |
fix(api): filter specialty model catalogs (#6303)
Derive specialty model catalogs from the unified catalog via a shared predicate filter. Integrated into release/v3.8.46. |
||
|
|
49795c24ef |
fix(api): dynamic import for MITM + fix Turbopack over-bundling warnings (#6366)
Dynamic MITM manager import on the agent-bridge route + Turbopack static-analyzer anchor in the skills generator (#6329). Integrated into release/v3.8.46. |
||
|
|
049bad494b |
fix(internal): use explicit internal key selection for dashboard probes (#6372)
Internal probes pick a management-scoped key (pickApiKeyForInternalUse); API-manager model-editor fallback catalog. Integrated into release/v3.8.46. |
||
|
|
b796f2b457 |
feat(providers): link web session guide to provider site (#6316)
Add an Open-provider-site link to the web-session credential guide (+host-extraction test). Integrated into release/v3.8.46. |
||
|
|
8853b257aa |
fix(dashboard): trust provider topology live state (#6322)
Trust live provider-topology state on the Home dashboard. Integrated into release/v3.8.46. |
||
|
|
dd12539a2c |
fix(api): Zod-validate POST /api/github-skills + document new gate envs + pin merge-integrity actions
Three latent heavy-CI reds surfaced by the VPS validation dispatch (the fast path never runs these gates): - t06 route-validation: POST /api/github-skills destructured request.json() blind — a non-array 'targets' would .map-crash. Now validateBody(zod) with defaults preserved (Hard Rule #7). Guard: tests/unit/github-skills-route-validation.test.ts (4/4). - env-doc-sync: document OMNIROUTE_SKIP_SYSTEM_TRUST (#6310) and the changelog-integrity gate envs CHANGELOG_BASE_REF/ALLOW_CHANGELOG_REMOVALS (#6300) in .env.example + ENVIRONMENT.md. - zizmor ratchet: the new merge-integrity job's checkout/setup-node uses were unpinned (+1 finding, 160 > baseline 159); pinned by SHA -> 158 (< baseline). |
||
|
|
b6ffe8ce20 |
fix(proxy): make "Test All" read-only + add bulk enable/disable (#6246) (#6299)
Merged into release/v3.8.45. Delta do par #6246 (Test-All read-only + bulk enable/disable), reconciliado com o núcleo #6296 já mergeado. CHANGELOG restaurado (ambos bullets do #6246 coexistem). Reds pré-existentes: dast-smoke (infra) + file-size drift. |
||
|
|
fecf888fd9 |
fix(quality): clear the cycle's 11 net-new ESLint errors + make validate-release-green suppressions-aware
- executor-kiro/save-call-log/call-logs-correlation tests: replace 15 'as any' casts with typed shapes (net-new no-explicit-any errors from #6213/#6216); prune the now-empty suppression entries so the frozen baseline stays exact - github-skills + usage/call-logs routes: raw toLowerCase().includes() search replaced by matchesSearch() (no-restricted-syntax — Turkish-safe search, behavior covered by tests/unit/call-logs-correlation-substring.test.ts and tests/unit/github-collector.test.ts) - validate-release-green.mjs: run ESLint with --suppressions-location (match the npm run lint contract — frozen debt is not a release red) and raise the lint timeout 15->30min (a full pass takes ~14min alone; the 15min ceiling expired under concurrent suite load and surfaced as 'could not parse eslint json') |
||
|
|
faf68a222f |
fix(dashboard): null-guard connection in EditConnectionModal base-URL override (#6147) (#6287)
fix(dashboard): null-guard connection in EditConnectionModal (#6147) — fixes 'Cannot read properties of null (reading authType)' crash on every provider-detail page entry. TDD: connModals.test.tsx null-mount 9/9. Base-reds only. Integrated into release/v3.8.45. |
||
|
|
fc16dcd6ee |
feat(dashboard): filter Free Provider Rankings by configured/available (#6150) (#6251)
feat(dashboard): configured-only / available-only filters on Free Provider Rankings (#6150) — server-side query params + tested lib helper; supersedes the client-side #6245 toggle with an available-only dimension. Lib logic 11/11 green; UI validated live on VPS. Base-reds only. Integrated into release/v3.8.45. |
||
|
|
f237c07def |
Fix/5976 continued (#6216)
fix(combo): 5 streaming-path fixes (#5976) — locked-stream 500, error-frame-only-if-no-content, Gemini MALFORMED_RESPONSE→content_filter failover, correlationId substring, per-model-500 lockout skip + request-logger UI. Merged — thank you, @hartmark! Maintainer follow-up: releaseQualityClone cancels the abandoned quality-check tee branch (per-request memory) + regression test. All 41 combo/streaming quality tests green. Base-reds only. Integrated into release/v3.8.45. |
||
|
|
9899a6dacc |
fix(github-skills): add missing import, add unit tests, fix settings JSON parse (#6186)
feat(skills): GitHub skill-discovery subsystem — search/score/scan/import agent skills from GitHub, MCP tools (scope-gated) + /api/github-skills route (host-pinned api.github.com, sanitized errors). Merged — thank you, @Moseyuh333! Pre-merge fixes: passed toolDef.scopes so tools gate correctly under scope enforcement, routed the GET error path through sanitizeErrorMessage+500 (Hard Rule #12), and realigned the agent-skills count guards (catalog/routes/generator/mcp) for the new catalog entry. Base-reds only. Integrated into release/v3.8.45. |
||
|
|
1044821bda |
feat(combo): add option to disable session stickiness (#6168) (#6252)
feat(combo): add option to disable session stickiness (#6168) — per-combo/global, precedence config→settings→false (preserves #3825). TDD guard combo-disable-session-stickiness.test.ts (8/8). Base-reds only. Integrated into release/v3.8.45. (thanks @RCrushMe) |
||
|
|
cefbcfb278 |
feat(dashboard): routing/settings UX clarity — share %, Cloud Sync rename, base-URL override (#6147) (#6253)
feat(dashboard): routing/settings UX clarity (#6147) — effective share %, Cloud Sync→Remote Settings Sync rename, opt-in advanced base-URL override. TDD guard routing-settings-ux-6147.test.ts (6/6). Base-reds only. Integrated into release/v3.8.45. |
||
|
|
776a7a3a58 |
feat(providers): bulk-add API keys for Cloudflare Workers AI (#6174) (#6254)
feat(providers): bulk-add API keys for Cloudflare Workers AI (#6174). Per-entry providerSpecificData (fixes shared-object reuse); TDD guard bulk-api-key-parser-cloudflare.test.ts. Base-reds only. Integrated into release/v3.8.45. (thanks @muflifadla38) |
||
|
|
f26aa16da3 |
feat(rankings): add 'Configured Only' filter to Free Provider Rankings page (#6245)
feat(rankings): add 'Configured Only' filter to Free Provider Rankings (#6245, closes #6150). 9/9 test green. Thanks @Iammilansoni. Integrated into release/v3.8.45. |
||
|
|
0e0ca7e26b |
fix(dashboard): use connection.id (UUID) not connection.provider (category) in onboarding wizard href (issue #6144) (#6166)
refactor(dashboard): extract tested buildProviderDetailsHref helper for onboarding wizard (#6166). Behavioral #6144 fix already on tip via #6145; this lands the tested-helper hardening. Thanks @KooshaPari. Integrated into release/v3.8.45. |
||
|
|
7e2b839935 |
fix(security): require management auth for mutable cloud routes (#6233) (#6233)
fix(security): require management auth for mutable cloud routes (#6233). Verified: 3 PR tests + full authz/route-guard suite 241/241 green. Thanks @vittoroliveira-dev. Integrated into release/v3.8.45. |
||
|
|
9827ae6137 |
fix(api): count tool_use/tool_result/thinking blocks in count_tokens estimate (port from 9router#2337) (#6221)
fix(api): count tool_use/tool_result/thinking blocks in count_tokens estimate (#6221, port from 9router#2337). TDD-covered (6/6); typed the test casts to clear no-new-eslint. Reds are pre-existing base-red drift on release/v3.8.45 (dast-smoke #6228; executor-kiro.test.ts eslint anys; changelog/package.json version drift) — none introduced by this PR. Thanks @luweiCN. Integrated into release/v3.8.45. |
||
|
|
e755c5ac68 |
fix(providers): refresh GitHub Copilot catalog (#6154)
* fix(providers): refresh github copilot catalog Limit GitHub Copilot discovery to the curated supported model set and keep the provider cooldown panel client-safe by moving countdown formatting out of localDb. * chore(quality): rebaseline providerPageHelpers.ts file-size (+13, #6154 copilot catalog) The GitHub Copilot catalog refresh grows the provider-page model-section helper (1021->1034). Fast-path PR->release skips check:file-size, so the bump lands with the PR. Justification recorded in file-size-baseline.json. Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> --------- Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> |
||
|
|
6816bcdaf3 |
fix(dashboard): providers page data-timeout guard + live-ws standalone wiring (#6211)
* fix(dashboard): providers page data-timeout guard + live-ws standalone wiring Captura de trabalho em progresso: timeout de dados na página de providers, ajuste em ProviderLimits e instrumentation-node, com testes novos (providers-page-data-timeout, live-ws-standalone-wiring). * chore(quality): rebaseline ProviderLimits/index.tsx file-size (+6, #6211 data-timeout guard) Cohesive fix growth from PR #6211's data-timeout guard on the quota page's two first-paint fetches (1121->1127). The fast-path PR->release skips check:file-size, so the bump lands with the PR. Justification recorded in file-size-baseline.json. |
||
|
|
8a7b62ec85 |
fix(dashboard): remove the always-on Auto-Routing (combo) banner from the home page (#6164)
The blue "Auto-Routing Active — OmniRoute is automatically routing requests using combo-based strategies" banner was rendered unconditionally on the home page (`/home`, the default dashboard landing) — it did NOT reflect whether auto-routing was actually active, and reappeared on every fresh browser / private window / cleared localStorage (dismissal is stored per-browser). It added noise to the landing page without conveying live state. Remove it: drop the <AutoRoutingBanner /> usage + import from home/page.tsx and delete the now-unused component and its test. |
||
|
|
e44f125992 |
fix(dashboard): stop model-test error freezing the page (React #31 object toast) (#6161)
Clicking 'test' on a provider model (e.g. a ClinePass flash model) could freeze the entire dashboard. Root cause: POST /api/models/test returned an OBJECT in `error` on the Zod-validation and invalid-JSON paths (`validation.error.format()` / a details object). The client does `notify.error(data.error)`, and NotificationToast renders the message directly as a React child — an object throws React #31 ('Objects are not valid as a React child'), crashing the tree = frozen page instead of a toast. Fixed in three layers (defense in depth): 1. Server (root cause): /api/models/test now returns a STRING `error` on every path — flattens Zod issues to text, returns 'Invalid JSON body' for bad JSON. 2. Client: onTestModel funnels the response through extractApiErrorMessage() so any object-shaped error is coerced to a string before notify.error. 3. Toast: NotificationToast coerces title/message via toToastText() — a resilient catch-all so no future caller can freeze the page with a non-string. Tests (Rule #18, both node:test / blocking suite): - tests/unit/models-test-error-shape.test.ts — asserts STRING error on Zod-fail, missing-field, and invalid-JSON (fails on the pre-fix route: 3/3 red -> green). - tests/unit/notification-toast-coercion.test.ts — toToastText coercion matrix. |
||
|
|
cf6c2798b4 |
fix(oauth): extract keychain-import-only guard to restore file-size freeze (base-red) (#6158)
`src/app/api/oauth/[provider]/[action]/route.ts` grew to 959 lines, past its frozen cap of 924 (`check:file-size` → Fast Quality Gates red on release/v3.8.44). The growth came from #6054 (graceful 400 for keychain-import-only providers / zed): a doc block, two Sets (KEYCHAIN_IMPORT_ONLY_PROVIDERS, OAUTH_FLOW_ACTIONS) and a keychainImportOnlyResponse() helper, plus two duplicated guard blocks in GET/POST. That is a cohesive, self-contained leaf, so extract it to a new `keychainImportOnly.ts` exposing `keychainImportOnlyGuard(provider, action)` (returns the 400 NextResponse or null). The two route callsites collapse to a 2-line guard each. route.ts: 959 -> 918 (< 924, freeze restored). No behavior change. Tests (Rule #8/#18): - Existing tests/unit/oauth-keychain-import-only-6041.test.ts (route-level GET/POST zed 400) still pass unchanged — behavior preserved. - New tests/unit/oauth-keychain-import-only-guard.test.ts pins the extracted guard in isolation (zed+flow -> 400, normal provider -> null, zed+non-flow -> null). |
||
|
|
1473261c4c | fix(backend): distinct max_input_tokens for GPT-family models (#6191) (#6230) | ||
|
|
670d502bd9 | fix(auth): clear error for stale-key decryption failures (#6148) (#6226) | ||
|
|
8cb7f00821 | fix(services): 9Router embed route + pre-spawn port probe (#6205) (#6227) | ||
|
|
2d5bd41261 | fix(api): stabilize relay SSRF-guard binding for minified builds (#6149) (#6224) | ||
|
|
059dbe9f13 |
feat(quality): no-new-warnings por PR — ESLint bulk suppressions + lint-guard fork-condicional (Pacote 4) (#6218)
* 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. |
||
|
|
1bda6c15dc |
Release v3.8.44 (#5925)
* fix(install): add pnpm-workspace.yaml allowBuilds + pnpm.json for pnpm 11+ pnpm 11 introduced ERR_PNPM_IGNORED_BUILDS for native addon packages. Without explicit allowBuilds approval, these packages silently skip build scripts and OmniRoute fails to start with missing native modules. Changes: - pnpm-workspace.yaml: Set allowBuilds=true for all 13 native addon packages (@parcel/watcher, @swc/core, better-sqlite3, core-js, esbuild, keytar, koffi, libxmljs2, onnxruntime-node, protobufjs, sharp, tls-client-node, unrs-resolver) - pnpm.json: Migrate onlyBuiltDependencies from package.json (deprecated field) to the new pnpm.json config file per pnpm 11 spec. Tested on: pnpm 11.9.0, Node 24, Windows 11. Fixes: pnpm install ERR_PNPM_IGNORED_BUILDS on fresh clone with pnpm 11. * chore(release): open v3.8.44 development cycle * test(security): parse Kimi Web URL host instead of substring match (CodeQL #689) (#5928) Alert js/incomplete-url-substring-sanitization: the Kimi Web executor test asserted result.url.includes("www.kimi.com"), which a hostile host like www.kimi.com.evil.net would also satisfy. Parse the URL and assert on the exact hostname (new URL(result.url).hostname === "www.kimi.com"), which is both a stronger check and clears the CodeQL warning. * refactor(translator): extract thinking-budget fitting from openai-to-claude (#5932) Extract the thinking-budget fitting cluster (fitThinkingToMaxTokens + private safeCapMaxOutputTokens + MIN_* constants) verbatim into the pure leaf openai-to-claude/thinkingBudget.ts. Host re-exports fitThinkingToMaxTokens so external importers keep working and imports it back for internal use. Host 822 -> 738 LOC (under the 800 cap). No behavior change: byte-identical bodies, public export set unchanged. Adds a split-guard test; all consumer tests stay green (translator-openai-to-claude, strip-empty, minimax-m3, passthrough). * chore(release): pipeline hardening — test-masking pre-flight gate + contributors/uncovered helpers (#5926) * chore(ci): add test-masking PR-context gate to release-green pre-flight Reproduce check:test-masking (vs origin/main) inside validate-release-green so non-allowlisted net-assert reductions surface in the local pre-flight instead of in a ~40-min CI layer on the release PR. run() now merges a per-gate opts.env so GITHUB_BASE_REF reaches the child. HARD gate; skipped under --quick. Context: v3.8.43 release cost 3 CI round-trips for PR-context gates (test-masking, file-size, pr-evidence) that check:release-green did not reproduce locally. * chore(release): add contributors generator + uncovered-commit reconciliation helpers - scripts/release/gen-contributors.mjs: reproducible `### 🙌 Contributors` table for a CHANGELOG version (parenthetical-group parser → accurate per-PR attribution, noise-handle denylist). v3.8.43 shipped without the section (a real miss) because it was hand-built. npm run release:contributors <version> [--inject]. - scripts/release/list-uncovered-commits.mjs: lists commits since the last tag with no CHANGELOG bullet (v3.8.43 had 123/176 uncovered at reconciliation start). Advisory, maintainer-side. npm run release:uncovered. - 20 unit tests (parenthetical attribution, noise exclusion, idempotent injection, coverage window). * chore(quality): absorb web-cookie-providers-new file-size drift from #5928 (base-red on release/v3.8.44) * refactor(translator): split openai-responses request translator into pure leaves (#5940) Extract the shared pure primitives and the chat->Responses direction out of the 894-line openai-responses.ts request translator: - openai-responses/helpers.ts: pure primitives (toRecord/toString/clampCallId/ normalizeVerbosity/etc + markers/regexes/JsonRecord), zero host imports - openai-responses/toResponses.ts: openaiToOpenAIResponsesRequest (chat->Responses), imports the helpers leaf Host keeps openaiResponsesToOpenAIRequest (Responses->chat, imported by production) plus both register() directions, and re-exports openaiToOpenAIResponsesRequest so external importers (tests) keep working. Host 894 -> 529 LOC (under the 800 cap). Verbatim bodies (multiset check: leaf A 54/54, leaf B 294 lines, fn1 intact), public export set unchanged, leaves never import the host (no cycle). Adds a split-guard test; all consumer tests stay green (responses-translation-fixes 37, verbosity 4, reasoning-effort 4, orphaned-tool-filter 8, empty-tool-name-loop 8, headroom-responses-format 3). * chore(ci): pr-evidence FAIL output tells you to push (body edit does not re-run the gate) (#5944) ci.yml ignores the 'edited' event, so adding the Evidence block to the PR body after a push does not re-run check:pr-evidence — you need another commit. The FAIL report now says so, at the exact place someone sees the red check. + 5 unit tests (classification + hint-on-fail / no-hint-on-pass). Decided against a separate edited-triggered workflow: pr-evidence is not a required check (no ruleset gates it; release PRs merge UNSTABLE, not BLOCKED), so the gap is cosmetic and the generate-release skill already puts Evidence in the body before the first push. * fix(providers): Perplexity Web emits real tool_calls in streaming mode (mirror chatgpt-web toolMode) (#5927) (#5937) Perplexity Web (Pro/Max) only converted <tool>{...}</tool> text into OpenAI tool_calls for non-streaming requests (hasTools && !stream). Streaming requests -- the default for agentic coding clients -- got the raw <tool> text as plain delta.content and never emitted a tool_calls SSE delta, so clients could not execute tools. Reuses the provider-agnostic buildToolModeResponse()/ toolCompletionToSseStream() helpers already shipped for chatgpt-web (#5240): when tools are requested, buffer the full completion and convert it into either a JSON completion or a terminal SSE replay carrying delta.tool_calls + finish_reason: tool_calls, regardless of the caller's stream flag. Extended buildToolModeResponse()'s idSeed to be caller-supplied (default 'cgpt', perplexity-web passes 'pplx') so tool_call ids stay provider-specific without duplicating the helper. Non-tool streaming is unchanged (still lives token-by-token via buildStreamingResponse). * fix(discovery): resolve duplicate /v1 paths and redirect aborts (#5904) Integrated into release/v3.8.44. Thanks @hamsa0x7 for diagnosing the doubled /v1 discovery path and the REDIRECT_BLOCKED probe-loop abort (#5899). De-scoped to the discovery fix (the #5903 session-affinity work is handled by #5943) and added Rule #18 regression guards. * docs(changelog): record #5926 + #5944 (release-pipeline hardening) under v3.8.44 Maintenance (#5952) * docs(claude): add Hard Rule #22 — cross-session safety (git stash + in-flight PRs) (#5955) Integrated into release/v3.8.44 — Hard Rule #22 (cross-session safety). * refactor(translator): extract pure helpers from response/openai-responses (#5949) Extract the 5 stateless helpers (normalizeToolName, stripEmptyOptionalToolArgs, normalizeOutputIndex, normalizeUpstreamFailure, extractResponsesReasoningSummaryText) verbatim into the pure leaf openai-responses/pureHelpers.ts (no stream state, no host import). Host imports them back and re-exports normalizeUpstreamFailure for external importers (tests). Host 1091 -> 1001 LOC. The stateful streaming core stays in the host (out of scope). Byte-identical bodies (multiset 73/73), no cycle. Adds a split-guard; consumer tests stay green (responses-translation-fixes 37, combo-param-validation-fallback-4519 5). * docs(compression): document upstream sync policy for RTK/Caveman engines (#5830) (#5948) Integrated into release/v3.8.44 — docs-only upstream sync policy for RTK/Caveman engines (closes #5830). All 7 checks green. * fix(sse): strip ANSI/VT100 codes from gemini-cli stream frames (#5934) Integrated into release/v3.8.44 — ReDoS-safe ANSI/VT100 strip for gemini-cli stream frames (port of upstream #2273, thanks @anki1kr). PR test green (5/5), file-size gate OK. * fix(translator): strict Anthropic content-block compliance in antigravity→openai request (#5935) Integrated into release/v3.8.44 — strict Anthropic content-block compliance in antigravity→openai (port upstream #2296). PR test green (9/9). UNSTABLE red is the pre-existing environmental setup-claude base-red (opencode-plugin dist not built in fast-path), not a regression from this PR. * fix(mcp): auto-recover stale streamable HTTP sessions on initialize (#5957) Integrated into release/v3.8.44 — MCP stale streamable-HTTP session auto-recovery (thanks @Chewji9875). * fix(providers): validate v0 Platform API keys via chats endpoint (#5954) Integrated into release/v3.8.44 — v0-vercel Platform API key validation (thanks @vittoroliveira-dev). * fix(api): relax provider-scoped chat completion validation (#5907) Integrated into release/v3.8.44 — relaxed provider-scoped chat validation + regression test (thanks @nickwizard). * fix(providers): strip /v1 unconditionally to avoid /v1/v1/models fetch error (#5899) (#5920) Integrated into release/v3.8.44 — unconditional /v1 strip in both models-discovery paths + regression test (thanks @anki1kr). * fix(resilience): per-window is_exhausted + honor quota-exhaustion preflight for priority combos (#5923) (#5941) Integrated into release/v3.8.44. * fix(resilience): honor active codex session affinity over per-request reset-aware re-scoring (#5903) (#5943) Integrated into release/v3.8.44. * fix(thinking): only inject redacted_thinking replay block when tool_use present and thinking enabled (#5945) (#5953) Integrated into release/v3.8.44. * feat(providers): add ClinePass API-key provider (#5942) Integrated into release/v3.8.44 — ClinePass API-key (BYOK) provider (port upstream 9router#2304, co-authored @adentdk). Validated locally: 16 clinepass tests green; fixed the APIKEY count 158→159 + translate-path golden snapshot (clinepass is a genuine new provider). Remaining UNSTABLE red is the pre-existing environmental setup-claude base-red (opencode-plugin dist not built in fast-path). Supersedes stub #5541. * feat(api): add /v1/ocr endpoint (Mistral OCR) + Mistral moderation (#5950) Integrated into release/v3.8.44 — /v1/ocr endpoint (Mistral OCR) + Mistral moderation (port upstream 9router#2064, co-authored @waguriagentic). Validated locally: 14 ocr-route tests + moderation/servicekind/endpoint-category suites green (CORS→Zod→handler + no-stack-leak assertion). Reds are inherited DRIFT only: cognitive-complexity ratchet (none from OCR files — pre-existing cycle drift, rebaselined at release) + environmental setup-claude base-red. * fix(codex): convert chat json schema to responses text format (#5933) Integrated into release/v3.8.44 — converts Chat Completions json_schema response_format → Responses API text.format on the Codex path, and preserves existing text.format through verbosity normalization. Base redirected main→release; the openai-responses.ts split that landed this cycle was reconciled by re-applying the delta onto openai-responses/toResponses.ts. Validated locally: 48 translator-openai-responses-req + 8 codex-verbosity tests green. Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * feat(providers): add Claude Sonnet 5 support across the model pipeline (#5833) Integrated into release/v3.8.44 — wires claude-sonnet-5 end-to-end (registries, modelSpecs, pricing ×3, cost, Sonnet-family fallback, 1M-ctx, static models). Reconciled the add/add overlap with the already-merged #5796 (kept the PR's superset test with the family-fallback assertion). Validated locally: kiro-sonnet-5 + catalog + pricing/modelSpecs/fallback suites all green. Thanks @ggiak! Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * feat(relay): gate bifrost auto routing by provider manifest (#5870) Integrated into release/v3.8.44 — gates Bifrost auto-routing by the provider plugin manifest (only manifest-eligible providers reach the sidecar; ineligible/unknown fall back to the TS path with explicit reasons). Superset of #5869 (carries the full manifest + registry + docs). Resolved an integration-test conflict in favor of the release (which already subsumes this PR's readiness/removeDirWithRetry improvements). Validated locally: 4 provider-plugin-manifest + 11 relay-routing-backend tests green. Thanks @KooshaPari! Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * refactor(translator): extract pure message helpers from openai-to-kiro (852→751) (#5947) * refactor(translator): extract pure message helpers from openai-to-kiro Extract the pure tool/message helpers (parseToolInput, normalizeKiroToolSchema, serializeToolResultContent) verbatim into the leaf openai-to-kiro/messageHelpers.ts. The host imports them back for convertMessages. They were module-private, so the public export set is unchanged (no re-export needed). Host 852 -> 751 LOC. Byte-identical bodies (multiset 99/99), leaf has zero imports (no cycle). Adds a split-guard; consumer tests stay green (translator-openai-to-kiro 33, translator-ai-sdk-image-parts 3). * chore: re-trigger CI (stuck runner on 2/2 shard) * refactor(executors): extract pure prompt + composer helpers from cursor (#5960) Extract two pure clusters from the cursor executor into sibling leaves: - cursor/prompt.ts: isRecordLike + toolChoiceDirectiveLine + buildCursorOutputConstraints - cursor/composer.ts: composer thinking-as-content decoding (isComposerModel, visibleComposerContentFromThinking, composerReasoningRemainder + markers) Host imports both back for internal use and re-exports the 3 composer helpers for external importers (tests). Host 1576 -> 1451 LOC. Byte-identical bodies (verbatim multiset prompt 65/65, composer 32/32), leaves have zero imports (no cycle). Adds a split-guard; consumer tests stay green (cursor-composer-thinking, cursor-streaming, cursor-agent-tool-calls, translator-openai-to-cursor, cursor-agent-system-prompt). * refactor(executors): extract pure SSE-collect parsing from antigravity (#5962) Extract the pure SSE-payload -> collected-stream parser (AntigravityCollectedStream, stripZeroWidth, parseAntigravityTextualToolCall, addAntigravityTextualToolCall, processAntigravitySSEPayload/Text, flushAntigravitySSEText) verbatim into the leaf antigravity/sseCollect.ts. Host imports the helpers it uses and re-exports processAntigravitySSEPayload for external importers (tests). Host 1812 -> 1671 LOC. Byte-identical bodies (verbatim multiset 135/135), leaf does not import the host (no cycle). Credit/quota state, auth, and HTTP dispatch untouched. Adds a split-guard; consumer tests stay green (executor-agy 8, executor-antigravity 26, antigravity-sse-collect-socket-release, copilot-agent-antigravity-parity 6). * refactor(executors): extract pure model maps + resolvers from chatgpt-web (#5967) Extract the static model maps (MODEL_MAP, MODEL_FORCED_EFFORT, THINKING_CAPABLE_SLUGS) and the pure thinking-effort resolvers (isThinkingCapableModel, normalizeThinkingEffort, resolveThinkingEffort, ResolvedChatGptModel, resolveChatGptModel) verbatim into the pure leaf chatgpt-web/models.ts. Host imports the two resolvers it uses back. Host 3205 -> 3076 LOC. Byte-identical bodies (verbatim multiset 120/120), leaf has zero imports (no cycle). Auth/PoW/session/HTTP dispatch and all module caches untouched. Adds a split-guard; consumer tests stay green (chatgpt-web 86, chatgpt-web-tools-5240 4, chatgpt-web-sha3-boringssl-5531 5). * refactor(executors): decompose grok-web into pure tool/markup leaves (#5994) Extract the pure OpenAI<->Grok tool-translation, native-tool mapping, markup cleanup, and NDJSON stream types out of the 1872-line grok-web executor into 4 sibling leaves: - grok-web/types.ts: GrokStreamResponse/GrokStreamEvent (stream types) - grok-web/tool-bridge.ts: OpenAI<->Grok tool translation + registry + classifiers - grok-web/native-tools.ts: native-tool selection/scoring + native->OpenAI mapping - grok-web/text-cleanup.ts: Grok markup stripping + GrokMarkupFilter Layered, acyclic: types <- tool-bridge <- native-tools; text-cleanup <- types; host imports the leaves. All symbols module-private (no host re-export). Host 1872 -> 887 LOC. Byte-identical bodies (verbatim per-leaf), no cycle, all new leaves <= 800 cap (tool-bridge split at line 753 to stay under). Auth/cookie/TLS/HTTP dispatch untouched. Adds a split-guard; consumer tests stay green (grok-web 62, grok-cli-oauth 15, grok-cli-strip-params 2). * refactor(executors): extract pure quota parsing from codex (#5999) Extract the pure Codex quota-snapshot parsing + reset/cooldown scheduling (CodexQuotaSnapshot, parseCodexQuotaHeaders, getCodexResetTime, getCodexDualWindowCooldownMs) verbatim into the leaf codex/quota.ts. Host re-exports the 4 symbols so handlers/chatCore/codexQuota.ts + tests keep resolving. Host 1539 -> 1427 LOC. Byte-identical bodies (verbatim 98/98), leaf has zero imports (only Date, no cycle). WS transport, auth, HTTP dispatch untouched. Adds a split-guard; consumer tests stay green (executor-codex 40, codex-quota-fetcher 7, chatcore-codex-quota 5). * refactor(executors): extract pure stream formatters from deepseek-web (#6000) Extract the pure content/citation formatters (isThinkingModel, isSearchModel, cleanDeepSeekToken, formatStreamContent, DeepSeekSearchResult, appendSearchCitations) verbatim into the leaf deepseek-web/stream-format.ts. Host imports the 5 it uses back into transformSSE/collectSSEContent (cleanDeepSeekToken stays internal to the leaf). Host 1147 -> 1108 LOC. Byte-identical bodies (verbatim 34/34), leaf has zero imports (no cycle), all module-private (no re-export). PoW/auth/token-cache/HTTP dispatch untouched. Adds a split-guard; consumer tests stay green (deepseek-web 35, deepseek-web-rolling-window-2942 5, deepseek-web-tools-execute 3). * refactor(api): add validatedJsonBody helper (salvage #5075) (#5931) Fuses JSON body parsing + Zod validation into a single call that returns either type-narrowed data or a ready-to-return 400 NextResponse with the standard error envelope. Salvaged as the Tier 1 portable helper from the closed refactor PR #5075; the bulk route migration is intentionally not ported. Adds a focused 6-case regression test. Co-authored-by: KooshaPari <KooshaPari@users.noreply.github.com> * feat(qoder): drive PAT auth via qodercli, add dashboard quota, fix connection display (#5816) Integrated into release/v3.8.44 — Qoder PAT auth via qodercli binary + dashboard quota + dual-auth connection fix. Thanks @AgentKiller45 (co-author @judy459)! Validated locally (release-green on its own merits): lint 0, typecheck:core 0, 104 qoder/usage/UI tests green, file-size gate OK (owner-approved qoderCli.ts baseline-freeze 666→989), env-doc-sync fixed (documented QODER_CLI_CONFIG_DIR). The 2 remaining CI reds are INHERITED base-reds, not caused by this PR: (1) LEDGER-4 minimax-m3 supportsVision (minimax-m3 base + cline-pass/minimax-m3 from the already-merged #5942); (2) mutation-test-coverage missing 3 tests in stryker.conf (#5903/#5942/#5923). Both cleaned up separately. Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> * fix(providers): minimax-m3 supportsVision (LEDGER-4) + stryker tap.testFiles drift (#6012) Release-green cleanup — clears LEDGER-4 minimax-m3 supportsVision + stryker tap.testFiles drift base-reds. Validated locally. * fix(registry): flag cline-pass/minimax-m3 as multimodal (supportsVision) (#6003) The cline-pass provider's minimax-m3 entry was missing supportsVision, breaking the LEDGER-4 registry-consistency test (all minimax-m3 entries must set supportsVision to match lite.ts — minimax-m3 is multimodal). Every other minimax-m3 registry entry (trae, bazaarlink, cline, ollama-cloud, ...) already sets it. This was a base-red on release/v3.8.44 inherited by every open PR. Validated by the existing failing-then-passing guard tests/unit/review-reviews-v3814-fixes.test.ts (LEDGER-4). * refactor(executors): extract pure payload construction from claude-web (#6006) Extract the pure Claude-web payload types + transforms + default tools/style (ClaudeWebRequestPayload, ClaudeWebStreamChunk, DEFAULT_CLAUDE_MODEL, generateMessageUUIDs, getDefaultTools, getDefaultPersonalizedStyle, transformToClaude, transformFromClaude) verbatim into the leaf claude-web/payload.ts. Host imports the 3 it uses back (ClaudeWebRequestPayload type + the two transforms). Host 1056 -> 835 LOC. Byte-identical bodies (verbatim 149/149), leaf imports only randomUUID (no host import, no cycle), all module-private (no re-export). Cookie/auth/ Turnstile/TLS/HTTP dispatch untouched. Adds a split-guard; consumer tests stay green (claude-web 13, claude-web-auto-refresh 6). * refactor(executors): extract pure upstream-header helpers from base (#6008) Extract the pure upstream-header helpers (mergeUpstreamExtraHeaders, getCustomUserAgent, setUserAgentHeader, applyConfiguredUserAgent, isOpenAICompatibleEndpoint, stripStainlessHeadersForOpenAICompat) verbatim into the leaf base/headers.ts. base.ts is imported by ~18 executors, so the host re-exports all 6 to keep those import paths intact; it also imports the 4 it uses internally in the BaseExecutor class. The trivial JsonRecord type alias is redefined locally in the leaf to avoid a base<->leaf cycle. Host 1539 -> 1451 LOC. Byte-identical bodies (verbatim 78/78), leaf does not import the host (no cycle). typecheck:core validates all base importers still resolve via the re-export. Adds a split-guard; consumer tests stay green (executor-base-utils 22, executor-default-base 49, executor-strip-stainless-openai-compat 6, plus executor sanity via typecheck). * refactor(executors): extract pure wire protocol from perplexity-web (#6014) Extract the pure Perplexity wire protocol (consts, SSE stream types, SSE parsing, OpenAI<->Perplexity message translation, request/query builders, content extraction, sseChunk) verbatim into the leaf perplexity-web/protocol.ts. Host imports back the 10 symbols it uses; everything module-private (no re-export). Session cache, TLS fetch, auth, and the executor class stay in the host. Host 1028 -> 534 LOC. Byte-identical bodies (verbatim), leaf imports only randomUUID (no host import, no cycle). Adds a split-guard; consumer tests stay green (perplexity-web 26, streaming-tools-5927 2, tls-client 6, key-validation-models 2). * refactor(executors): extract pure URL normalizers from default (#6015) Extract the pure per-provider chat-URL normalizers (normalizeBailianMessagesUrl, normalizeDataRobotChatUrl, normalizeAzureAiChatUrl, normalizeWatsonxChatUrl, normalizeOciChatUrl, normalizeSapChatUrl, normalizeXiaomiMimoChatUrl, normalizeOpenAIChatUrl, getOpenRouterConnectionPreset) verbatim into the leaf default/urlNormalizers.ts. Host imports them back into buildUrl/transformRequest; the now-dead build*ChatUrl/normalizeBaseUrl imports move to the leaf. All module-private (no re-export). Host 864 -> 815 LOC (shrunk below its frozen baseline). Byte-identical bodies (verbatim 45/45), leaf does not import the host (no cycle). buildHeaders/execute/auth untouched. Adds a split-guard; consumer tests stay green (executor-default-base 49, anthropic-compatible-bearer 3, strip-client-metadata 3). * feat(webfetch): support self-hosted FireCrawl instances (#5793) Integrated into release/v3.8.44 — self-hosted FireCrawl support (FIRECRAWL_BASE_URL/FIRECRAWL_TIMEOUT_MS). Re-cut clean onto the release tip (branch was fossilized from a pre-v3.8.40 snapshot). Validated: 4 firecrawl tests green, env-doc-sync + docs-sync pass. UNSTABLE red is the inherited environmental setup-claude base-red. * feat(xai): register XaiExecutor with reasoning-effort suffix parsing (#5800) Integrated into release/v3.8.44 — XaiExecutor with reasoning-effort suffix parsing. Re-cut clean onto the release tip (branch was fossilized). Validated: 6 xai-executor tests green, provider-consistency OK, typecheck:core 0 errors, env-doc-sync in sync. UNSTABLE red is the inherited environmental setup-claude base-red. * feat(discovery): Phase 2 — reporter, /api/discovery/* routes (strict loopback-only) + dashboard UI (#5939) * feat(discovery): Phase 2 reporter — discoveryResults DB module + service wiring Adds src/lib/db/discoveryResults.ts (CRUD over the discovery_results table from migration 074) and wires the opt-in discovery service to persist and read findings through it: persistDiscoveryResult / getDiscoveryResults / getDiscoveryResultById / markVerified / deleteDiscoveryResult, with (provider, method, endpoint) upsert de-duplication. Re-exported from localDb. The service stays opt-in / default-off. The /api/discovery/* routes and the dashboard UI tab are intentionally deferred to Phase 2b — they need the local-only enforcement model (Hard Rules #15/#17 territory) decided first. TDD: tests/unit/db/discovery-results.test.ts (8 cases, DB + service delegation), isolated DATA_DIR with resetDbInstance cleanup. * feat(discovery): Phase 2b — /api/discovery/* routes (strict loopback-only) Adds the discovery HTTP surface on top of the reporter DB module: GET /api/discovery/results list findings (optional ?providerId) GET /api/discovery/results/:id one finding (404 if absent) DELETE /api/discovery/results/:id delete a finding POST /api/discovery/scan scan a provider + persist findings POST /api/discovery/verify/:id mark a finding verified Authorization: strict loopback-only. "/api/discovery/" is added to LOCAL_ONLY_API_PREFIXES so the central authz pipeline (proxy.ts → runAuthzPipeline → managementPolicy) rejects non-loopback callers with a 403 LOCAL_ONLY before any handler runs. It is deliberately NOT in LOCAL_ONLY_MANAGE_SCOPE_BYPASS_PREFIXES — no remote manage-scope bypass — because POST /scan issues outbound probes to provider endpoints (SSRF-adjacent) and must never be tunnel-reachable. Handlers also call requireManagementAuth (defense in depth) and return sanitized errors via createErrorResponse. Tests: - tests/unit/authz/discovery-routes-local-only.test.ts (8) — security guard: isLocalOnlyPath true + not manage-scope-bypassable for all four paths. - tests/unit/api/discovery-routes.test.ts (6) — handler integration over an isolated DATA_DIR: list/filter, by-id 200/404/400, scan persist + 400 on empty/malformed body, verify 200/404, delete 200/404, no stack-trace leak. * feat(discovery): Phase 2c — dashboard UI tab (Tools → Discovery) Adds the /dashboard/discovery page (DiscoveryPageClient) that consumes the Phase 2b /api/discovery/* routes: scan a provider, list findings, verify or delete them. Registered in the sidebar under the Tools group (icon travel_explore) and given a "discovery" i18n namespace + sidebar keys in en.json (other locales fall back to en via next-intl until synced — the locale files are in a pre-existing coverage deficit unrelated to this change). Registers the UI test path in vitest.config.ts (advisory ui suite). Tests: src/app/(dashboard)/dashboard/discovery/__tests__/DiscoveryPageClient.test.tsx (3 cases: loads+renders results, empty state, fetches /api/discovery/results on mount; stable useTranslations mock to avoid the fetch-loop). NOTE: the ui vitest suite cannot run in this workspace — @testing-library/dom (a @testing-library/ react peer dep) is absent from node_modules, which fails ALL existing ui tests equally; the test runs in CI. Component verified locally via typecheck + lint. * test(discovery): register discovery-routes-local-only in stryker tap.testFiles The mutation-test-coverage gate (--strict) flags any unit test covering a mutated module that isn't listed in stryker.conf.json tap.testFiles. This PR's tests/unit/authz/discovery-routes-local-only.test.ts covers src/server/authz/ routeGuard.ts (a mutated module, which this PR edits by adding the /api/discovery/ local-only prefix), so it must be registered for its mutant kills to count. No behavior change. * refactor(discovery): split DiscoveryPageClient to satisfy max-lines-per-function The complexity ratchet (max-lines-per-function: 80) flagged the single 184-line DiscoveryPageClient function (+1 over baseline). Extract the data layer into two hooks (useDiscoveryResults for list/loading/feedback, useDiscoveryActions for scan/verify/delete), a shared callApi helper, and two presentational sub-components (DiscoveryScanForm, DiscoveryResultCard). Every function is now under the 80-line ceiling; complexity gate back to baseline 1995. No behavior change — same exported component, same endpoints, same props. * test(sidebar): include discovery in omni-proxy item-order snapshot Adding the Discovery item to the Tools group (this PR's sidebar entry) extends the ordered omni-proxy section list. Update the exact-match deepEqual snapshot in sidebar-visibility.test.ts to include "discovery" in its position (after traffic-inspector). The assertion stays exact — this reflects the intentional new item, it does not weaken the check. * docs(changelog): restore release bullets eaten by merge auto-resolve; re-add discovery bullet additively * chore(quality): bump testFrozen for translator-openai-responses-req.test.ts (1097 -> 1172) Base-red inherited from #5933, which grew the test file to 1171 lines (Hard Rule #18 regression tests) without adjusting the frozen cap. The release tip itself fails check:file-size; this unblocks every PR into release/v3.8.44. File untouched by this PR. * chore(quality): restore stryker tap.testFiles entries eaten by merge auto-resolve The merge of origin/release/v3.8.44 silently dropped the 3 entries added on the release side (#5903, clinepass, #5923). Took the release version verbatim and re-added only this PR's entry (discovery-routes-local-only) in alphabetical order. check:mutation-test-coverage green locally. * chore(quality): reconcile inherited v3.8.44 merge-burst drift + include discovery in tools-group order test - complexity 1995->2003 and cognitive 856->859: both measure IDENTICAL on the pristine release tip ( |