Third member of the harness-env-leak family (ai-flow#38, ai-flow#44, dev#89): any harness that runs under bundle exec and shells out to dev leaks RUBYOPT=-r.../bundler/setup + BUNDLE_GEMFILE into the child, and the interpreter activates the caller's bundle before bin/dev's first line runs — dev then can't load its own gems (LoadError: cannot load such file -- cli/ui, observed live in ai-flow#44).
dev vendors its own gems and picks its own Ruby; there is no caller whose bundler activation it ever wants. So instead of every caller patching every spawn site, bin/dev should defend itself: the sh shim unsets the bundler-activation keys (RUBYOPT, RUBYLIB, BUNDLE_*) before probing for / exec'ing Ruby. Callers' scrubs (ai-flow's HarnessEnv, dev#90's HARNESS_ENV_SCRUB) become defense-in-depth instead of load-bearing.
Scope note: GEM_HOME/GEM_PATH stay untouched — they are legitimate user config, and the one place they redirect resolution into an ephemeral cache is already guarded (dev#90).
Third member of the harness-env-leak family (ai-flow#38, ai-flow#44, dev#89): any harness that runs under
bundle execand shells out todevleaksRUBYOPT=-r.../bundler/setup+BUNDLE_GEMFILEinto the child, and the interpreter activates the caller's bundle beforebin/dev's first line runs — dev then can't load its own gems (LoadError: cannot load such file -- cli/ui, observed live in ai-flow#44).dev vendors its own gems and picks its own Ruby; there is no caller whose bundler activation it ever wants. So instead of every caller patching every spawn site,
bin/devshould defend itself: the sh shim unsets the bundler-activation keys (RUBYOPT,RUBYLIB,BUNDLE_*) before probing for / exec'ing Ruby. Callers' scrubs (ai-flow'sHarnessEnv, dev#90'sHARNESS_ENV_SCRUB) become defense-in-depth instead of load-bearing.Scope note:
GEM_HOME/GEM_PATHstay untouched — they are legitimate user config, and the one place they redirect resolution into an ephemeral cache is already guarded (dev#90).