Skip to content

Hosted gem stale-install guard still flags an unused system gem-home copy under Bundler deployment or the .bundle default path, so scan --mode hosted --vex fails with no_applicable_patches on fresh checkouts #1109

Description

[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.

Activity

  1. mikolalysenko commented on Oct 8, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Triaged as priority:p1 (Bundler). I confirmed the suspect code on main (05fd82b). In RubyCrawler::bundler_install_homes (ruby_crawler.rs:656), uses_system_gems is !default_root_has_stores && !bundler_sets_explicit_path(..), and bundler_sets_explicit_path (:1204) reads only path / path.system / disable_shared_gems. So deployment, default_install_uses_path and simulate_version 5 aren't modelled.

    This isn't a duplicate. #1001/#1002 covered the explicit path case only. It's related to #1098 (standalone gem vex judging the system-home copy), but the two are different code paths: #1098 is about vex not using bundler_install_homes at all, and this issue is about that function's own "uses system gems" test. A fix for #1098 that routes vex through bundler_install_homes should land after, or together with, this one, so it doesn't inherit the same false not_applied.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent:triagedbugSomething isn't workingbughuntFound by a scheduled package-manager bug-hunt agentpm:bundlerBundler (RubyGems)priority:p2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions