fix(vtz): semver resolver — ^0.27.3 must not match 0.25.12 (#2738)#2747
Merged
Conversation
`vtz install` was incorrectly resolving `esbuild: ^0.27.3` to `0.25.12` when a stale lockfile entry existed. Two places in the resolver were fail-open: 1. **Lockfile fast path** (`resolve_one_task`): trusted any pinned version under the matching `name@range` key without verifying the pinned version still satisfies the range. A stale entry left over from a prior install (or registry-state change) would be silently reused. 2. **Root-dep wiring** (`graph_to_lockfile`): looked up the hoisted package by name only, then wrote the lockfile entry without checking the chosen version satisfies the declared range. Both paths now revalidate via two new helpers, `version_satisfies_range()` and `lockfile_entry_satisfies_range()`. A mismatch falls through to a fresh registry resolve so the correct in-range version is picked. `github:` and `link:` specifiers are exempt (they're not semver). Test strategy (TDD): - Direct unit tests on the helpers for `^0.27.3`, `~0.27.3`, and explicit `>=0.27.3 <0.28.0` covering accept/reject of 0.25.12, 0.27.3, 0.27.5, 0.28.0. - Regression test `test_graph_to_lockfile_rejects_hoisted_version_outside_range` that was confirmed RED without the fix (asserts ^0.27.3 does not wire to a hoisted 0.25.12). - E2E: fresh `vtz install` on a lockfile that previously had the bug — the `esbuild@^0.27.3` entry now resolves to 0.27.7 (highest in the 0.27.x range) and the `node_modules/.bin/esbuild` version matches the package version. Closes #2738 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced Apr 17, 2026
viniciusdacal
added a commit
that referenced
this pull request
Apr 21, 2026
Repo-wide CI failure: @vertz/ci build step dies with "Cannot start service: Host version 0.27.3 does not match binary version 0.27.7". Same signature on every PR built against current main, including unrelated ones (#2910, #2894). The lockfile held two esbuild resolutions — esbuild@0.27.3 and esbuild@^0.27.3 → 0.27.7 — and the platform-specific @esbuild/<os>-<arch> binaries got hoisted at 0.27.7 while the esbuild JS host stayed at 0.27.3, so any vertz-build spawn failed the version handshake. Fix: add "esbuild": "0.27.3" to root package.json overrides. This is the same approach as 01ae939 on a prior branch — collapses every esbuild range to a single resolution so the host + binary always match. vtz install then dedupes the lockfile down to one esbuild entry. The underlying issue is that the semver resolver fix in #2747 correctly started resolving "^0.27.3" to the latest 0.27.7 the moment esbuild released it, which exposed this hoist-inconsistency. Filing a follow-up to have esbuild treated as platform-linked so host + binary always pin together. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
viniciusdacal
added a commit
that referenced
this pull request
Apr 21, 2026
…#2889] (#2907) * fix(db): stringify primitive values written to d.jsonb<T>() on SQLite [#2889] d.jsonb<T>() on SQLite stores values as TEXT and the read path always runs JSON.parse on the raw cell. Writes only stringified plain objects and arrays, so primitives (strings, numbers, booleans) were persisted raw and blew up with JsonbParseError on read-back. Add a CRUD-layer marshaling pass that runs after runJsonbValidators and wraps every non-null, non-DbExpr value for jsonb/json columns in JSON.stringify when the dialect is SQLite. null passes through to emit SQL NULL; DbExpr SQL fragments are left alone. All six write call sites (create/createMany/createManyAndReturn/update/updateMany/upsert, including the upsert updateValues path) go through the new pass. Postgres writes are unchanged. Closes #2889. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(db): cover createMany/updateMany/upsert primitive round-trip [#2889] Review S1 follow-up: the original round-trip tests only exercised create() and update(). Add matching primitive-jsonb round-trip tests for createMany, createManyAndReturn, updateMany, and upsert (both insert and update branches) so a copy-paste regression in any of the 4 remaining call sites is caught. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(db): share PGlite instance across migrateStatus tests to fix CI timeout status.test.ts was creating a fresh PGlite instance in beforeEach for all 24 tests. Each WASM init costs ~300-500ms on CI runners, pushing the file past the vtz test-runner's 60s per-file cap under resource pressure — reproduced twice on this PR's CI while the file passes locally in 9.8s (CI was a stable 9.8s when resources were not contended but failed under load). Hoist PGlite creation to beforeAll and reset state in afterEach by dropping the three tables any test creates (_vertz_migrations, users, posts). Reduces file runtime 9.8s → 0.8s locally (12x faster) without changing test isolation semantics — each test still sees a fresh, empty schema. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(deps): pin esbuild to 0.27.3 via overrides (unblock CI) Repo-wide CI failure: @vertz/ci build step dies with "Cannot start service: Host version 0.27.3 does not match binary version 0.27.7". Same signature on every PR built against current main, including unrelated ones (#2910, #2894). The lockfile held two esbuild resolutions — esbuild@0.27.3 and esbuild@^0.27.3 → 0.27.7 — and the platform-specific @esbuild/<os>-<arch> binaries got hoisted at 0.27.7 while the esbuild JS host stayed at 0.27.3, so any vertz-build spawn failed the version handshake. Fix: add "esbuild": "0.27.3" to root package.json overrides. This is the same approach as 01ae939 on a prior branch — collapses every esbuild range to a single resolution so the host + binary always match. vtz install then dedupes the lockfile down to one esbuild entry. The underlying issue is that the semver resolver fix in #2747 correctly started resolving "^0.27.3" to the latest 0.27.7 the moment esbuild released it, which exposed this hoist-inconsistency. Filing a follow-up to have esbuild treated as platform-linked so host + binary always pin together. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
Fixes #2738.
vtz install's semver resolver was producing lockfile entries likeesbuild@^0.27.3 → 0.25.12, even though0.25.12clearly does not satisfy^0.27.3. This caused every esbuild spawn across the monorepo to fail with Host version "0.27.3" does not match binary version "0.25.12" because the optional platform binaries still pinned to 0.27.3 while the host library resolved to 0.25.12.Root Cause
Two fail-open paths in
native/vtz/src/pm/resolver.rs:resolve_one_task(~line 262) trusted any pinned version under the matchingname@rangekey without verifying the pin still satisfies the range. A stale entry left over from a prior install or registry-state change was silently reused.graph_to_lockfile(~line 526) looked up the hoisted package by name only, then wrote a lockfile entry pointing at whatever hoisted version happened to be present — even if it was out of range.The low-level
resolve_version()andnode_semveritself were already correct — the bug was purely in the reuse / wiring glue that bypassed the satisfies-check.Fix
Two new helpers, both in
resolver.rs:version_satisfies_range(version, range)— thin wrapper overRange::parse(...).satisfies(Version::parse(...))returningfalseon parse failure (fail-closed).lockfile_entry_satisfies_range(entry, range)— same, but treatsgithub:andlink:specs as always-valid since they're pinned outside semver.Applied at both bug sites:
lockfile_entry_satisfies_range(entry, range)before reusing a pinned version. A failing check falls through to a fresh registry resolve.graph_to_lockfile's hoisted-by-name lookup is replaced with hoisted-by-name-and-range.Test Strategy (TDD)
All tests in
native/vtz/src/pm/resolver.rs, rancargo test -p vtz --libto confirm red → green.test_version_satisfies_range_caret_zero_x—^0.27.3must reject 0.25.12 / 0.28.0 and accept 0.27.3 / 0.27.5.test_version_satisfies_range_tilde— same bounds for~0.27.3.test_version_satisfies_range_explicit—>=0.27.3 <0.28.0accept/reject.test_resolve_version_caret_rejects_lower_minor—^0.27.3against a version list containing 0.25.12 must pick 0.27.3.test_lockfile_entry_satisfies_for_current_range_rejects_stale— a pinned 0.25.12 under the^0.27.3key must fail the guard; 0.27.3 passes.test_graph_to_lockfile_rejects_hoisted_version_outside_range— confirmed RED on the prior code path; GREEN with the fix.E2E Verification
Rebuilt the release binary (
cargo build --release -p vtz), linked viascripts/link-runtime.sh, cleared~/.vertz/cache/npm/store/esbuild*+@esbuild+*, ranvtz install:Before:
esbuild@^0.27.3: version "0.25.12"(WRONG)After:
esbuild@^0.27.3: version "0.27.7"(CORRECT — highest in [0.27.3, 0.28.0))And
node_modules/.bin/esbuild --versionnow prints0.27.7, matching the installed package version.Quality Gates
cargo test -p vtz --lib— 3456 passed, 0 failedcargo clippy -p vtz --all-targets -- -D warnings— cleancargo fmt --all -- --check— cleanTest Plan
-D warningsvtz installproduces correctesbuild@^0.27.3 → 0.27.xlockfile entrynode_modules/.bin/esbuildmatches installed package version🤖 Generated with Claude Code