Repository navigation
Lock inventory reads only Gemfile.lock, so a gems.rb project's gems.locked is invisible and a stale Gemfile.lock is read instead #736
Description
Activity
- addedbugSomething isn't workingSomething isn't workingpm:bundlerBundler (RubyGems)Bundler (RubyGems)arch-auditFiled by a scheduled architecture audit routine (see the architecture review discussion)Filed by a scheduled architecture audit routine (see the architecture review discussion)
on Oct 4, 2026 mikolalysenko commented
on Oct 4, 2026 CollaboratorAuthorMore actions[agent] Triaged:
priority:p1(Bundler). Not a duplicate; no existing fix PR. #341 / #390 fixed the writer side of the samegems.rb/Gemfilepair selection; this is the reader side (lock inventory,gem_remotes, VEX discovery).
Generated by Claude Code
mikolalysenko commented
on Oct 4, 2026 CollaboratorAuthorMore actions[agent] Bundler bug-hunt routine (ledger #316): a real-install repro of the VEX-discovery half of this issue, at
045d7ec.Shape: the project moved from
Gemfiletogems.rb. It hasgems.rb+gems.locked(upstreamcolorize 0.8.1from rubygems.org, with CHECKSUMS), plus a leftoverGemfile.lockholding the hosted redirect (remote: …/patch-registry/gem/…,colorize (= 0.8.1)!) from a hosted scan run while the project still usedGemfile. There is noGemfileand no.socket/manifest.json.# gems.rb: source "https://rubygems.org"; gem "rake"; gem "colorize", "~> 0.8.1" bundle config set --local path vendor/bundle && bundle install && bundle lock --add-checksums && rm -rf vendor # Gemfile.lock = the redirected lock a `scan --mode hosted` wrote for the same deps as Gemfile/Gemfile.lock socket-patch vex --output pre.json --product pkg:generic/app@1 --cwd . --patch-server-url $MOCK --api-url $MOCK --org org --api-token fake bundle install && grep -c SOCKET_PATCHED vendor/bundle/ruby/*/gems/colorize-0.8.1/lib/colorize.rb
Bundler (Ruby 3.3.6, Linux) vexbefore installbundle installinstalled bytes 4.0.17 (×2) exit 0, not_affected(inline_mitigations_already_exist) forpkg:gem/colorize@0.8.1exit 0, reads gems.lockedunpatched (0 markers) 2.6.9 exit 0, not_affectedexit 0 unpatched 2.4.22 n/a (no CHECKSUMS, so the leftover lock carries no registry pin) Control: the same project without the leftover
Gemfile.lockmakesvexrefuse with "no hosted or vendored patch references were found", which is correct. After the install,vexin verify mode refuses (not_applied), so the false attestation only hits lock-only checkouts, such as CI that runsvexbeforebundle install. That matches theBUNDLER_LOCKSloop invex/discover/gem.rsreading the lock Bundler ignores. The patch API and registry were mocked on loopback; the upstream was the real rubygems.org.
Generated by Claude Code
mikolalysenko commented
on Oct 4, 2026 CollaboratorAuthorMore actions[agent] Claiming this issue (shared root cause: the gem lock readers pick their lockfile by a hard-coded name instead of Bundler's loaded manifest/lock pair). Branch: agent/fix-gem-loaded-lock-readers. Claim-ID: 2026-10-04T04:20:29Z-681f48
Generated by Claude Code
mikolalysenko commented
on Oct 4, 2026 CollaboratorAuthorMore actions- added 2 commits that reference this issue
on Oct 4, 2026 - added 2 commits that reference this issue
on Oct 5, 2026
[agent] Filed by the scheduled architecture audit routine (ecosystems and formats). Register: discussion #560 register.
Kind: bug. Source: new finding, register
E56(related to review Part 5.4 on gem section models,E19).Problem
Bundler loads one manifest/lock pair. When there is no
BUNDLE_GEMFILE, that isgems.rb+gems.lockedifgems.rbexists, andGemfile+Gemfile.lockotherwise. socket-patch has a shared resolver for this,LoadedManifest::pair.`` Hosted mode uses it throughkeep_bundler_loaded_gem_files, which also works over a memory view. The vendored refusal (`gem_manifest_refusal`) and the crawler use it as well.Two other readers each choose the lock their own way:
Gemfile.lock:lock_inventory/gem.rs#L46-L53(view.read_text("Gemfile.lock")).gem_remotesdoes the same.vex/discover/gem.rs#L133-L138(for file in BUNDLER_LOCKS).That makes three rules for one question.
Proof by execution (a unit probe at
045d7ec, run twice, not committed). The project hasgems.rb(gem "rack", "2.2.8") and a validgems.lockedlockingrack (2.2.8):Consumers that see the wrong set
hosted/memory/mod.rs#L571-L578).`` So for agems.rbproject it finds no gems, even though its own gem rewriter would edit `gems.locked`. With a leftover `Gemfile.lock`, it plans versions Bundler doesn't use.scan/discovery.rs#L71-L95) and apply'slockfile_resolved(apply.rs#L2304-L2306) miss lockfile-only gems, or count stale ones.vex/discover/mod.rs#L1611-L1613) judges a hosted gem pin against the wrong lock.Symptoms
None filed. #341 and #390 fixed the same "wrong pair" class for the vendored and hosted writers but not for the readers.
Impact
Medium for
gems.rbprojects, which are Bundler's documented alternate spelling. For a stale-twin project it is incorrect data rather than missing data.Proposed change
keep_bundler_loaded_gem_filesdoes intoformats::gem::manifestasloaded_lock(view: &ProjectView) -> Option<&'static str>(disk:bundler_loaded_manifest; memory:.bundle/configonly, as today).inventory_gemfile_lock_raw_in,gem_remotesand VEX discovery'sextractread only that lock. VEX may still diagnose the ignored twin."Gemfile.lock"reads inlock_inventory/gem.rsand theBUNDLER_LOCKSloop's both-locks rule.Size and scope
4 files and under 120 changed production lines. Out of scope: the three gem section models (E19) and
BUNDLE_GEMFILEpointing outside the root, which stays unsupported.Acceptance criteria
inventory_projecton agems.rb+gems.lockedproject returns its gems.Gemfile.lockbesidegems.rb+gems.locked, inventory and VEX discovery read onlygems.locked..bundle/configBUNDLE_GEMFILE: Gemfilebeside agems.rbreadsGemfile.lock(disk and memory views).gems.rbproject yields a gem candidate.lock_inventory,vex::discover::gem,formats::gem::manifestand hosted gem tests stay green.Dependencies
None. It touches files near open #712, #684 and #621 (gem settings), but not the same functions.