Repository navigation
fix(db): converge the install and upgrade schemas; stop ENOSPC from faking a divergence - #11845
Merged
Merged
Conversation
…aking 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.
…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.
check:docs-counts-sync enforces the shipped migration count as a STRICT claim in README.md, AGENTS.md and llm.txt.
Contributor
CI Coverage Report
Coverage artifact was not available for this run. |
diegosouzapw
added a commit
that referenced
this pull request
Aug 28, 2026
…11855) * 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.
diegosouzapw
added a commit
that referenced
this pull request
Aug 28, 2026
…nc-back step 2/2
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):
b090b60 / 026e1ca deps: nanoid 3.3.18 equal, dompurify 3.4.14 newer
918fba5 .gitignore: /_tasks already anchored
5f0a394 #10026 hide health-check-excluded models — same helper, 5 call sites
c68cda7 #11075 shared passthrough providers — superseded by #11071/#11078
superseded, one piece kept:
ca23eed #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:
8778ea7 stamp dist/BUILD_SHA before the npm provenance gate (#11721)
aa52351 decouple the Bun image from the release manifest (#11724)
925feb2 let the bun digest artifact be absent (#11740)
b65ef33 size the install-upgrade gate to a measured run
0ce2123 #11845 converge install/upgrade schemas (migration renumbered in
the next commit: 163 collides with 163_radar_feed_cache_generated_at)
b7c07ed #11855 install-upgrade gate on disk, not tmpfs
8e2fb04 #11864 drop *.nft.json from the npm tarball (413)
dea6bb8 #11877 publish npm from a hosted runner (provenance 422)
handled by the sync script that follows (CHANGELOG protocol):
b4ec780 Release v3.8.50 — squash of content this branch already carries
5458026 / c44c0a2 CHANGELOG aggregation, stats and top-25
applied separately (its own commit, ten files):
65e8115 #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.
diegosouzapw
added a commit
that referenced
this pull request
Aug 28, 2026
…nch collision with 163_radar_feed_cache_generated_at) #11845 landed on main as 163 while release/v3.8.51 had already used 163–168. Per the cross-PR collision precedent (#3365/#3371) the later arrival takes the next free number; the SQL is idempotent, so installs that already ran it as 163 on main are unaffected.
This was referenced Sep 7, 2026
Merged
diegosouzapw
added a commit
that referenced
this pull request
Sep 7, 2026
…ments, cover every cycle commit, credit every contributor (#12971) Mid-cycle reconciliation of `## [3.8.51]` against the full cycle range `release/v3.8.50..release/v3.8.51` (0915890..d6f3150, 696 non-merge commits): - fold the 366 `changelog.d/` fragments into the section (features had been landing under `[Unreleased]` because the aggregator appends at the FIRST matching heading) and delete them - drop 25 fragments that duplicate bullets already shipped in `[3.8.50]` / `[Unreleased]` (six phantom fragments re-added by branches cut before the v3.8.50 aggregation; 19 pre-cycle PR fragments) - generate one bullet per cycle commit that had no fragment (350 commits: 44 features / 196 fixes / 91 maintenance, 17 Dependabot bumps rolled up), carrying the merged PR link and `— thanks @author` - credit audit: link every fragment to the PR that actually landed it (`git log --diff-filter=A`), fix three `#PR_NUMBER` placeholders and two misnumbered fragments (#11845, #11864), credit the recreated PRs to their original authors (#11887–#11892 → @MumuTW, #12255 → @backryun, #11771 → @Rahulsharma0810), the nine hartmark co-authored fixes, and the deleted PR #11370 to @kriptoburak - add the "Release by the numbers" block, the Top-25 ranking (mailmap + GitHub login) and the mandatory `### 🙌 Contributors` hall (111 external contributors + maintainer, generated by `release:contributors --inject`) - resync the 41 i18n CHANGELOG mirrors Gates: check:changelog-integrity OK, check:docs-sync PASS.
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…nc-back step 2/2
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):
7124dd3 / bae33e2 deps: nanoid 3.3.18 equal, dompurify 3.4.14 newer
ceb8e3e .gitignore: /_tasks already anchored
bf87c7d cheaper-inference#10026 hide health-check-excluded models — same helper, 5 call sites
3816ade cheaper-inference#11075 shared passthrough providers — superseded by cheaper-inference#11071/cheaper-inference#11078
superseded, one piece kept:
b735371 cheaper-inference#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:
a93d123 stamp dist/BUILD_SHA before the npm provenance gate (cheaper-inference#11721)
dd329d7 decouple the Bun image from the release manifest (cheaper-inference#11724)
a5e649b let the bun digest artifact be absent (cheaper-inference#11740)
e9045c4 size the install-upgrade gate to a measured run
f5addae cheaper-inference#11845 converge install/upgrade schemas (migration renumbered in
the next commit: 163 collides with 163_radar_feed_cache_generated_at)
a38590e cheaper-inference#11855 install-upgrade gate on disk, not tmpfs
8588804 cheaper-inference#11864 drop *.nft.json from the npm tarball (413)
28bde09 cheaper-inference#11877 publish npm from a hosted runner (provenance 422)
handled by the sync script that follows (CHANGELOG protocol):
fc15eef Release v3.8.50 — squash of content this branch already carries
2b80128 / 4398260 CHANGELOG aggregation, stats and top-25
applied separately (its own commit, ten files):
837b36a cheaper-inference#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.
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…nch collision with 163_radar_feed_cache_generated_at) cheaper-inference#11845 landed on main as 163 while release/v3.8.51 had already used 163–168. Per the cross-PR collision precedent (cheaper-inference#3365/cheaper-inference#3371) the later arrival takes the next free number; the SQL is idempotent, so installs that already ran it as 163 on main are unaffected.
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…aking a divergence (cheaper-inference#11845) * 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
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…heaper-inference#11855) * 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 cheaper-inference#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 cheaper-inference#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.
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…ments, cover every cycle commit, credit every contributor (cheaper-inference#12971) Mid-cycle reconciliation of `## [3.8.51]` against the full cycle range `release/v3.8.50..release/v3.8.51` (104d4f8..8da2a33, 696 non-merge commits): - fold the 366 `changelog.d/` fragments into the section (features had been landing under `[Unreleased]` because the aggregator appends at the FIRST matching heading) and delete them - drop 25 fragments that duplicate bullets already shipped in `[3.8.50]` / `[Unreleased]` (six phantom fragments re-added by branches cut before the v3.8.50 aggregation; 19 pre-cycle PR fragments) - generate one bullet per cycle commit that had no fragment (350 commits: 44 features / 196 fixes / 91 maintenance, 17 Dependabot bumps rolled up), carrying the merged PR link and `— thanks @author` - credit audit: link every fragment to the PR that actually landed it (`git log --diff-filter=A`), fix three `#PR_NUMBER` placeholders and two misnumbered fragments (cheaper-inference#11845, cheaper-inference#11864), credit the recreated PRs to their original authors (cheaper-inference#11887–cheaper-inference#11892 → @MumuTW, cheaper-inference#12255 → @backryun, cheaper-inference#11771 → @Rahulsharma0810), the nine hartmark co-authored fixes, and the deleted PR cheaper-inference#11370 to @kriptoburak - add the "Release by the numbers" block, the Top-25 ranking (mailmap + GitHub login) and the mandatory `### 🙌 Contributors` hall (111 external contributors + maintainer, generated by `release:contributors --inject`) - resync the 41 i18n CHANGELOG mirrors Gates: check:changelog-integrity OK, check:docs-sync PASS.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The reported defect was mostly a false alarm — and the log said so
The v3.8.50 npm publish failed
check:install-upgradewith what looked like a serious migration gap: 15 tables a clean install creates that an upgrade does not.It reported four failures, not one, and the second invalidates the third:
Between them sit 5,611 lines of:
Phase B's
npm installof the 275 MB tarball filled the runner's disk, npm reported ENOSPC as a warning and exited 0, the truncated package madeomniroute serveexit before serving,runMigrations()never ran — and the gate compared a healthy clean install against an upgrade that never happened. The 15 missing tables were the intact 3.8.49 schema.Proven, not assumed: a real booted 3.8.49 database (115 tables, 130 migrations applied) replayed through the current runner applies 29 migrations and reaches 130 tables. Full convergence. Packaging and the mass-migration guard were both ruled out by experiment.
Two genuine defects, both fixed
1.
model_capabilitieshad no migration. It was created only byensureCapabilitiesTable()(src/lib/modelsDevSync.ts) on the first models.dev sync — so whether a database had it depended on timing, not schema version. Now migration163, withensureCapabilitiesTable()kept as an idempotent no-op safety net and a test pinning the two definitions against drift.2. The gate's version assertion could never pass on 3.8.50. GHSA-mvf8-qc78-5mxm made
/api/monitoring/healthwithholdversionfrom anonymous callers; the probe readbody.versionwith no credential.check:pack-boothad already solved this with a token —check:install-upgradewas left behind.Gate hardened
spawnSync:execFileSyncpiped stderr straight to the parent, so the script never saw the warnings)Also
The
cache_metricsallowlist justification was factually wrong ("a CREATE that left the migration set"); it is lazily created insrc/lib/semanticCache.ts, the same class asmodel_capabilities. Corrected, with which cause needs a migration rather than an allowlist. Nothing was added to the allowlist and the gate was not weakened.Tests
tests/unit/db-install-upgrade-schema-parity.test.ts(new, TDD — red before, green after) plus 3 ENOSPC-guard tests. Green: db-* (986), check-* (487), typecheck:core, migration-numbering (160), changelog-integrity, docs-sync, docs-counts.Honest caveats
npm packof the 2.8 GBdist/takes >68 min on the devbox versus 4.5 min on the CI runner. And ENOSPC is not reproducible here by construction — 209 GB free. CI is the real proof for this PR.tests/unit/check-deps.test.tsfails on@tensorflow/tfjs— pre-existing base-red, no manifest touched.audit_log,cache_metrics,combo_health,prompt_templates, …) — the same latent class, able to fake this alarm in either direction on a future run. Deliberately out of scope; the pattern is documented.