Key Solargraph pin cache on fork revision; clear stale-cache @sg-ignore markers - #469
Merged
Conversation
…ct gap castwide/solargraph#1274's PR thread confirmed the generic<X>/generic<T> leak that prompted these 49 markers was a stale on-disk pin cache artifact, not a real regression: PinCache keys its cache dir only on the static Solargraph::VERSION string, so a bundle update that swaps the gem revision without clearing ~/.cache/solargraph reuses pins written under the old Pin::Callable schema. A fresh cache makes the symptom disappear with no code change needed. Removes all 49 tool-limitation:pr-1274-follow-on ignores this implies; local strong typecheck now reports 0 problems. Also tags one new marker surfaced by that same run: .git-hooks/pre_commit/punchlist.rb's Overcommit::Hook::Message.new(...) call, where Solargraph resolves the block-form Struct subclass's generated initializer against Struct.new's own class-definition signature instead (tool-limitation:struct-new-block-form) -- in the same neighborhood as the open castwide/solargraph#1268/#1269/#260 Struct-support gaps, but not yet filed as its own issue. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S3YDrArZm1e4DQ3MnFCSZk
Solargraph's PinCache.work_dir keys its on-disk cache directory only on the static Solargraph::VERSION string. This repo pins solargraph to a branch of the apiology/solargraph integration fork rather than a released version, so a `bundle update` that moves the branch to a new commit doesn't bump VERSION -- stale pins from the old commit get silently reused. That exact scenario produced a false-positive regression report against castwide/solargraph#1274 (generic<X>/generic<T> leaking into ordinary Hash#fetch calls) that turned out to be nothing but this stale cache. Sets SOLARGRAPH_FORCE_VERSION to include the Gemfile.lock-pinned git revision whenever solargraph is git-sourced, so any revision change busts the cache automatically. apiology/solargraph's own .envrc does something similar already, keyed off branch name; this keys off the exact revision instead, since a branch can move to a new commit without a corresponding version bump. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S3YDrArZm1e4DQ3MnFCSZk
.envrc (e071f39) forces SOLARGRAPH_FORCE_VERSION from the pinned git revision so Solargraph's PinCache busts on a revision bump. That same variable also feeds the apiology/solargraph fork's own gemspec (s.version = Solargraph::VERSION), so leaving Gemfile.lock's spec entry at the stale static "solargraph (0.60.3)" label made Bundler see a mismatch against the live-evaluated gemspec and fail with Bundler::GemNotFound on install/check. `bundle update solargraph --conservative` (run with the forced version active) re-resolves and records the matching label, "solargraph (0.0.1.dev.pre.<revision>)", so Bundler and the forced version agree going forward. `bundle check` and `bin/solargraph typecheck --level strong` are both clean after this change. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S3YDrArZm1e4DQ3MnFCSZk
…nned Bundler's git-source checkout for solargraph (<GEM_HOME>/bundler/gems/solargraph-<shortsha>/) lives under the shared, machine-wide GEM_HOME by default -- one checkout per revision, shared across every worktree/repo on this machine. Its on-disk gemspec is dynamic (s.version reads SOLARGRAPH_FORCE_VERSION), so any other process resolving the same revision with a different (or absent) value races against this worktree and can leave RubyGems' cached spec for that checkout answering with the wrong version. That race reproduced two distinct failures locally: a hard `Bundler::GemNotFound`/"can no longer be found in that source" error on plain `bundle install`/`bundle check`, and -- more subtly -- a silent partial `rbs collection update` result (69 gems -> 25) with no error at all, since rbs's Bundler-based gem enumeration degrades rather than raising when the shared spec is momentarily wrong. Setting BUNDLE_PATH to a worktree-local `vendor/bundle` (already gitignored and already a supported mode per the Makefile's `realclean` target) removes the sharing entirely -- there's nothing left to race against. Scoped to only fire when solargraph is git-pinned (same Gemfile.lock GIT-block check already used for SOLARGRAPH_FORCE_VERSION): a released solargraph has no such dynamic gemspec, so there's nothing to isolate and no need to pay a full per-worktree `bundle install` (measured ~1min for checkoff's ~150 gems). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S3YDrArZm1e4DQ3MnFCSZk
CI (and any other caller that skips .envrc) needs the same SOLARGRAPH_FORCE_VERSION .envrc computes locally, or `bundle install` fails: the apiology/solargraph fork's gemspec (s.version reads this env var) evaluates to the plain default with it unset, which doesn't match the forced label Gemfile.lock records for the pinned git revision. A prior attempt added a separate CircleCI step exporting into $BASH_ENV before fix.sh runs, but that didn't take effect in the "Initialize packages" step that actually calls `bundle install` -- reverted. Computing it directly inside fix.sh (guarded so an already-set value from .envrc wins) puts it in the exact process that needs it, regardless of caller. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S3YDrArZm1e4DQ3MnFCSZk
apiology
force-pushed
the
worktree-delegated-honking-duckling
branch
from
August 7, 2026 19:11
5b2c9e1 to
6ff1d15
Compare
The "rbenv" cache (vendor/bundle, Gemfile.lock.installed, .bundle) used a fallback key ladder ending in a bare "rbenv-" prefix. CircleCI's restore_cache treats a dash-terminated key as a prefix match, so that bare fallback matches *any* cache ever saved under "rbenv-v5-...", "-v4-", etc, regardless of Gemfile.lock/Gemfile/gemspec/Makefile/config.yml content. That let a stale vendor/bundle (with a bundler/gems/solargraph-* checkout resolved under a different or absent SOLARGRAPH_FORCE_VERSION) get restored ahead of fix.sh even running, reproducing the exact Bundler::GemNotFound failure this session's fix.sh/.envrc/Gemfile.lock changes were supposed to eliminate -- the same GEM_HOME race as the original local bug, just via CircleCI's cache instead of a shared GEM_HOME. Bumping v5 -> v6 and removing the bare fallback forces a real cache miss here, so this build actually exercises the new fix.sh logic instead of restoring pre-fix state. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S3YDrArZm1e4DQ3MnFCSZk
The previous commit fixed today's stale-cache restore by bumping the version prefix and dropping the ladder's loosest fallback tiers, but that made the cache bust on any gemspec/Gemfile/Makefile/config.yml change too, defeating the point of the fallback ladder. Gemfile.lock's checksum already changes whenever solargraph's pinned git revision does, but every fallback tier past the first one drops that checksum to tolerate unrelated Gemfile.lock churn (other gems' version bumps). That's what let a stale vendor/bundle -- with a bundler/gems/solargraph-* checkout resolved under a different or absent SOLARGRAPH_FORCE_VERSION -- get restored ahead of fix.sh even running. Isolate solargraph's pinned revision into its own file (/tmp/solargraph_revision, computed via the same Gemfile.lock GIT-block awk extraction .envrc/fix.sh already use) and include its checksum in every tier of the ladder, including the loosest. A solargraph revision change now always forces a fresh install regardless of what else changed, while unrelated file changes still get the existing graceful degradation. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S3YDrArZm1e4DQ3MnFCSZk
CI's last run got past bundle install for the first time this session (the cache-key fix worked) but bin/solargraph typecheck --level strong found 52 problems in 13 files that don't reproduce locally (0 problems). solargraph cached 217 gems in CI vs 218 locally, so something about the resolved environment differs between CI and local despite both showing the correct forced version and pinned revision at the shell level. Echoing SOLARGRAPH_FORCE_VERSION, BUNDLE_PATH, `solargraph --version`, `bundle show solargraph`, and the Gemfile.lock pinned revision right before typecheck runs, in the exact same job step, to compare CI's actual values against local's next to each other. Temporary -- revert once the mismatch is found. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S3YDrArZm1e4DQ3MnFCSZk
Debug output confirmed SOLARGRAPH_FORCE_VERSION and BUNDLE_PATH were both <unset> at the ci-solargraph step, resolving solargraph against the shared GEM_HOME path rather than the pinned/forced version -- explaining the 52 bogus typecheck problems in the last CI run despite bundle install succeeding earlier in the same job. CircleCI runs each `run:` step as its own fresh shell. fix.sh's `export` only lives inside the "Initialize packages" step's process, so `bundle install` there sees the correct forced version, but the later "Typecheck" step's `bundle exec`/`solargraph typecheck` runs in a new shell that never saw it, re-evaluates the pinned gem's dynamic gemspec with the var unset, and gets the plain default version instead. $BASH_ENV is sourced at the start of every step in a CircleCI job, so appending the export there (guarded on $BASH_ENV being set, i.e. only under CircleCI) makes every later step in the job see the same value. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S3YDrArZm1e4DQ3MnFCSZk
Their output found the actual bug (SOLARGRAPH_FORCE_VERSION unset by the time the Typecheck step ran, fixed by persisting it to $BASH_ENV in fix.sh) -- no longer needed now that CI is green. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S3YDrArZm1e4DQ3MnFCSZk
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.
Summary
.envrc: setsSOLARGRAPH_FORCE_VERSIONfrom the Gemfile.lock-pinnedapiology/solargraphgit revision, so Solargraph'sPinCache(keyed only on the staticSolargraph::VERSIONstring) busts automatically whenever the pinned fork revision changes -- previously abundle updatemoving the branch to a new commit could silently reuse stale pins.tool-limitation:pr-1274-follow-on@sg-ignoremarkers: castwide/solargraph#1274's thread confirmed thegeneric<X>/generic<T>leak they guarded against was exactly this stale-cache artifact, not a real regression.bin/solargraph typecheck --level strongnow reports 0 problems both under the old warm cache and a full rebuild under the new revision-keyed cache dir..git-hooks/pre_commit/punchlist.rb'sOvercommit::Hook::Message.new(...)call, where Solargraph resolves a block-formStructsubclass's generated initializer againstStruct.new's own class-definition signature instead (tool-limitation:struct-new-block-form-- same neighborhood as open castwide/solargraph#1268/#1269/#260).Test plan
bin/solargraph typecheck --level strong-- 0 problems, both before and after a full cache rebuild under the newSOLARGRAPH_FORCE_VERSION-keyed directorybundle exec rake test-- 308 tests, 0 failuresbundle exec rubocop .git-hooks/pre_commit/punchlist.rb-- no offenses