Fix gem pair model ignoring custom lockfile and Bundler 1 twins (#749, #751) - #768
Open
Mikola Lysenko (mikolalysenko) wants to merge 4 commits into
Open
Mikola Lysenko (mikolalysenko) wants to merge 4 commits into
Mikola Lysenko (mikolalysenko) wants to merge 4 commits into
Conversation
Assisted-by: Claude Code:claude-opus-5-5
This was referenced Oct 4, 2026
Hosted mode wired the wrong gem files in two Bundler layouts, so the scan reported success (and its VEX attested a patch) while Bundler installed the unpatched gem or frozen installs failed: - Bundler 4's custom lockfile (BUNDLE_LOCKFILE, env or .bundle/config) was ignored, so the lock Bundler reads was never pinned (#749). A lockfile naming anything but the pair's own default lock is now refused in hosted (redirect_gem_bundle_lockfile_unsupported) and vendored (gemfile_not_loaded) mode before any write. - A Gemfile + gems.rb twin always followed Bundler >= 2 and wired gems.rb, but Bundler 1.x loads the Gemfile (#751). A twin whose locks say BUNDLED WITH 1.x is now wired through the Gemfile pair, and twin locks that disagree on the major are refused (redirect_gem_twin_bundler_versions_diverge). Assisted-by: Claude Code:claude-opus-5-5
Two host capstones in e2e_redirect_gem_build: a Bundler 4 project with `lockfile custom.lock` (and a leftover Gemfile.lock) redirects and attests nothing and still installs frozen (#749), and a Bundler 1.x Gemfile + gems.rb twin is wired through the Gemfile and a fresh checkout installs the patched gem (#751). Each skips on the Bundler line it does not apply to. Assisted-by: Claude Code:claude-opus-5-5
Mikola Lysenko (mikolalysenko)
marked this pull request as ready for review
October 4, 2026 09:53
Collaborator
Author
|
BugBot review Generated by Claude Code |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 731f489. Configure here.
Collaborator
Author
|
Ready for review at
Generated by Claude Code |
Tanmay Singla (Tanmay182003)
approved these changes
Oct 5, 2026
Release notes are written when a release is cut, from the merged PR log and the code, so PRs no longer edit CHANGELOG.md. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This branch has not been deployed
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.
LLM Description written by Claude Code:claude-opus-5-5
Fixes #749
Fixes #751
Summary
Hosted gem mode wired the wrong files in two Bundler layouts. In both, the scan reported success (and
--vexattested the patch) while Bundler installed the unpatched gem or every frozen install failed. Both now wire the pair Bundler actually loads, or refuse before writing anything.Root cause
formats::gem::manifest::LoadedManifestis the single model of "which manifest/lock pair does Bundler load", and hosted mode (keep_bundler_loaded_gem_files) and vendored mode (gem_manifest_refusal) both rely on it. It modelledBUNDLE_GEMFILE, but:lockfilesetting /BUNDLE_LOCKFILE), so it never pins the lock Bundler uses and frozen installs fail with no warning #749: it derived the lock only from the manifest name, so it never saw Bundler 4's custom lockfile (BUNDLE_LOCKFILEenv, orlockfilein.bundle/config). The lock Bundler reads was never pinned. A leftoverGemfile.lockwas rewritten instead, even though Bundler ignores it.gems.rbin a Gemfile/gems.rb twin locked by Bundler 1.17, which loadsGemfile, so the install stays unpatched while the in-run VEX attests it #751: default discovery for aGemfile+gems.rbtwin always followed Bundler ≥ 2 (gems.rbfirst). Bundler 1.x loadsGemfilefirst.Fix
LoadedManifest::with_lockfileresolves the configured lockfile the wayBundler::CLIdoes (env first, then app config;BUNDLE_IGNORE_CONFIGhonoured; relative to the project root). If it names the loaded pair's own lock, nothing changes. Anything else is the newUnsupportedLockfile:redirect_gem_bundle_lockfile_unsupported(nothing written, nothing attested);gemfile_not_loaded.default_twin_manifestreads both twin locks'BUNDLED WITH(new sharedformats::gem::bundled_with_major):Gemfile+Gemfile.lock;gems.rb(unchanged);redirect_gem_twin_bundler_versions_diverge.Tests (red → green)
hosted_memory_engine::a_bundler4_custom_lockfile_is_refusede2e_redirect_gem_build::gem_hosted_bundler4_custom_lockfile_redirects_nothing(real Bundler 4.0.17)redirected: 1, rewrittenFiles: [Gemfile, Gemfile.lock], vex statements: 1bundle installof the untouched project succeedsruby_crawler::loaded_manifest_reads_the_lockfile_setting(disk, no leftover lock; env vs config priority;BUNDLE_IGNORE_CONFIG)vendor::gem::a_bundler4_custom_lockfile_is_refusedhosted_memory_engine::a_lockfile_setting_naming_the_default_lock_is_wired(control)hosted_memory_engine::a_bundler1_twin_wires_the_gemfile_pairhosted_memory_engine::a_twin_with_diverging_bundler_majors_is_refusedhosted_memory_engine::a_bundler2_twin_still_wires_gems_rb(control)e2e_redirect_gem_build::gem_hosted_bundler1_twin_wires_the_gemfile_and_installsmanifest.rsunit tests (with_lockfile_*,default_twin_manifest_*,config_lockfile_*),bundled_with_major_reads_the_version_lineLocal results
cargo clippy --workspace --all-features -- -D warnings: clean.cargo fmt --all -- --checkis not clean onmainitself (≈500 diffs, and CI has no fmt gate), so I formatted only the hunks I touched.cargo test --workspace --all-features --lib --bins: the core lib has 4848 passed and 4 failed. All four (copy_tree::relax_loop_must_not_traverse_symlinked_root,vlt_heal::an_unremovable_hidden_lock_keeps_every_store_entry,pypi_poetry::wire_write_failure_*,pypi_requirements::wire_failure_rolls_back_*) are chmod-based write-failure tests that can't fail writes when run as root (uid 0) in this sandbox. They don't touch gem code.hosted_memory_engine33/33,hosted_memory_parity,in_process_vendorande2e_redirect_gem_stale_installall ok.in_process_redirecthas 104 passed and 3 failed, again only the chmod-based write-failure tests (root sandbox).e2e_redirect_gem_build --ignored(real Bundler 4.0.17): new custom-lockfile, dual-boot and gems.rb arms pass.cargo test --workspacelocally because the sandbox's disk allowance runs out building every integration-test binary. CI runs the full suite.Notes / follow-ups
LoadedManifest::pair. Once both land, a Bundler 1.x twin's post-install VEX should usedefault_twin_manifesttoo. This PR doesn't touch those readers, to avoid conflicting with Fix gem lock readers ignoring gems.locked (#736) #750.lockfile "custom.lock"DSL (issue matrix row 4) is Ruby code the model can't read. Bundler 4.0.17 itself fails frozen installs on that shape before any scan.BUNDLE_LOCKFILEsetting is refused even on Bundler < 4 (which ignores it). That's a conservative, fail-closed choice.Note
Medium Risk
Changes which gem manifest/lock files get rewritten in hosted and vendored modes; incorrect behavior previously caused silent unpatched installs or broken frozen
bundle install, but the fix still touches lockfile mutation paths.Overview
Gem hosted/vendored wiring now matches Bundler’s real manifest and lock pair, fixing false success and bad VEX when Bundler 4 uses a custom lockfile (#749) or when a
Gemfile/gems.rbtwin was last locked with Bundler 1.x (#751).LoadedManifestgainswith_lockfile(env/configBUNDLE_LOCKFILE, same priority as Bundler). If the configured lock is not the pair’s default, the run is refused before any write withredirect_gem_bundle_lockfile_unsupported(hosted) orgemfile_not_loaded(vendored), instead of rewriting a leftoverGemfile.lockBundler ignores.For Gemfile + gems.rb twins,
default_twin_manifestuses each lock’sBUNDLED WITHmajor: all 1.x → wireGemfile/Gemfile.lock; otherwise keep Bundler ≥2’sgems.rbpath. Conflicting majors refuse withredirect_gem_twin_bundler_versions_diverge. The hosted engine’s candidate filtering andruby_crawler::bundler_loaded_manifest(BundlerEnv) apply the same rules.Docs and tests (memory engine, e2e with real Bundler, manifest unit tests) cover custom lock refusal, Bundler 1 twin wiring, and divergent-twin refusal.
Reviewed by Cursor Bugbot for commit 731f489. Configure here.
Generated by Claude Code