[agent] Filed by the scheduled architecture audit routine (ecosystems and formats). Register: register comment.
Kind: bug. Source: new finding, register E68 (same class as E24, the nine revert mechanisms).
Problem
Vendored gem decides whether the Gemfile wiring is "ours" in two different ways:
- Forward (
vendor, hot path): the Gemfile only has to contain the copy path: gemfile_text.contains(©_rel) (gem.rs#L361-L362).
- Revert (
revert_gemfile_record): the recorded line must match a Gemfile line exactly: lines.iter().position(|l| *l == written) (gem.rs#L2226-L2242).
The records are also reverted one by one, lock first (gem.rs#L1194-L1240). A drifted Gemfile record doesn't stop the lock records from being written. Poetry's legacy formats have the atomic variant revert_lock_fragment_splice_atomic (common.rs#L714-L723) for exactly this, and #822 fixed the same half-revert in uv.
Proof: I ran a throwaway test in vendor::gem::tests three times on 9c43dfc, using the existing fixture(GEMFILE_DIRECT, LOCK_DIRECT) and run_vendor:
- Vendor
rack 3.2.6. The Gemfile line becomes gem "rack", "3.2.6", path: ".socket/vendor/gem/<uuid>/rack-3.2.6".
- Append
# CVE fix, do not bump to that line.
- Re-run vendor:
success=true, no files patched, no new entry (in sync).
revert_gem(&entry): success=true, kept_artifact=true, with warnings vendor_lock_entry_drifted ("Gemfile no longer carries what vendor wrote for rack") and vendor_artifact_kept.
- The Gemfile still says
path: ".socket/vendor/gem/<uuid>/rack-3.2.6".
Gemfile.lock was restored to the registry: the PATH section is gone, rack (3.2.6) is back under GEM, and DEPENDENCIES says rack (~> 3.1).
- A second
revert_gem: drift-keep again, the same two warnings. It can never finish.
Symptoms
Impact
- A user who annotates their Gemfile line, or whose formatter rewrites it, ends up with a Gemfile that names a
path: source while the lock says rubygems.org. bundle install --frozen / --deployment refuse that pair, because the Gemfile and lock disagree.
- The revert reports
success, and the warning's remedy ("undo the drift … or re-vendor") doesn't help: re-vendor thinks it is in sync until the lock is half-reverted, and after that the second revert still drift-keeps.
- The size is small: one file plus tests.
Proposed change
- Make the Gemfile revert recognize our line by the same predicate the forward path uses: a
gem "<name>" declaration whose path: names this uuid's copy. Then restore original over that line, keeping any trailing comment the user added. Delete the exact-line position(|l| *l == written) match.
- Make the gem revert all-or-nothing across its coupled records (
gemfile_line, gemfile_lock_spec, gemfile_lock_checksum): plan every record first, and write nothing if any record drifted, as revert_lock_fragment_splice_atomic does.
Size and scope
Acceptance criteria
Dependencies
None. This is independent of the E24 tracking issue, which it informs.
Backlog review — 2026-10-08
Priority: P1 → P2. A trailing comment creates a partial Gem rollback and install failure. Concrete defect, but conditional and observable.
[agent] Filed by the scheduled architecture audit routine (ecosystems and formats). Register: register comment.
Kind: bug. Source: new finding, register E68 (same class as E24, the nine revert mechanisms).
Problem
Vendored gem decides whether the Gemfile wiring is "ours" in two different ways:
vendor, hot path): the Gemfile only has to contain the copy path:gemfile_text.contains(©_rel)(gem.rs#L361-L362).revert_gemfile_record): the recorded line must match a Gemfile line exactly:lines.iter().position(|l| *l == written)(gem.rs#L2226-L2242).The records are also reverted one by one, lock first (
gem.rs#L1194-L1240). A drifted Gemfile record doesn't stop the lock records from being written. Poetry's legacy formats have the atomic variantrevert_lock_fragment_splice_atomic(common.rs#L714-L723) for exactly this, and #822 fixed the same half-revert in uv.Proof: I ran a throwaway test in
vendor::gem::teststhree times on9c43dfc, using the existingfixture(GEMFILE_DIRECT, LOCK_DIRECT)andrun_vendor:rack 3.2.6. The Gemfile line becomesgem "rack", "3.2.6", path: ".socket/vendor/gem/<uuid>/rack-3.2.6".# CVE fix, do not bumpto that line.success=true, no files patched, no new entry (in sync).revert_gem(&entry):success=true,kept_artifact=true, with warningsvendor_lock_entry_drifted("Gemfile no longer carries what vendor wrote for rack") andvendor_artifact_kept.path: ".socket/vendor/gem/<uuid>/rack-3.2.6".Gemfile.lockwas restored to the registry: the PATH section is gone,rack (3.2.6)is back under GEM, and DEPENDENCIES saysrack (~> 3.1).revert_gem: drift-keep again, the same two warnings. It can never finish.Symptoms
# socket-patch vendor:comment:vendor --revertdrift-keeps forever (exit 0), and the suggested "re-vendor" remedy is a no-op or adds a duplicate line #977 is the same forward/revert recognizer split in requirements.txt (code-only match forward, whole-line match on revert). This issue is the gem instance, plus the half-revert, which Vendored requirements.txt can't be reverted once the user touches the# socket-patch vendor:comment:vendor --revertdrift-keeps forever (exit 0), and the suggested "re-vendor" remedy is a no-op or adds a duplicate line #977 doesn't have.Impact
path:source while the lock says rubygems.org.bundle install --frozen/--deploymentrefuse that pair, because the Gemfile and lock disagree.success, and the warning's remedy ("undo the drift … or re-vendor") doesn't help: re-vendor thinks it is in sync until the lock is half-reverted, and after that the second revert still drift-keeps.Proposed change
gem "<name>"declaration whosepath:names this uuid's copy. Then restoreoriginalover that line, keeping any trailing comment the user added. Delete the exact-lineposition(|l| *l == written)match.gemfile_line,gemfile_lock_spec,gemfile_lock_checksum): plan every record first, and write nothing if any record drifted, asrevert_lock_fragment_splice_atomicdoes.Size and scope
vendor/gem.rsonly, about 60–120 production lines plus tests.# socket-patch vendor:comment:vendor --revertdrift-keeps forever (exit 0), and the suggested "re-vendor" remedy is a no-op or adds a duplicate line #977).Acceptance criteria
path:was removed or points elsewhere) leaves both the Gemfile andGemfile.lockbyte-identical (no half-revert), and the artifact is kept.test_revert_round_trip_*,test_revert_converged_files_are_silent_and_still_removeand the legacy-ledger gem fixtures stay green.Dependencies
None. This is independent of the E24 tracking issue, which it informs.
Backlog review — 2026-10-08
Priority: P1 → P2. A trailing comment creates a partial Gem rollback and install failure. Concrete defect, but conditional and observable.