From ec98525326429e94ecea4ad09ab11b4bb80a1e78 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza Date: Wed, 16 Sep 2026 10:10:32 -0300 Subject: [PATCH 1/3] fix(tests): list dist/httpClientAbortGuard.mjs in the pack-artifact policy assertion (#13872) Merged on local evidence: the assertion is red on a pristine release/v3.8.51 (19 pass / 1 fail) and green here (20/0). One-line test fix; the packaging policy itself is untouched. --- changelog.d/fixes/13636-pack-artifact-policy-abort-guard-test.md | 1 + tests/unit/pack-artifact-policy.test.ts | 1 + 2 files changed, 2 insertions(+) create mode 100644 changelog.d/fixes/13636-pack-artifact-policy-abort-guard-test.md diff --git a/changelog.d/fixes/13636-pack-artifact-policy-abort-guard-test.md b/changelog.d/fixes/13636-pack-artifact-policy-abort-guard-test.md new file mode 100644 index 0000000000..66266410b7 --- /dev/null +++ b/changelog.d/fixes/13636-pack-artifact-policy-abort-guard-test.md @@ -0,0 +1 @@ +- **fix(tests):** add `dist/httpClientAbortGuard.mjs` to the expected missing-paths list in `tests/unit/pack-artifact-policy.test.ts` — [#13636](https://github.com/diegosouzapw/OmniRoute/pull/13636) registered the file in `PACK_ARTIFACT_REQUIRED_PATHS` without updating the assertion, leaving the test red on the release tip for every PR that runs it ([#12732](https://github.com/diegosouzapw/OmniRoute/issues/12732)) diff --git a/tests/unit/pack-artifact-policy.test.ts b/tests/unit/pack-artifact-policy.test.ts index bd855302ac..8c5200fa36 100644 --- a/tests/unit/pack-artifact-policy.test.ts +++ b/tests/unit/pack-artifact-policy.test.ts @@ -299,6 +299,7 @@ test("findMissingArtifactPaths flags missing root runtime files in the tarball", "config/release/wreq-js-rust-notices.md", "dist/head-response-guard.cjs", "dist/http-method-guard.cjs", + "dist/httpClientAbortGuard.mjs", "dist/main-server-timeouts.mjs", "dist/open-sse/services/compression/engines/rtk/filters/generic-output.json", "dist/open-sse/services/compression/rules/en/filler.json", From 5faf44f975b6b887167b07c34923802982f46274 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza Date: Wed, 16 Sep 2026 10:40:27 -0300 Subject: [PATCH 2/3] fix(docs): restore the env/docs contract broken by the #13679 vars (#13875) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `check:env-doc-sync` is failing on the release tip, which fails "Docs Gates (fast-path)" on every open PR against release/v3.8.51 (base-red #13866). Both gaps come from #13679: - `OMNIROUTE_CLOUD_SYNC_ENFORCE_SIGNATURE` is read in src/lib/cloudSync.ts but was in neither .env.example nor ENVIRONMENT.md. Documented with the behaviour the code actually implements: opt-in rejection of an UNSIGNED response when no local secret is configured, default off for v3.8.x back-compat, and a present signature always verified — and always rejected when OMNIROUTE_CLOUD_SYNC_SECRET is unset — regardless of the flag. - `CDP_PROXY_TOKEN` was in .env.example but missing from ENVIRONMENT.md. Added to the ChatGPT Web (Codex) table next to CHATGPT_WEB_CODEX_CDP_URL, in that section's language, describing the X-Omni-Cdp-Token header the sidecar expects and the compose-network isolation that applies when it is unset. Docs only, no code change. Verified on this branch: check:env-doc-sync reports all three directions in sync (817 vars in .env.example, 834 in ENVIRONMENT.md); check:docs-sync passes; check:docs-counts reports only pre-existing soft drift. --- .env.example | 7 +++++++ docs/reference/ENVIRONMENT.md | 2 ++ 2 files changed, 9 insertions(+) diff --git a/.env.example b/.env.example index 1aa19cd898..f8f22af3e7 100644 --- a/.env.example +++ b/.env.example @@ -2748,6 +2748,13 @@ APP_LOG_TO_FILE=true # tokens (accessToken / refreshToken / providerSpecificData). Default OFF — # only non-credential metadata is synced. See docs/security/SOCKET_DEV_FINDINGS.md §5. # OMNIROUTE_CLOUD_SYNC_SECRETS=false +# +# Set to "true" to reject an UNSIGNED Cloud sync response when no local secret +# is configured (#13679). Default OFF keeps v3.8.x back-compat for peers that +# have not rotated in a shared secret yet; v3.9 flips the default to enforced. +# A signature that IS present is always verified, and always rejected when +# OMNIROUTE_CLOUD_SYNC_SECRET is unset, regardless of this flag. +# OMNIROUTE_CLOUD_SYNC_ENFORCE_SIGNATURE=false # ─── Zed import legacy compat (v3.8.6) ────────────────────────────────────── # Set to "true" to fall back to the v3.8.5 one-step "import everything from diff --git a/docs/reference/ENVIRONMENT.md b/docs/reference/ENVIRONMENT.md index e25c1c6ca6..cc66597bac 100644 --- a/docs/reference/ENVIRONMENT.md +++ b/docs/reference/ENVIRONMENT.md @@ -323,6 +323,7 @@ OmniRoute provides a two-layer defense: request-side injection scanning and resp | `OMNIROUTE_STANDALONE_DIR` | _.build/ standalone output_ | `scripts/build/colocate-standalone.mjs` | Build-time override for the standalone output directory consumed by the post-build colocation step. Not a runtime setting. | | `OMNIROUTE_CLOUD_SYNC_SECRET` | _(empty)_ | `src/lib/cloudSync.ts` | Shared secret used to verify the HMAC-SHA256 signature of Cloud Sync responses. | | `OMNIROUTE_CLOUD_SYNC_SECRETS` | `false` | `src/lib/cloudSync.ts` | Set to `true` to allow the Cloud Sync endpoint to overwrite local credentials. Default is `false`. | +| `OMNIROUTE_CLOUD_SYNC_ENFORCE_SIGNATURE` | `false` | `src/lib/cloudSync.ts` | Set to `true` to reject an unsigned Cloud Sync response when no local secret is configured (#13679). A signature that is present is always verified — and always rejected when `OMNIROUTE_CLOUD_SYNC_SECRET` is unset — regardless of this flag. The default flips to enforced in v3.9. | | `OMNIROUTE_ZED_IMPORT_LEGACY_ONE_STEP` | `false` | `src/app/api/providers/zed/import/route.ts` | Set to `true` to fall back to the v3.8.5 one-step "import everything" behavior without user confirmation. | | `NEXT_PUBLIC_BASE_URL` | `http://localhost:20128` | OAuth, Dashboard, sync | Public-facing URL for OAuth redirect_uri, Dashboard links, and generated public URLs. Set this to the stable public URL when OAuth callbacks or generated browser links must use a canonical reverse-proxy host. | | `NEXT_PUBLIC_CLOUD_URL` | _(empty)_ | Client-side | Client-side mirror of `CLOUD_URL`. | @@ -1658,6 +1659,7 @@ Globale Defaults für den headless Browser und den ausgehenden Tool-Tunnel. Im D | `CHATGPT_WEB_CODEX_CHROME_PATH` | _(auto-detect)_ | `open-sse/executors/chatgpt-web-codex.ts` | Expliziter Chrome-/Chromium-Pfad für npm-, systemd- und PM2-Betrieb. | | `CHROME_PATH` | _(auto-detect)_ | `open-sse/executors/chatgpt-web-codex.ts` | Gemeinsamer Fallback für einen expliziten Chrome-/Chromium-Pfad. | | `CHATGPT_WEB_CODEX_CDP_URL` | _(unset)_ | `open-sse/executors/chatgpt-web-codex.ts` | Interner CDP-Endpunkt; Docker verwendet den Sidecar auf Port `9223`. | +| `CDP_PROXY_TOKEN` | _(unset)_ | `docker/chatgpt-web-codex-browser/cdp-proxy.mjs` | Wenn gesetzt, muss jede Anfrage an den CDP-Proxy-Sidecar diesen Wert im Header `X-Omni-Cdp-Token` mitschicken (#13679). Ohne Wert leitet der Proxy unauthentifiziert weiter — dann schützt nur die Netzisolierung des Compose-Netzes `chatgpt-web-codex-net`. Erzeugen mit `openssl rand -hex 32`. | | `CHATGPT_WEB_CODEX_TUNNEL_ID` | _(unset)_ | `open-sse/executors/chatgpt-web-codex.ts` | Globale OpenAI-Tunnel-ID für lokale Codex-Tool-Runden. | | `CHATGPT_WEB_CODEX_RUNTIME_KEY` | _(unset)_ | `open-sse/executors/chatgpt-web-codex.ts` | Globaler Tunnel Runtime-Key; niemals in Logs ausgeben. | | `CHATGPT_WEB_CODEX_CONNECTOR_NAME` | `OmniRoute Codex v2` | `open-sse/executors/chatgpt-web-codex.ts` | Exakter Name des neu erstellten ChatGPT-Custom-Connectors für die MCP-Brücke. | From 1cb4c820714126724c7f37afe45c306fd24e4af0 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza Date: Wed, 16 Sep 2026 11:45:40 -0300 Subject: [PATCH 3/3] fix(ci): bound the forgotten-sibling report so an advisory step stops failing the job (#13889) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "Fast Quality Gates" is red on every open PR against release/v3.8.51. The failing step is `forgotten-sibling-tests`, which is explicitly advisory — its own output says "Report-only calibration: these findings do not fail the job" — yet it exits 1. When a PR diff touches a hub module (`open-sse/config/providerRegistry.ts` in the current reds), the analysis walks every import edge in the repo and multiplies each consumer by its candidate tests. The result reaches millions of rows, and `lines.join("\n")` then exceeds V8's maximum string length. The throw lands in main()'s catch, which exits 1 — so an advisory report takes the whole job down. Measured with a synthetic hub cross-product, before the change: 3,000,000 findings -> a 435 MB report string (no throw, but absurd) 4,500,000 findings -> Invalid string length (the CI failure, verbatim) After: the same 4,500,000 findings render as 27 KB. The fix bounds only the ENUMERATION. The header keeps the exact totals, so the signal ("this diff has N unreviewed sibling tests") is unchanged; at most 200 rows per section are listed, followed by a line naming how many were withheld. The JSON artifact gets the same treatment (5,000 items per array) plus an explicit `totals` object, since `JSON.stringify` would throw on the same input for the same reason. `markdown()` is exported so the bound is testable without a CI-sized diff. --- ...asereds-forgotten-sibling-report-bounds.md | 1 + .../check/check-forgotten-sibling-tests.mjs | 56 +++++++++++++++---- 2 files changed, 47 insertions(+), 10 deletions(-) create mode 100644 changelog.d/fixes/basereds-forgotten-sibling-report-bounds.md diff --git a/changelog.d/fixes/basereds-forgotten-sibling-report-bounds.md b/changelog.d/fixes/basereds-forgotten-sibling-report-bounds.md new file mode 100644 index 0000000000..18c0e435e7 --- /dev/null +++ b/changelog.d/fixes/basereds-forgotten-sibling-report-bounds.md @@ -0,0 +1 @@ +- **fix(ci):** the advisory `forgotten-sibling-tests` step no longer fails "Fast Quality Gates" when a PR touches a hub module — the cross-product of consumers × candidate tests reached millions of rows and rendering them exceeded V8's maximum string length, so the throw hit `main()`'s catch and exited 1. The report now lists at most 200 rows per section (and 5 000 per array in the JSON artifact) while the header keeps the exact totals diff --git a/scripts/check/check-forgotten-sibling-tests.mjs b/scripts/check/check-forgotten-sibling-tests.mjs index 6707fc8aa8..7c93e347bf 100644 --- a/scripts/check/check-forgotten-sibling-tests.mjs +++ b/scripts/check/check-forgotten-sibling-tests.mjs @@ -205,7 +205,26 @@ function changedSymbols(root, base, entries) { return result; } -function markdown(result, base) { +// A changed hub module (providerRegistry.ts, providers.ts, …) is imported by thousands of +// consumers, and every consumer multiplies by its candidate tests, so the cross-product reaches +// millions of rows. Rendering all of them made `lines.join("\n")` exceed V8's maximum string +// length; the throw landed in main()'s catch, which exits 1 — so an ADVISORY step turned +// "Fast Quality Gates" red on every PR whose diff touched a hub (#13866 follow-up). The header +// keeps the exact totals; only the enumeration is bounded. +const RENDER_LIMIT = 200; +const JSON_ITEM_LIMIT = 5000; + +/** First `limit` items plus a one-line note naming how many were withheld. */ +function renderBounded(lines, items, format, limit = RENDER_LIMIT) { + for (const item of items.slice(0, limit)) lines.push(format(item)); + if (items.length > limit) { + lines.push( + `- _… and ${items.length - limit} more not listed (report bounded at ${limit} rows per section; the counts above are exact)._` + ); + } +} + +export function markdown(result, base) { const lines = [ "## Forgotten sibling tests (advisory)", "", @@ -218,12 +237,10 @@ function markdown(result, base) { ]; if (result.findings.length) { lines.push("### Candidate tests absent from this diff", ""); - for (const item of result.findings) { + renderBounded(lines, result.findings, (item) => { const symbol = item.changedSymbols.length ? ` (${item.changedSymbols.join(", ")})` : ""; - lines.push( - `- \`${item.changedModule}\`${symbol} -> \`${item.consumer}\` -> \`${item.candidateTest}\`` - ); - } + return `- \`${item.changedModule}\`${symbol} -> \`${item.consumer}\` -> \`${item.candidateTest}\``; + }); lines.push("", "> Report-only calibration: these findings do not fail the job.", ""); } for (const [heading, items] of [ @@ -232,10 +249,12 @@ function markdown(result, base) { ]) { if (!items.length) continue; lines.push(`### ${heading}`, ""); - for (const item of items) - lines.push( + renderBounded( + lines, + items, + (item) => `- \`${item.changedModule}\` -> \`${item.consumer}\`${item.candidateTest ? ` -> \`${item.candidateTest}\`` : ""}: ${item.reason || item.message}` - ); + ); lines.push(""); } return `${lines.join("\n")}\n`; @@ -271,9 +290,26 @@ function main() { }); const report = markdown(result, base); process.stdout.write(report); + // The JSON artifact is bounded for the same reason the markdown is: a hub-module diff + // produces millions of rows and `JSON.stringify` would throw the same "Invalid string + // length". `totals` keeps every count exact, so tooling can still see the real numbers. + const jsonResult = { + ...result, + totals: { + findings: result.findings.length, + diagnostics: result.diagnostics.length, + suppressed: result.suppressed.length, + maskingRisks: result.maskingRisks.length, + }, + itemLimit: JSON_ITEM_LIMIT, + findings: result.findings.slice(0, JSON_ITEM_LIMIT), + diagnostics: result.diagnostics.slice(0, JSON_ITEM_LIMIT), + suppressed: result.suppressed.slice(0, JSON_ITEM_LIMIT), + maskingRisks: result.maskingRisks.slice(0, JSON_ITEM_LIMIT), + }; for (const [target, contents] of [ [summaryPath, report], - [jsonPath, `${JSON.stringify(result, null, 2)}\n`], + [jsonPath, `${JSON.stringify(jsonResult, null, 2)}\n`], ]) { if (!target) continue; fs.mkdirSync(path.dirname(target), { recursive: true });