Boarded with 8 other PRs in one combined worktree: typecheck:core, check:file-size, check:changelog-integrity, check:complexity, check:cognitive-complexity, check:cycles, check-native-deps all green; 75/75 focused tests pass. Fixes a genuinely confusing failure mode — matches the documented TROUBLESHOOTING.md symptom exactly. Thanks for the preflight check and clear diagnostics.
Boarded in a combined worktree: typecheck:core, check:dashboard-typecheck, check:file-size, check:changelog-integrity, check:complexity, check:cognitive-complexity, check:cycles, check-deps all green. Clean, self-contained addition (5 new files, 0 modifications to existing code) that mirrors the existing dashboard-typecheck baseline-ratchet pattern. Thanks for closing a real coverage gap — API routes had no dedicated typecheck gate.
Resynced onto the release tip after #12051/#12052/#12053 landed. Same LKGP-clear conflict as #12053 (kept the current clearStaleLKGP() helper at both call sites). One additional issue this final phase's combined-worktree validation surfaced: clearStaleLKGP() itself (added by #12013, which none of the 4 phase PRs could have seen since it landed after they were authored) still had a dynamic `await import("@/lib/localDb")` — a real break once this PR deletes the barrel. Fixed to `await import("@/lib/db/settings")`, matching the direct-import pattern used at every other call site. typecheck:core, check-db-rules, check:cycles, and the eslint-import-boundaries regression test (3/3, including "G14 rejects localDb barrel imports") all green after resync — zero barrel-importing production files remain. Nice clean 5-phase migration, and thanks for taking on the full #11795 cleanup.
Confirmed the bug is real and unfixed on the current tip before merging: `scripts/build/prepublish.ts` line 332 was still calling `execFileSync(NPX_BIN, ...)` directly (raw win32 npx.cmd spawn), the exact CVE-2024-27980 shim pattern the file's own header warns about. Root cause, fix, and evidence match — routing through the existing `runBuildTool()` helper. Thanks for catching the one call site the earlier refactor missed.
FU-07/FU-09 promotion-evidence harness (Refs #11656): delivers the manifest schema, deterministic fixture recipes, metrics aggregator, and promotion-verdict evaluator #11656 asks for — deliberately does NOT deliver the promotion verdicts themselves (they require real models against real fixtures on a live host, HOLD with explicit reason instead of any fabricated result). New files only, no collision with sibling PRs.
* fix(release): the packaged-app smoke verifies the database opened, not a driver line the primary path never prints (release/v3.8.51 twin of #12032)
Same change as #12032 on main: the packaged app opens SQLite during the smoke but
its primary open path prints no "[DB] Driver: …" line (only the recovery path and
the sql.js fallback do), so the #7592 assertion failed every Linux release leg. The
guard rejects the sql.js fallback line, accepts a native driver line, and otherwise
accepts demonstrable database activity; after readiness the smoke requests
/api/monitoring/health and waits for that activity outside the readiness loop.
electron-smoke-script suite 10/10.
* fix(release): reapply the smoke rework on top of release/v3.8.51's own copy of the script
The previous commit copied main's file wholesale and dropped this branch's
ensureSmokeEnvDirs(currentPlatform) fix and its tests; this reapplies only the
DB-open evidence change as a patch. electron-smoke-script suite green.
* fix(cli): drop the never-produced dist/index.cjs requirement from prepublish's opencode-plugin skip check (#11787)
* test(build): resolve tsup/npm portably in the #11787 regression test instead of a hardcoded .bin path
The old test assumed @omniroute/opencode-plugin/node_modules/.bin/tsup
already existed. A fresh checkout (CI's npm ci never installs this
standalone package's own deps) has no such node_modules at all, so the
test failed with MODULE_NOT_FOUND in CI while passing locally on a devbox
that had installed it before. Mirror scripts/build/prepublish.ts's own
install-then-resolveLocalBinEntry approach.
---------
Co-authored-by: Markus Hartung <mail@hartmark.se>
* test(infra): retry recursive temp-dir removal instead of failing a shard on ENOTEMPTY (#11966)
Two shards on release/v3.8.51 went red in one day with the same signature —
"ENOTEMPTY, Directory not empty: /tmp/omniroute-<test>-XXXXXX" — from
combo-same-provider-cascade (Unit Tests fast-path 4/4, on a PR that touches only
.github/) and auth-policy-embeddings-webfetch-7785 (the 20k-test TIA step). Both pass
alone and on re-run: the cleanup races something still writing into the directory
(SQLite WAL/-shm checkpoint, a worker, the backup) and under a loaded hosted runner
the window opens. 1154 test files do their own cleanup with
fs.rmSync(dir, { recursive: true, force: true }); 57 already asked for retries.
One-shot codemod (scripts/ad-hoc/codemod-rm-maxretries.mjs, kept for the record):
every rm / rmSync / rmdirSync option object with `recursive: true` and no
`maxRetries` gains `maxRetries: 5, retryDelay: 100` — Node itself then retries
ENOTEMPTY/EBUSY/EPERM for up to ~0.5 s before giving up. 2243 call sites in 1292
files under tests/, the shared tests/_setup/isolateDataDir.ts exit hook included.
Only the option object changes: no call site, assertion or import is touched.
Validation: prettier and ESLint (with the frozen suppressions) clean on all 1292
files; a random 20-file sample runs green (quota-redis-store hangs identically on
the untouched tree — it needs a Redis on localhost, an environment matter). The
four unit shards on this PR are the full run.
* fix(quality): let check-forgotten-sibling-tests read a 1,000-file diff
The gate shells out to `git diff` through execFileSync with Node's default 1 MB
maxBuffer; the 1,292-file codemod in this PR is the first diff large enough to
overflow it, and the gate died with `spawnSync git ENOBUFS` before comparing
anything. 64 MB is far above any real PR and costs nothing when unused.
Brings e4683cd22d (#11867 Alibaba allowlist time bomb), 09de69edc7 (#11891
config expiry detector), e71be03398 (#11893 runner janitor), 9dc8eab70e
(#11895 provenance × self-hosted lint) and f564b64f7d (#11901 heavy-build
lanes). main is already an ancestor of this branch (v3.8.50 sync-back), so the
merge is exactly these five commits.
# Conflicts:
# tests/unit/alibaba-free-tier-allowlist.test.ts
npm rejects provenance-signed uploads from self-hosted runners:
422 Unprocessable Entity - Error verifying sigstore provenance bundle:
Unsupported GitHub Actions runner environment: "self-hosted".
Only "github-hosted" runners are supported when publishing with provenance.
v3.8.50 learned that at minute 76 of its 10th publish attempt, after the tag,
the GitHub Release and the Docker images were already out. USE_VPS_RUNNER had
routed the job to the .113 pool on 2026-08-02; no release ran between 07-30 and
08-28, so the pairing sat latent for four weeks.
It is pure text — a job whose runs-on resolves to self-hosted and a step whose
run contains --provenance — so the workflow lint now checks it as a hard rule:
reported in plain mode, blocking under --strict and --ratchet (the CI mode),
emitted as provenanceRunnerFindings=<n> next to the other counters.
Against origin/main the rule finds the two real offenders (the staged upload
AND the DIRECT emergency fallback in npm-publish.yml); against the #11877 split
it finds none. --provenance-file is deliberately not matched (different flag,
pre-built bundle) and an opaque runs-on expression with no literal self-hosted
is classified unknown and skipped — the check never guesses.
The unit suite's last case walks the real .github/workflows and asserts zero
findings, so it is red on main until #11877 lands and green after; that is the
regression guard working, not a flake.
* chore(ops): make the runner janitor act on what it can prove, not advise
The .113 janitor already knew the rules and had been shouting them into a log
nobody reads: on 2026-08-28 12:00Z it reported "10 listeners > ceiling 8" and
"disk 85%" — for hours — while 6.7 GB of dead-run leftovers sat on the 12 GB
tmpfs (RAM) because its patterns matched neither e2e-build.tar.gz nor
next-build/, its 24 h fuse is a day too long for memory, and its _work/_temp
base (/home/*/actions-runner*) does not exist on this box (runners live under
/opt). Measured while draining the v3.8.50 npm publish (postmortem, Parte III).
What changes:
- idle is PROVEN before removal, with ONE lsof snapshot filtered to the swept
bases (lsof +D per path walked whole trees and took minutes; 460 candidates
grepping a re-printed 83k-line string was the other half). 20 s on the box.
Without lsof the janitor removes nothing and says why (exit 1).
- tmpfs leftovers go after 3 h, disk _work/_temp after 24 h; both overridable.
Patterns gain next-build* and e2e-build.tar.gz; /opt/actions-runner* is swept.
- zombie builds: a next-build older than 75 min has no job (a real Build step is
~26 min). On 2026-08-27 one ran 70 min after GitHub had declared its job lost,
holding 3.6 GB. KillMode=mixed on the units covers systemctl stop/restart;
this covers the lost-connection path.
- prunes 48 h-old checkouts under _work of runners whose unit is STOPPED — an
active runner is never touched.
- alerts on memory PSI (full/avg60) and reports the listener ceiling with an
omniroute/other breakdown (the box also hosts OmniHeuris and OmniMind).
Enforcing the ceiling stays an operator decision (label split), not cron's.
- --dry-run prints exactly what it would do and touches nothing; unknown
arguments are rejected.
Dry-run on the real box: 460 stale omniroute-* test fixtures (930 MB of RAM) it
would reclaim, 0 busy, 0 false "removed" lines, 20 s. The unit suite drives the
script against a fixture tree with every base redirected; the sweep branch runs
where lsof exists (hosted CI images) and the without-lsof contract everywhere.
docs/ops/RUNNER_BOX.md reconciled to the measured box: 31 GB (it said 16), ten
listeners, the 14 GB next-build ceiling, the KillMode drop-in, and the rule that
nothing is cleaned by hand while a runner is busy.
* docs(ops): restore the frontmatter fumadocs requires on RUNNER_BOX.md
Rewriting the page whole dropped its `title:` frontmatter, and docs/ is
compiled into the Next build by fumadocs-mdx — so Build, Fast Production Build
and dast-smoke all died with "[MDX] invalid frontmatter in
docs/ops/RUNNER_BOX.md". Same block as before, verbatim.
config/alibaba-free-tier-allowlist.json carried "validUntil": "2026-08-27".
On the 28th the loader started rejecting it — correctly, that is the design —
and a test that asserted "the shipped pack loads" turned every PR and main red
with no commit involved (#11866). A time bomb: the one class of defect a diff
review can never catch, because there is no diff.
scripts/check/lib/configExpiry.mjs walks config/**/*.json for validUntil /
validTo / expiresAt / expiry / expires (and snake_case forms), parses the dates,
and classifies each as expired / expiring (< 7 days) / ok / unparseable.
The repo-wide test fails on expired or expiring packs unless the file is in a
small allowlist keyed to the issue that owns the renewal — and fails the OTHER
way when an allowlisted pack is no longer expiring, so entries cannot go stale.
A positive anchor requires at least one dated pack to be found, so a renamed
key cannot silently turn the suite into a no-op.
The Alibaba pack is allowlisted against #11866: whether the curated free-tier
list still matches reality is an operator data decision, not a test fix.
Removing that entry makes the suite fail as intended (verified).
The eighteen commits main carries beyond the cycle branch, and what each one
became here:
already in release/v3.8.51 by its own PR (no-op, verified by content):
b090b601a5 / 026e1cadaa deps: nanoid 3.3.18 equal, dompurify 3.4.14 newer
918fba5e39 .gitignore: /_tasks already anchored
5f0a394091#10026 hide health-check-excluded models — same helper, 5 call sites
c68cda7dfb#11075 shared passthrough providers — superseded by #11071/#11078
superseded, one piece kept:
ca23eed77c#10055 memoize models.dev pricing — the cycle memoizes on the
catalog cache version already; only the resetDbInstance() hook is
ported, wired to that memo
applied as-is:
8778ea7d18 stamp dist/BUILD_SHA before the npm provenance gate (#11721)
aa52351113 decouple the Bun image from the release manifest (#11724)
925feb27b8 let the bun digest artifact be absent (#11740)
b65ef333da size the install-upgrade gate to a measured run
0ce21232db#11845 converge install/upgrade schemas (migration renumbered in
the next commit: 163 collides with 163_radar_feed_cache_generated_at)
b7c07edad8#11855 install-upgrade gate on disk, not tmpfs
8e2fb04329#11864 drop *.nft.json from the npm tarball (413)
dea6bb8b6b#11877 publish npm from a hosted runner (provenance 422)
handled by the sync script that follows (CHANGELOG protocol):
b4ec7807ab Release v3.8.50 — squash of content this branch already carries
5458026c21 / c44c0a29e8 CHANGELOG aggregation, stats and top-25
applied separately (its own commit, ten files):
65e81158ab#11088 Ollama capability routing — a 5,094-file squash from a
stale base; only the Ollama files are the change
Every cherry-pick that touched a file this branch had also changed was
resolved by hand and re-run through the tests both sides own for it.
Fixes the stale-shell PWA lockout after a deploy: navigationFallback now returns
Response.error() instead of replaying a cached shell whose /_next/static chunk
references are dead, and the worker is registered as /sw.js?v=<build-id> so each
deploy is actually observed instead of never updating until a navigation to the
new build first succeeds.
Recreated onto release/v3.8.51 (original base was main, which had diverged too far
for a clean retarget) — both commits cherry-picked and force-pushed to the
contributor's branch (author preserved), then the PR's base edited in place.
4/4 focused tests passing (2 via vitest for the jsdom-environment PwaRegister
suite, 2 via node:test for the service-worker fallback suite). Thanks for the fix!
Recreated from #11702 (MumuTW) onto the current release/v3.8.51 — fixes the object-note
comparator bug that has silently blocked the automated ratchet-bank lane since 2026-08-11;
24/24 focused tests + file-size gate green on this tip. Thanks for tracking this down!
Recreated from #11734 (MumuTW) onto the current release/v3.8.51 — full ESLint inventory 0
errors, release-green ESLint hard gate fixed, the useApiKeySave hook-render fix preserved
alongside all 3 existing test cases. Thanks for the fix!
Rebased onto the current release/v3.8.51 tip as part of a combined provider-retirement/provenance merge batch (Designer Web, Felo Web, Runtime, GPL-derived removal, Qwen Web already landed). Large conflict set (this is the biggest PR in the batch — the common ChatGPT Web provider touches chat, images, count-tokens, session leases, and combos). Conflicts resolved:
- `open-sse/config/providers/registry/chatgpt-web/*`, `open-sse/executors/chatgpt-web*`, `open-sse/handlers/imageGeneration/providers/chatgptWeb.ts`, and their tests: kept deleted, matching the PR's stated scope.
- `open-sse/config/providers/registry/minimax/web/index.ts`, `open-sse/handlers/imageGeneration/providers/geminiWeb.ts`, `open-sse/executors/gemini-web.ts`'s stale image-mode branch: base-drift collisions against already-merged sibling retirements (#11691, #11708) — kept deleted / dropped the dead code, since this PR's own branch forked before those merged.
- `src/shared/constants/reservedProviderPrefixes.ts`, `open-sse/executors/index.ts`, `executorProxy.ts`, `virtualFactory.ts`, `autoStrategy.ts`, `src/lib/db/providers.ts`, `src/sse/handlers/chat.ts`: combined the Designer + Runtime (Felo/Qwen) + common-ChatGPT-Web retirement guard calls at each shared chokepoint — compute-once-then-OR pattern, consistent with prior combinations in this batch.
- `src/sse/services/model.ts` / `src/sse/handlers/chatHelpers.ts`: adopted this PR's new `getModelInfoOrRetirementResponse()` central wrapper (a real improvement over ad-hoc try/catch), and extended it to also catch the Designer + Runtime retirement errors it didn't originally cover, so the consolidation doesn't regress the other two mechanisms.
- `src/app/api/v1/images/edits/route.ts`: this PR moved the retirement check earlier (before `enforceApiKeyPolicy`) but left the old later call+catch block in place from base drift — removed the now-redundant duplicate `resolveImageRouteModel()` call and merged the Designer catch into the earlier one.
- `open-sse/config/imageRegistry.ts`, `tests/snapshots/executors/executor-map.json` (`keyCount` recomputed to 133), `tests/snapshots/provider/translate-path.json`: same "both sides inserted a different retired provider at the same slot" pattern — resolved by dropping both.
- `tests/unit/chatcore-executor-proxy.test.ts`, `provider-node-reserved-prefix.test.ts`, `combo-auto-candidate-expansion.test.ts`, `messages-count-tokens-route.test.ts`, `virtual-auto-combo.test.ts`: split into independent per-mechanism test blocks (established pattern); `virtual-auto-combo.test.ts`'s old "includes cookie web-session providers" positive-inclusion test (which used chatgpt-web as its example) was retired along with the provider and replaced by this PR's negative-exclusion test for the same slot.
- `docs/architecture/ARCHITECTURE.md`, `CODEBASE_DOCUMENTATION.md` (+ 4 i18n mirrors), `README.md`, `FREE-TIERS-GUIDE.md`, `docs/diagrams/free-tier-budget.svg`, `docs/screenshots/free-tier-budget-card.svg`, `docs/reference/PROVIDER_REFERENCE.md`: recomputed every stale count from the real merged state — 104 executors (`countFiles` gate logic), 351 providers (regenerated via `gen:provider-reference`), 152/351 `hasFree` entries, 445/438/7 free-tier catalog rows, 13 ToS-avoid providers, budget-card regenerated via its real generator script. One doc conflict (`oauth/` module list) needed picking HEAD's side specifically — theirs still listed the already-removed `raycast` module instead of the real `openference`.
- `config/quality/test-masking-allowlist.json`: additive merge of the PR's 17 `_deletedWithReplacement` entries alongside the batch's existing ones (one real duplicate-key mistake in my first pass, caught and fixed via a `object_pairs_hook` duplicate-key check before finalizing).
Also fixed two real, unrelated-to-my-merge issues surfaced by the focused suite:
- `tests/unit/resolve-web-provider-host.test.ts`: the PR's own test had a typo — it asserted `perplexity-web`'s resolved host as `"perplexity.ai"`, but the provider's registered `website` is `"https://www.perplexity.ai"` and the resolver returns the URL's `host` verbatim (no www-stripping), so the correct value is `"www.perplexity.ai"` (consistent with the same test's own `url` assertion).
- `tests/unit/hard-session-lease-bypass-inventory.test.ts`: this golden call-site inventory was already stale on the pristine post-#11713 tip (confirmed via a throwaway probe worktree) — `src/lib/db/providers.ts`'s 3 connection-fallback sites and a third `src/app/api/providers/route.ts` site were never added to the golden list by the earlier-merged #11698/#11720 PRs. Updated it to the real current inventory (dated inline comments explain each delta and which PR introduced it), plus this PR's own legitimate deltas (image-edits duplicate-call removal, `ChatGptWebExecutor.execute()` site removed).
Focused suite green (433/433 across executor-proxy, reserved-prefix, hard-session-lease-bypass-inventory, resolve-web-provider-host, retirement/runtime-block/source-retirement/management-retirement/image-handler-retirement, migration-168, combo-auto-candidate-expansion, virtual-auto-combo, executor-map-golden and siblings), plus `typecheck:core`, `check-file-size`, and `check-changelog-integrity` clean. Thanks for the thorough provenance-hold retirement work — appreciated.
Rebased onto the current release/v3.8.51 tip as part of a combined provider-retirement/provenance merge batch (Designer Web, Felo Web, Runtime, and this GPL-derived Raycast/Hailuo Web removal all landed together). Conflicts resolved:
- `config/quality/test-masking-allowlist.json`: additive merge of the Hailuo-Web/Raycast-auth/Raycast-local-extract entries alongside prior sibling retirement entries.
- `docs/reference/PROVIDER_REFERENCE.md`: kept the branch's generated content (deferred to a future `npm run gen:provider-reference` regeneration pass).
- `src/app/api/providers/[id]/test/webSessionTestDispatch.ts`: comment-only, dropped stale retired-provider examples.
- `tests/snapshots/executors/executor-map.json`: recomputed `keyCount` to 137 (matches the actual merged `entries` object).
- `tests/unit/provider-test-token-web-session-dispatch.test.ts`: kept both sibling assertions (hailuo-web + t3-chat-web), avoided duplicating the dedicated microsoft-designer-web test already present.
Also recomputed the golden `RESERVED_PREFIX_COUNT` (397, down from 400) to reflect the 3 GPL-derived ids/aliases this PR removes from `REGISTRY`, and rebaselined `file-size-baseline.json` for the combined retirement-guard growth accumulated across the sibling PRs in this batch.
Focused suite green (86 tests across authz/oauth-autoimport, public-route-exact-match, gpl-derived-provider-removals, migration-166, muse-spark-ws-auth-token, oauth-providers-config, provider-alias-uniqueness, provider-test-token-web-session-dispatch, providers-constants-split, ts7-executor-override-signatures, executor-map-golden, provider-node-reserved-prefix), plus `typecheck:core` and `check-file-size` clean. Thanks for the GPL-license cleanup — appreciated.
Merged via /merge-batch (v3.8.51 provenance sweep). Boarded and validated together with the batch's other provenance PRs in a combined worktree — full gate suite green, including the new provider-asset-provenance gate/manifest introduced here. Thank you.
* fix(ci): run the install-upgrade gate on disk, not on the /tmp tmpfs
The v3.8.50 publish failed this gate again, and this time it said why:
free space in /tmp: 2.9 GB
⚠️ only 2.9 GB free — this gate needs roughly 12 GB
crashed: upgrade install ran out of disk space (58269 ENOSPC errors)
On the self-hosted runner `/tmp` is a **12 GB tmpfs backed by RAM**, while the
root filesystem had 66 GB free. The gate builds two ~3 GB install trees, installs
the second one over twice, and packs a 275 MB tarball — roughly 12 GB, all of it
demanded from the wrong filesystem.
This is why freeing disk never fixed it: 84 GB were freed on `/`, and none of it
ever reached the volume the gate was using. The check even measured the right
number and reported it against the wrong path, so the warning read as "the disk
is full" when the disk was fine.
- work in `<repo>/.install-upgrade/` (gitignored) instead of `os.tmpdir()`,
overridable with `OMNIROUTE_INSTALL_UPGRADE_WORKDIR`
- the free-space log and the ENOSPC crash message now name the directory the run
actually uses, so the next reader is sent to the filesystem that ran out
Phase A already passes on the current main: clean install healthy, version
reported correctly, 130 tables — the authentication fix and migration 163 from
#11845 both hold. Only Phase B was starved.
* docs(env): document OMNIROUTE_INSTALL_UPGRADE_WORKDIR
The workdir override introduced in this branch is a new `process.env.*` read, and
two gates caught it immediately: `issue #7793: real .env.example is in sync with
process.env.* reads in code` and `check:env-doc-sync` (Docs Sync STRICT).
Both were right — an env var that exists only in code is an env var nobody can
find. Documented in `.env.example` and `docs/reference/ENVIRONMENT.md` with the
reason it exists: the gate needs ~12 GB and must not land on a small tmpfs.
* fix(db): converge the install and upgrade schemas; stop ENOSPC from faking a divergence
The v3.8.50 publish run failed `check:install-upgrade` with "15 tables a CLEAN install
creates but an UPGRADE does not" (agentic_conversations, ccr_blocks, the whole Radar set,
jobs/job_runs, exclusive_connection_leases, …). None of them was missing.
Root cause, from the CI log (run 33104507735): the Phase B upgrade `npm install` hit
`npm warn tar TAR_ENTRY_ERROR ENOSPC: no space left on device` 5611 times, npm still exited
0, and the resulting truncated package made `omniroute serve` "exit with code 0 before
serving". No migration ever ran, so the database still held the 3.8.49 schema (115 tables)
and every post-133 migration table read as a divergence.
Verified against the real thing: booting the published omniroute@3.8.49 and replaying that
database through the current runner applies exactly 29 migrations and lands on the same
table set a clean install produces — the migration set was never at fault.
What changes:
- `163_model_capabilities.sql` — the one genuine convergence defect. The table was only
ever created by `ensureCapabilitiesTable()` on the first models.dev sync, so whether a
database has it depends on timing, not on the schema version. It is the residual the
gate reported. A migration makes both install paths deterministic.
- `check:install-upgrade` now fails on an ENOSPC-truncated install instead of measuring a
broken tree; authenticates its health probe with a minted internal-service token, so the
version assertion works against the health payload hardened by GHSA-mvf8-qc78-5mxm
(an anonymous caller gets no version — the same run also failed with "health reports
version undefined"); frees the ~3 GB clean-install tree before the upgrade phase; warns
when the temp filesystem cannot hold the run; prints the failing server's output; and
skips the convergence verdict when a phase never served, so a broken boot can no longer
manufacture a schema divergence on top of the real failure.
Tests: `tests/unit/db-install-upgrade-schema-parity.test.ts` pins the deterministic half of
the gate in milliseconds (every migration reachable on a clean install; model_capabilities
comes from the migration set; its DDL does not drift from the runtime helper), and the
ENOSPC guard is covered in the existing gate test.
* docs(db): record the real cause of the cache_metrics residual in the allowlist
The allowlist described every residual as "a CREATE that left the migration set in some
past cycle". cache_metrics never was in the migration set: it is created lazily by
ensureCacheMetricsTable() (src/lib/semanticCache.ts:34) the first time the semantic cache
runs, which is the same class as the model_capabilities divergence that blocked the v3.8.50
publish. Document both causes so the next residual is fixed with a migration where that is
the right answer, instead of reflexively allowlisted.
* docs: bump the migration count to 160 after 163_model_capabilities
check:docs-counts-sync enforces the shipped migration count as a STRICT claim in README.md,
AGENTS.md and llm.txt.
* docs(i18n): re-sync the 42 llm.txt mirrors after the migration-count bump
The v3.8.50 publish died at `Prove clean-install AND upgrade-over-previous both
boot` — timed out after 30 minutes. Not a defect found: the gate never got to
finish.
The log says why, once you read past the first line:
03:42:49 packing v3.8.50…
04:07:28 PHASE A — clean install of the packed tarball
04:13:08 timeout
`npm pack` alone took **24m37s**, leaving 5 minutes for two installs and two
boots. The budget was never going to hold.
Worth naming: this gate landed in #8953 and the 2026-08-27 run was the FIRST to
ever reach it. Every earlier publish died upstream — disk exhaustion, a missing
dist/BUILD_SHA — so `timeout-minutes: 30` had never been measured against a real
execution. It was a guess, and it blew on its debut. Same shape as the rest of
this cycle: a gate that had never been allowed to finish speaking.
Two changes, and the second is the one that matters next time:
- `timeout-minutes: 30` -> `60`, sized to the single measurement available.
- the script now times the pack and prints duration + tarball size. Without it
the log showed `packing…` and then nothing for 30 minutes, which reads like a
hang and is not — raising a limit blind would have been a guess on top of a
guess.
If 60 also proves short, the next log will say exactly which phase ate it.
Merged via /merge-batch (2026-08-26, v3.8.51). Validado: typecheck/complexity/file-size/changelog verdes, 18/18 testes de authz passando (com o isolamento correto de DATA_DIR — meu primeiro run isolado falhou por eu não ter carregado tests/_setup/isolateDataDir.ts, não é bug seu). Obrigado pela contribuição.
Merged via /merge-batch (lote 2026-08-26, v3.8.51). Boarded no worktree combinado junto com outras ~30 PRs; validação única: typecheck/complexity/cognitive-complexity/changelog-integrity verdes, file-size rebaseado onde necessário (crescimento legítimo), lint com os mesmos 228 achados pré-existentes confirmados via sonda contra o tip puro (não introduzidos por este lote), e ~370 testes focados (unit + vitest) passando. Obrigado pela contribuição.
Merged via /merge-batch (lote 2026-08-26, v3.8.51). Boarded no worktree combinado junto com outras ~30 PRs; validação única: typecheck/complexity/cognitive-complexity/changelog-integrity verdes, file-size rebaseado onde necessário (crescimento legítimo), lint com os mesmos 228 achados pré-existentes confirmados via sonda contra o tip puro (não introduzidos por este lote), e ~370 testes focados (unit + vitest) passando. Obrigado pela contribuição.
Merged via /merge-batch (lote 2026-08-26, v3.8.51). Boarded no worktree combinado junto com outras ~30 PRs; validação única: typecheck/complexity/cognitive-complexity/changelog-integrity verdes, file-size rebaseado onde necessário (crescimento legítimo), lint com os mesmos 228 achados pré-existentes confirmados via sonda contra o tip puro (não introduzidos por este lote), e ~370 testes focados (unit + vitest) passando. Obrigado pela contribuição.
Merged via /merge-batch (lote 2026-08-26, v3.8.51). Boarded no worktree combinado junto com outras ~30 PRs; validação única: typecheck/complexity/cognitive-complexity/changelog-integrity verdes, file-size rebaseado onde necessário (crescimento legítimo), lint com os mesmos 228 achados pré-existentes confirmados via sonda contra o tip puro (não introduzidos por este lote), e ~370 testes focados (unit + vitest) passando. Obrigado pela contribuição.
Validated in a combined 4-PR batch worktree off release/v3.8.51 tip (last of the Bun-native cluster; conflicted against the already-merged #11482's Dockerfile.bun hunk in the shared worktree — resolved by taking this PR's configurable ARG/ENV shape, which is exactly what it's designed to replace, and pushed the same resolution to this branch).
- Focused test: resolve-next-build-bundler-flag.test.mjs — 3/3 pass, part of batch's 5/5 node:test run
- typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity — all OK
- Full-repo lint: 228 pre-existing dashboard react-hooks/* findings, unrelated to this diff
Thanks for validating with both node --test and bun test — good practice given the dual-runtime surface this touches.
Validated in a combined 4-PR batch worktree off release/v3.8.51 tip (Bun-native SQLite infrastructure cluster from the same contributor).
- Focused test: bunSqliteAdapter.test.ts — part of batch's 5/5 node:test run
- typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity — all OK
- Full-repo lint: 228 pre-existing dashboard react-hooks/* findings, unrelated to this diff
Thanks for tracing this to the pre-boot better-sqlite3 require — a native NAPI abort that JS try/catch cannot recover from is exactly the kind of failure mode that needs the guard moved earlier, and aligning bootstrap/sync-env with the already-preferred bun:sqlite driver is the right fix.
Validated in a combined 3-PR batch worktree off release/v3.8.51 tip.
- Focused test: electron-smoke-script.test.ts — part of batch's 165/165 node:test run
- typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity, check:docs-counts-sync — all OK
- Full-repo lint: 228 pre-existing dashboard react-hooks/* findings, unrelated to this diff
Real hardware validation on a fresh packaged Windows build (Hard Rule #18) surfacing and fixing 5 genuine defects (GNU-tar drive-letter parsing, missing asar file, electron-builder's node_modules extraResources drop, USERPROFILE-derived userData path, unreachable driver-log assertion) is exactly the kind of investigation this repo needs more of. Thank you.
Validated in a combined 3-PR batch worktree off release/v3.8.51 tip.
- Focused test: protocol-e2e-server-stamping-11535.test.ts — part of batch's 165/165 node:test run
- typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity, check:docs-counts-sync — all OK
- Full-repo lint: 228 pre-existing dashboard react-hooks/* findings, unrelated to this diff
Thanks for the meticulous root-causing here — three distinct issues (server flavor, HOST pin, open-bootstrap env leak) traced to exact line numbers with live-boot before/after evidence.
Validated in a combined 3-PR batch worktree off release/v3.8.51 tip (a sibling PR from the same author, #11495, was held out — a typecheck error in zai-web.ts only reproduced with this PR + #11495 boarded together, and cleared without #11495; isolated this PR alone confirmed clean on its own too, so the interaction belonged to #11495's side — see its comment).
- Golden lock: executor-map-golden.test.ts — passes byte-identical (same keys, classes, provider identities, dispatch guards)
- Focused tests part of batch's 94/94 node:test run
- typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity — all OK
- Full-repo lint: 228 pre-existing dashboard react-hooks/* findings, unrelated to this diff
Thanks for the measured, careful methodology here — the golden-lock contract plus the isolated DATA_DIR benchmarking make this an easy PR to trust despite the wide surface (72 files).
Validated in a combined 3-PR batch worktree off release/v3.8.51 tip. The prerequisite sibling (159→160 migrations) was already covered by #11502 — closed the redundant #11543 separately.
- New check:docs-counts gate: proven fail-before/pass-after per the PR's own methodology, re-verified here (PASS, 2 unrelated soft-drift notices on cloud-agent/A2A doc counts, non-blocking)
- Focused tests: check-docs-counts-sync.test.ts, combo-matrix/auto.test.ts, lkgp-enabled-context-11181.test.ts — pass
- typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity — all OK
Thanks for replacing seventeen hand-maintained factor-count claims with one source-derived gate.
Validated in a combined 4-PR batch worktree off release/v3.8.51 tip. This PR's diff overlapped taskFitness.ts and autoCombo.test.ts with the already-merged #11492/#11506 — git's merge auto-resolved both hunks cleanly (non-overlapping layers: #11492/#11506 touch layer 2 arena lookup, this PR touches layer 4 static-table hygiene); verified no conflict markers remained and re-ran the full suite after boarding.
- npm run check:model-lifecycle — PASS, 68 retired ids, 1327 catalog ids, 0 violations (re-ran with the correct `node --import tsx/esm` loader after an initial bare-node invocation mistakenly failed on path-alias resolution — that was my invocation error, not the gate)
- Focused tests: fitness-table-hygiene-11503.test.ts, taskFitness-pattern-order-8603.test.ts, model-deprecation-aliases-11503.test.ts, check-model-lifecycle-gate.test.ts, model-deprecation.test.ts, autoCombo.test.ts — part of batch's 126/126 vitest + 246/246 node:test runs
- typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity, check:cycles — all OK
- Full-repo lint: 228 problems remaining, all pre-existing dashboard react-hooks/* findings unrelated to this diff (zero errors in any file this PR touches)
Thanks for this — genuinely thorough methodology (segment-boundary matching, provider-scoped alias guard, offline lifecycle gate with a documented burn-down list for the 6 remaining catalog offenders).
Validated in a combined 10-PR batch worktree off release/v3.8.51 tip. Fixes#11523.
- Focused test: tests/unit/build/docker-next-channel-8576.test.ts — 7/7 pass
- typecheck:core, file-size, changelog-integrity, complexity, cognitive-complexity gates — all OK
- Full-repo lint: 503 pre-existing problems confirmed identical on the pure release/v3.8.51 tip — unrelated to this diff
⚠️ base-red inherited: #11449
Thanks for treating Docker publishes from non-default frozen release branches as an expected no-op.
Retargetado para release/v3.8.51 (release/v3.8.50 está congelada — freeze issue #11439). Resolvido um conflito de merge não-relacionado em src/shared/utils/wsPath.ts (originado de um refactor já mergeado nessa branch depois do fork deste PR; o diff real deste PR — scripts/dev/sync-env.mjs + tests/unit/sync-env.test.ts — ficou intacto) e revalidado: typecheck:core limpo, 13/13 testes focados passando.
Segue o precedente correto do #1622 (STORAGE_ENCRYPTION_KEY) para os dois secrets restantes que a postinstall preenchia por engano, defeituando o mecanismo de ensureSecrets(). Obrigado pela contribuição!
Retargetado para release/v3.8.51 (release/v3.8.50 está congelada — freeze issue #11439). Validado em lote combinado (batch-0824h2, junto de #11435/#11436/#11437) contra o tip de release/v3.8.51: typecheck:core limpo, gates estáticos OK, 127/127 testes focados passando.
Investigação sólida com repro real via container isolado, três causas independentes identificadas e corrigidas com testes de regressão dedicados para cada uma. Obrigado pela contribuição!
Validated on the resolved merge against the current tip (527da656 + the post-#11281 rebaseline): the single conflict was a comment-only collision in providers/[id]/models/route.ts (kept the tip's #10828-ordering note). Focused suites 125/125 across all 13 touched test files (build-sqlite-stub, cc-compatible, copilot-claude-messages, copilot-gemini-route, executor-github, ghe-copilot, github-copilot-discovery-token, github-copilot-model-discovery, noauth-sibling-7620, provider-header-profiles, provider-models-config, request-log-payloads, upstream-error-passthrough), typecheck:core clean, file-size/changelog-integrity OK. Merged --admin over the inherited 2026-08-23 base-red cluster (#9985) — the reds are proven tip failures (CLI catalog cluster + @testing-library allowlist, being drained by #11280), not from this diff. Note: the rebase means several items the body listed (relay x-relay-path SSRF, /v1/search blocked-providers, #10736 rotation fence, #10903, #10865, #10899, #10916) already landed upstream and are NOT in this delta — the delta is: better-sqlite3 build guard + build heap/worker caps + telemetry-off (#10060 re-derived), credential-echo passthrough refusal + OCR/moderation redaction + call-log key redaction, Copilot CLI 1.0.81-6 wire identity + Claude→/v1/messages name-matched routing + discovery token fix, CC model_not_found 400, compat overrides for no-auth aliases (#7620-pinned). The Copilot wire-identity change is the one to watch in production. Thank you @arminanton — and the ported-author credits in the commit history (@rqzbeh, yidecode, the #10899/#10916 authors) are preserved. Your config-posture finding (REQUIRE_API_KEY default vs 0.0.0.0) is noted for a maintainer decision, as you scoped it.
check:pack-artifact and check:pack-boot have been self-contradictory since
05/08, blocking the v3.8.50 publish in ci.yml (build:cli job) and
npm-publish.yml:
- check:pack-artifact FAILS any tarball path containing a node_modules
segment (PACK_ARTIFACT_NEVER_ALLOWED_SEGMENTS; files[] also excludes
"!**/node_modules/**").
- check:pack-boot REQUIRED sql.js under the vendored
dist/node_modules/sql.js location — a path the tarball can never carry,
so both gates could never be green at once.
The packaging model is now dependency-based: sql.js and node-machine-id
are declared `dependencies` (a clean install places them under
<packageRoot>/node_modules/), and better-sqlite3 is an optionalDependency
installed natively per platform (^13.0.2 — which also covers the
darwin-arm64 prebuild gap from #11242 by construction). The runtime
already resolves the WASM at <cwd>/node_modules/sql.js/dist/sql-wasm.wasm
(src/lib/db/adapters/sqljsAdapter.ts).
Changes:
- scripts/check/check-pack-boot.mjs: REQUIRED_SQLJS_RUNTIME_FILES now
points at node_modules/sql.js/{package.json,dist/sql-wasm.js,
dist/sql-wasm.wasm} — the dependency-installed location the clean-prefix
install actually produces. REQUIRED_MACHINE_TOKEN_RUNTIME_FILES was
already correct and is unchanged.
- bin/cli/runtime/sqliteRuntime.mjs: BETTER_SQLITE3_VERSION bumped
^12.10.1 -> ^13.0.2 to match optionalDependencies (the lazy runtime
install was pulling the wrong major), and exported for the guard.
- tests/unit/pack-boot-runtime-paths.test.ts (new, TDD: RED -> GREEN):
pins that (a) no pack-boot required path references a never-publishable
vendored dist/<segment> location (driven by
PACK_ARTIFACT_NEVER_ALLOWED_SEGMENTS), (b) sql.js/node-machine-id stay
declared dependencies, (c) the lazy-install spec stays on the declared
optionalDependency major.
- tests/unit/check-pack-boot.test.ts: the sql.js contract test pinned the
old vendored path; updated to node_modules/sql.js/dist/sql-wasm.wasm.
This is alignment to the real new contract (vendoring ended), not
masking — the same test still asserts the find-missing behavior.
Electron is unaffected: the vendored dist/node_modules bundle still
exists for Electron packaging (postinstall.mjs and assembleStandalone.mjs
untouched).
Refs #11242
Refs #10296
Co-authored-by: Xiangzhe <bakryun0718@proton.me>
Landed with the design call resolved per the owner's pick — **option 1**: the synced store is now endpoint-agnostic (persistDiscoveredModels and managedModelImport no longer drop non-chat models at write time), and chat selectability moved to read time (auto-pool expansion in autoStrategy applies filterChatSelectableModels; the models-route projection already had its chatOnly filter). Your discovery test now passes end-to-end (3/3): /api/show capabilities persist per connection and image/embedding requests route through the advertising host.
Reconciliation notes: conflicted areas merged onto the current tip (adobe discovery import, requestedModel preflight signature, resolvedProvider fast-path coexists with the synced-route override — explicit resolution wins); carried base-red drains (#10055 memoization, #11071 test variants) dropped as already-landed; the managed-model-import exclusion test was propagated to the new contract (image/video models persist; the read filter still hides them from chat pickers — pinned by a new assertion). Full battery: 205/206 focused (the one red is a confirmed periodic-timer timing flake on the loaded devbox — 20/20 isolated), autoCombo vitest 30/30, combo suites 46/46, gates + typecheck clean.
Thank you @yourspraveen — the capability probe + routing design was right; it just needed the store contract opened up. Fixes#11087.
Validated on the combined batch board over tip 0b41259f: static gates clean, typecheck:core clean, 430+ focused tests green across 5 groups.
postbuild's esbuild spawn now resolves cross-platform (no more ENOENT after a successful Next compile on Windows). build-tool-runner-win-shim suite green. Thank you @aliyosufi — first contribution, welcome!
Merged after conflict triage: the six base-red repair files (vi.json, opencode.ts JSDoc, context-manager test, the three webhook dispatcher tests, the uncloseai orphan-test rename) were already drained on the tip by today's #11130/#11157/#11160/#11113 — those hunks resolved to the tip shape. What lands is the production-fix set: GLM transport-aware Anthropic headers, Claude Code-compatible model-listing rejection, combo live-test single-probe, zero-cost Auto-Combo interval normalization, recovery-clearing union handling, LLMLingua real-path compare, macOS netstat PID discovery, AI Horde R2 strict public-host validation. Sweep of every touched test file: 243/243 green; typecheck + file-size clean. (guide-settings-route's 4 reds reproduce on the pure tip — pre-existing drift from #11079, not from here.) Thank you @backryun!
⭐4 — Suporte de backend nativo Bun + Dockerfile.bun multi-stage + fallback dinâmico de driver SQLite (better-sqlite3 prioritário sob Bun, bun:sqlite fallback; Node preservado) + correção de estabilidade do DAST CI smoke.
Validado a fundo (worktree board sobre tip): bun-support 4/4, typecheck:core limpo, dashboard-typecheck OK (220 dentro do baseline), open-sse-typecheck OK (5 pré-existentes), gate de runtime OK sob Node, changelog-integrity OK, file-size/complexity/cognitive/dead-code OK. Verificado que o driver preserva a cadeia Node/falback conforme AGENTS.md; teste bun-support presente. Baselines de typecheck removidos são ratchet honesto (erros não existem mais).
OBS: destravei 2 base-reds do tip neste turno (push direto 7ffa3ef): movi o changelog fragment da #11050 da seção inválida breaking/ para fixes/, e rebaselinei AddApiKeyModal 1067->1073 (crescimento da #11056). Sem isso a #11039 e o resto da fila ficariam vermelhos.
⭐5 — freeAccessPolicy "strict" opt-in (default off): verifica candidatos de auto-combo contra estado de quota ao vivo + segurança econômica por conexão antes do dispatch (fail-closed — estado desconhecido/stale/incompleto é excluído). Zero mudança de comportamento com o default "off". 82 testes focados, eslint/prettier/typecheck limpos, docs em docs/routing/STRICT_ZERO_COST.md.
⭐5 — Provider x-search de primeira classe (SuperGrok/xAI x_search) em POST /v1/search e MCP omniroute_x_search. Fallback de credenciais xai-oauth→xao→xai; distinto de web search e do X Developer MCP.
Reconciliado com o release tip (que já incluía #10981 "skip catalog-default SearXNG" deste mesmo lote): merge trouxe 5 conflitos reais de contagem gerada (llm.txt/README.md/AGENTS.md/PROVIDER_REFERENCE.md/SVGs/46 mirrors i18n, todos verificados como bump puro 347→348, sem perda de conteúdo do HEAD) + 1 conflito real de mergeable=CONFLICTING.
Durante a validação, os 3 testes novos de SearXNG expuseram um bug real de interação com #10981: `isUnconfiguredLoopbackSearchProvider()` checava o baseUrl ESTÁTICO do catálogo em vez do baseUrl efetivo (após override de `provider_options.baseUrl` ou `providerSpecificData.baseUrl` da conexão), então QUALQUER request a searxng-search — mesmo com override customizado — era rejeitado como se fosse o default não-configurado. Corrigido em `open-sse/handlers/search.ts` (resolve o baseUrl efetivo via `resolveSearchBaseUrl()` antes do skip-check, tanto para o provider primário quanto o alternate). Um teste do próprio #10988 que assumia o comportamento pré-#10981 (default localhost:8888 sempre atendido) foi atualizado para refletir o comportamento já mesclado e intencional (503 quando não configurado).
Validação completa: typecheck limpo, 70/70 testes unit (search-route/search-registry/x-search-provider/searxng-loopback-default), 24/24 vitest MCP, 14/14 integration (search-providers-catalog), lint limpo nos arquivos tocados, docs-counts-sync OK (2 drifts soft pré-existentes, não relacionados), gates estáticos (file-size/complexity/cognitive/dead-code/changelog) todos OK.