Fail fast on package.json/bun.lock drift and stop rewriting non-registry catalog refs - #102
Open
typedrat wants to merge 2 commits into
Open
Fail fast on package.json/bun.lock drift and stop rewriting non-registry catalog refs#102typedrat wants to merge 2 commits into
typedrat wants to merge 2 commits into
Conversation
…network Two hook fixes for failure modes found installing opencode offline. Both stem from the same bun behavior: any drift between package.json and bun.lock makes bun re-resolve the affected dependencies, and re-resolving a git/github/remote-tarball dependency downloads it unconditionally — fatal in the sandbox no matter how complete the cache is. - Detect trustedDependencies / patchedDependencies drift between the root package.json and bun.lock and fail with an actionable message (refresh bun.lock, regenerate bun.nix). Projects routinely commit a lockfile whose copies of these sections lag package.json (opencode's does); without the check the drift surfaces as a wall of ConnectionRefused errors at resolve time with no hint of the cause. - Leave catalog: references pointing at non-registry specs (github:, git+, tarball URLs, file:) unrewritten. bun resolves those natively from the lockfile's catalog section; rewriting them to their resolution registered as a changed spec and forced a re-resolve. The hook now runs the prep script whenever bun.lock exists (previously only when it contained catalog: refs), so the drift check covers every project.
Runs resolve-catalog.ts against three inline fixtures: drifted trustedDependencies/patchedDependencies must fail with a diagnostic naming the drifted entries, identical-but-reordered sections must pass, and catalog: refs must be rewritten to exact versions except when they resolve to non-registry specs. The drifted fixture has no catalog: refs, pinning that the check runs for plain projects too.
typedrat
force-pushed
the
typedrat/hook-drift-detection
branch
from
July 26, 2026 00:55
fda92dc to
150dab2
Compare
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.
Two fixes to the install hook's offline-prep step, both stemming from the same bun behavior: any drift between package.json and bun.lock makes bun re-resolve the affected dependencies, and re-resolving a git/github/remote-tarball dependency downloads it unconditionally — fatal in the Nix sandbox no matter how complete the cache is. Both were found getting the opencode workspace (~3,200 packages, the reproduction from #77) to install offline, but neither depends on the manifest-cache work in the follow-up PR: the hook doesn't pass
--frozen-lockfile, so both failure modes are reachable on master today.trustedDependencies/patchedDependenciessections lag package.json (opencode's does). Any mismatch makes bun distrust the lockfile mapping wholesale, and the re-resolution surfaces as a wall ofConnectionRefusederrors at resolve time with no hint of the cause. The hook's prep script now compares those sections (as a set / as key-value pairs, so pure ordering differences don't count) and fails up front with the actual diagnosis and the fix: refresh bun.lock, commit, regenerate bun.nix. This is deliberately a hard error rather than a silent sync — a lockfile that disagrees with package.json means the dependency cache was generated from something other than what the project declares.catalog:rewrite was converting refs to their resolution even when that resolution is a github/git/tarball/file spec. That substitution itself registers as a changed spec and forces the same network re-resolve. Those refs are now left ascatalog:— bun resolves them natively from the lockfile's catalog section.The prep script also runs whenever bun.lock exists now, not only when it contains
catalog:refs, so the drift check covers every project.Testing: a new flake check (
lockfileDriftDetection) runs the prep script against three fixtures — drifted sections must fail naming the drifted entries, identical-but-reordered sections must pass, and a catalog with both a registry and a github ref must rewrite the former and preserve the latter. It's non-vacuous: against the current script it fails with "drifted fixture: expected failure, got exit 0". End-to-end, opencode-as-committed dies in seconds with the drift diagnostic instead of twenty minutes ofConnectionRefused; with its lockfile refreshed it proceeds normally.Compatibility: builds of projects with a synced lockfile are unaffected. Projects with drifted lockfiles previously failed at resolve time with inscrutable network errors; they now fail earlier with an actionable message. A project whose bun.lock has a non-registry catalog entry was previously broken by the rewrite itself; it now builds.
This is the second of three PRs from getting the full opencode workspace building offline; it stands alone and doesn't depend on the others.