[agent] Found by the scheduled Bundler (RubyGems) bug-hunt routine (ledger #316).
Summary
#1002 fixed #1001 only for an explicit path setting. Bundler also stops using system gems in three other ways:
deployment true (the .bundle/config setting or env BUNDLE_DEPLOYMENT=true), which installs into vendor/bundle;
simulate_version 5 on Bundler 4.x, which installs into .bundle/;
default_install_uses_path true on Bundler 2.x, which also installs into .bundle/.
RubyCrawler::bundler_install_homes still treats all three as "Bundler uses system gems". So on a fresh checkout or a cold CI cache, before the project's bundle dir exists, an unpatched same-version copy in the machine's gem env home is reported as a stale install. The warning is the shared-home flavor, and its remedy tells the user to bundle config set --local path vendor/bundle, which is what deployment already does. The flagged purl is then dropped from the same run's --vex. With one patched gem, scan --mode hosted --vex exits 1 with no_applicable_patches.
Impact
Proof with real Bundler (Ruby 3.3.6)
The system copy is never used in any of these shapes:
export GEM_HOME=$PWD/gh GEM_PATH=$PWD/gh
gem install colorize -v 0.8.1 --no-document # the shared-home copy
mkdir proj && cd proj
printf 'source "https://rubygems.org"\ngem "colorize", "0.8.1"\n' > Gemfile
bundle _4.0.18_ lock
bundle _4.0.18_ config set --local deployment true # or BUNDLE_DEPLOYMENT=true, or simulate_version 5
bundle _4.0.18_ install
bundle _4.0.18_ exec ruby -e 'puts Gem.loaded_specs["colorize"].full_gem_path'
| Setting |
Bundler |
Installs (fetches) into, and loads from |
local deployment true |
4.0.18 |
proj/vendor/bundle/ruby/3.3.0/gems/colorize-0.8.1 |
env BUNDLE_DEPLOYMENT=true |
2.5.22 |
proj/vendor/bundle/ruby/3.3.0/gems/colorize-0.8.1 |
local simulate_version 5 |
4.0.18 |
proj/.bundle/ruby/3.3.0/gems/colorize-0.8.1 |
socket-patch repro (main 05fd82b)
I used a throwaway test in crates/socket-patch-cli/tests/e2e_redirect_gem_stale_install.rs. It reuses that file's mount_api, write_manifest_pair, stage_system_home_copy and hosted_vex_scan_with_gem_on_path, exactly like gem_hosted_explicit_bundle_path_ignores_system_home_copy, and only changes the .bundle/config / env setting. I ran it twice: once with the harness's fake gem, and once with the real gem and GEM_HOME pointed at the staged home. Both runs gave the same results:
Case (.bundle/config or env; nothing installed in the project yet) |
stale warnings |
--vex attests |
exit |
control: BUNDLE_PATH: "vendor/bundle" |
0 |
yes |
0 |
BUNDLE_DEPLOYMENT: "true" |
1 |
no |
1 (no_applicable_patches) |
env BUNDLE_DEPLOYMENT=true |
1 |
no |
1 |
BUNDLE_SIMULATE_VERSION: "5" |
1 |
no |
1 |
BUNDLE_DEFAULT_INSTALL_USES_PATH: "true" |
1 |
no |
1 |
| control: no setting (Bundler really uses system gems) |
1 |
no |
1 (correct) |
Warning emitted for the deployment case:
pkg:gem/stale-probe-gem@1.0.0 was switched to its hosted patch, but a stale UNPATCHED install is materialized in the shared gem home at …/system-home/gems/stale-probe-gem-1.0.0 — bundle install reuses it without refetching … prefer switching this project to a project-local bundle path (bundle config set --local path vendor/bundle, then bundle install) …
Expected vs actual
- Expected: CLI_CONTRACT.md "Gem stale-install guard" says the
gem env homes count only when Bundler uses system gems, "since with such a path bundle install fetches non-default gems into it and never reuses a system copy". Bundler's Settings#path derives the same kind of non-system path from deployment (→ vendor/bundle) and from default_install_uses_path / simulate_version 5 (→ .bundle) whenever no tier sets path / path.system / disable_shared_gems. The guard should skip the system homes in those cases, as it does for an explicit path.
- Actual: the guard models only an explicit
path. Its "system gems" test is !default_root_has_stores && !bundler_sets_explicit_path(..), so it falls back to system homes until the deployment store has been installed.
OS × version
| OS |
Bundler |
Result |
| Linux |
4.0.18 (deployment, simulate_version 5), 2.5.22 (env deployment): real Bundler confirms the system copy is unused |
guard fails on 05fd82b (the logic is OS- and version-independent) |
| macOS / Windows |
— |
not probed; same code path |
First bad version
This isn't a regression. The guard judged every gem env home before #1002 (v4.0.0 included). #1002 (f23fd82) narrowed that for an explicit path only.
Suspect code
Backlog review — 2026-10-08
Priority: P1 → P2. Unused system gem copies trigger a false stale-install warning and VEX refusal for a correctly selected Bundler path.
[agent] Found by the scheduled Bundler (RubyGems) bug-hunt routine (ledger #316).
Summary
#1002 fixed #1001 only for an explicit
pathsetting. Bundler also stops using system gems in three other ways:deployment true(the.bundle/configsetting or envBUNDLE_DEPLOYMENT=true), which installs intovendor/bundle;simulate_version 5on Bundler 4.x, which installs into.bundle/;default_install_uses_path trueon Bundler 2.x, which also installs into.bundle/.RubyCrawler::bundler_install_homesstill treats all three as "Bundler uses system gems". So on a fresh checkout or a cold CI cache, before the project's bundle dir exists, an unpatched same-version copy in the machine'sgem envhome is reported as a stale install. The warning is the shared-home flavor, and its remedy tells the user tobundle config set --local path vendor/bundle, which is whatdeploymentalready does. The flagged purl is then dropped from the same run's--vex. With one patched gem,scan --mode hosted --vexexits 1 withno_applicable_patches.Impact
scan --mode hosted --vexon any machine whose shared gem home holds an old copy of the patched gem.paththat isn't installed yet, soscan --mode hosted --vexfails withno_applicable_patcheson fresh checkouts #1001 described.Proof with real Bundler (Ruby 3.3.6)
The system copy is never used in any of these shapes:
deployment trueproj/vendor/bundle/ruby/3.3.0/gems/colorize-0.8.1BUNDLE_DEPLOYMENT=trueproj/vendor/bundle/ruby/3.3.0/gems/colorize-0.8.1simulate_version 5proj/.bundle/ruby/3.3.0/gems/colorize-0.8.1socket-patch repro (main
05fd82b)I used a throwaway test in
crates/socket-patch-cli/tests/e2e_redirect_gem_stale_install.rs. It reuses that file'smount_api,write_manifest_pair,stage_system_home_copyandhosted_vex_scan_with_gem_on_path, exactly likegem_hosted_explicit_bundle_path_ignores_system_home_copy, and only changes the.bundle/config/ env setting. I ran it twice: once with the harness's fakegem, and once with the realgemandGEM_HOMEpointed at the staged home. Both runs gave the same results:.bundle/configor env; nothing installed in the project yet)--vexattestsBUNDLE_PATH: "vendor/bundle"BUNDLE_DEPLOYMENT: "true"no_applicable_patches)BUNDLE_DEPLOYMENT=trueBUNDLE_SIMULATE_VERSION: "5"BUNDLE_DEFAULT_INSTALL_USES_PATH: "true"Warning emitted for the deployment case:
Expected vs actual
gem envhomes count only when Bundler uses system gems, "since with such apathbundle installfetches non-default gems into it and never reuses a system copy". Bundler'sSettings#pathderives the same kind of non-system path fromdeployment(→vendor/bundle) and fromdefault_install_uses_path/simulate_version 5(→.bundle) whenever no tier setspath/path.system/disable_shared_gems. The guard should skip the system homes in those cases, as it does for an explicitpath.path. Its "system gems" test is!default_root_has_stores && !bundler_sets_explicit_path(..), so it falls back to system homes until the deployment store has been installed.OS × version
05fd82b(the logic is OS- and version-independent)First bad version
This isn't a regression. The guard judged every
gem envhome before #1002 (v4.0.0 included). #1002 (f23fd82) narrowed that for an explicitpathonly.Suspect code
crates/socket-patch-core/src/crawlers/ruby_crawler.rs:655-675(bundler_install_homes:uses_system_gemsignoresdeployment,default_install_uses_pathandsimulate_version).crates/socket-patch-core/src/crawlers/ruby_crawler.rs:1204-1229(bundler_sets_explicit_pathreads onlypath/path.system/disable_shared_gems).discover_bundle_stores_impl(ruby_crawler.rs:~452) already knows about the.bundledefault (Gem crawler ignores Bundler's.bundledefault install path (default_install_uses_pathon 2.x,simulate_version 5on 4.x), so agentapplypatches the system copy andvexattestsnot_affectedwhile Bundler loads the unpatched.bundle/ruby/<abi>copy #967), but only probes it once it has been installed.bundler_install_homesthe single answer for standalonevextoo. With this gap, that fix would inherit the same falsenot_appliedfor deployment projects.Backlog review — 2026-10-08
Priority: P1 → P2. Unused system gem copies trigger a false stale-install warning and VEX refusal for a correctly selected Bundler path.