ReviewOS

also looking at this

pantry-pm/pantry

fix(install): a lock pin decides what installs while it satisfies its spec

#263
Merged glennmichael123 wants to merge fix/260-honor-lock-pins into main
3 files +118 -1
3 days ago

Closes #260. Unblocks stacksjs/stacks#3062 and the pantry install --frozen-lockfile that Stacks cannot currently scaffold (stacksjs/stacks#2905).

The problem

Every declared range was resolved against the registry and the newest match won, so a committed pantry.lock never fixed what got installed. A plain install rewrote the pin; --frozen-lockfile installed the newer release and only then failed. That makes any upstream release inside a declared range turn a frozen CI run red with no change in the project — curl.se moving 8.22.0 → 8.23.0 between two pushes is what did it to Stacks.

Two things worth noting about the cause:

  • The machinery to consult the lock already existed and had no callers: resolveVersionWithLockfile with prefer_lockfile was never wired into this path.
  • It would not have sufficed on its own either — it returns the pin without asking whether the declared spec still admits it, which would pin straight through a dependency edit.

The rule

lockedVersionSatisfying honours a pin only while it satisfies the declared spec — bun install / npm ci semantics, with pantry update as the thing that moves a pin.

declaredlockinstalls
^3.53.43.53.43.53.4 (even though 3.54.1 exists)
latest / * / empty3.53.43.53.4 — any pin covers a rolling spec; committing a lock is the point
^3.54.03.53.4resolves again — the range no longer admits the pin
3.54.1 (exact)3.53.4resolves itself, so a pin/spec disagreement stays visible
unparseableanythingresolves — never install a pin nothing vouched for

The frozen check now runs before the pipeline, which is the report's other half: a frozen run fails without having written to pantry/. It skips the .env-inferred services from #253, which are deliberately absent from the lock and would otherwise fail every frozen install on a project with a .env.

Verified against the live registry, not just in unit tests

Reproduced the issue's own table with the built binary:

scenariobeforeafter
plain install, lock pins 3.53.4installs 3.54.1, rewrites the lockinstalls v3.53.4, lock byte-identical
frozen, lock satisfiesexit 1 after installing 3.54.1exit 0, pinned version, lock untouched
frozen, lock short a dep—exit 1, nothing written to pantry/, lock untouched

zig build test: 573 pass / 6 skip. The 2 failures (shell.install_route bash + zsh) are pre-existing on main on this machine and unrelated.

One thing for a reviewer

helpers.zig's 21 tests had never run — nothing imported it for their own sake, the same gap package.zig had in #261 — so lib.zig now names it and they all pass. That this has now bitten twice suggests the explicit include list is a standing trap rather than a one-off; worth its own issue, and I did not fold a fix for it in here.

🤖 Generated with Claude Code

3 changed files on the files tab, with 0 review threads.

Sign in to comment on this pull request.