Skip to content

Fix vex alias tests broken by store-copy merge - #851

Closed
Mikola Lysenko (mikolalysenko) wants to merge 1 commit into
mainfrom
agent/ci-vex-alias-store-tests
Closed

Mikola Lysenko (mikolalysenko) wants to merge 1 commit into
mainfrom
agent/ci-vex-alias-store-tests

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

LLM Description written by Claude Code:claude-opus-5-5

Main is red since 4646693 (#605): socket-patch-cli --lib fails two commands::vex_consumed tests, which breaks test/coverage on every open PR (first seen on #849).

Root cause

This is a semantic merge conflict between #738 and #605. Neither PR is wrong alone:

  • Fix agent mode skipping npm-aliased copies (#356) #738 added hosted_reuses_expanded_npm_copies_and_merges_alias_variants and hosted_expands_alias_only_copies. Their premise is that the name-keyed resolver (find_manifest_package_copies_reusing) never returns npm-aliased copies.
  • Fix npm store copies missed by agent apply and vex (#601, #603) #605 taught that resolver to probe bundled store trees. It now finds node_modules/lp, host/node_modules/lp and the nested host's store peers by itself. So assert_eq!(installed_again, installed) and assert!(installed.is_empty()) fail. The final vex copy set is still correct.

Fix (tests only)

  • Alias expansion is still covered: each test now feeds the alias-free set (the pre-alias installed, or an empty map) to hosted_consumed_copies and keeps every existing assertion on the expansion calls and the resulting copies.
  • Each test also runs the real pipeline on the resolver's own set and asserts it reaches exactly the same copies. The first test's no-duplicate check stays.

No production code changes, and no assertion was removed without an equivalent or stronger replacement.

Evidence

  • On origin/main 4646693: cargo test -p socket-patch-cli --all-features --lib -- commands::vex_consumed gives 8 passed, 2 failed (the panics above).
  • On this branch: 10 passed, 0 failed.

Blocks #849 (and other PRs that merge main).

🤖 Generated with Claude Code

https://claude.ai/code/session_01MxWLuzeHjHPXgKnhQWVncJ


Generated by Claude Code


Note

Low Risk
Test-only updates to match resolver semantics; no runtime behavior changes.

Overview
Repairs two failing commands::vex_consumed hosted npm tests after #605 made find_manifest_package_copies_reusing discover alias installs and nested store peers on its own.

Instead of asserting the resolver still returns an alias-free or empty installed set, each test still drives hosted_consumed_copies with the old inputs (pre-alias installed, or an empty map) so alias expansion and store-variant merging stay covered. New assertions run the same pipeline on the resolver’s full result and require it to match the expected copy set—including the nested-host case’s duplicate-path check where it already existed.

Tests only; no production changes.

Reviewed by Cursor Bugbot for commit 40dac07. Configure here.


Generated by Claude Code

#605 taught the name-keyed npm resolver to probe bundled store
trees, so it now finds aliased copies (node_modules/lp) and a nested
host's store peers itself. Two vex_consumed tests from #738 assumed
that set never held aliases, so main's CI went red after both merged.

The tests now feed the alias-free set explicitly to keep covering
alias expansion, and also check the resolver's own set reaches the
same copies with no duplicates. No production code changes.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 40dac07. Configure here.

Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 5, 2026
main is red since 4646693 (#605): two commands::vex_consumed tests
assumed the name-keyed resolver never returns npm-aliased copies, which
#605 changed. Same tests-only change as #851; it no-ops once main
carries it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Ao6g9qAnawPfNxv11f3wM
Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 5, 2026
main @ 4646693 (#605) broke two vex_consumed alias tests; the
coverage job fails on every PR. Same change as #851, so it no-ops
once that lands.

Assisted-by: Claude Code:claude-opus-5-5
Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 5, 2026
main is red since 4646693 (#605): two commands::vex_consumed tests
assumed the name-keyed resolver never returns npm-aliased copies.
Same test-only change as #851; it no-ops once main carries it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NhRxWtzEYpLyrByBegiiRy
Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 5, 2026
main's #605 made the name-keyed npm resolver reach alias and peer
copies itself, which broke two vex_consumed tests that assumed an
alias-blind resolver. Same change as #851, ported so this PR's CI
runs green against the current base; it no-ops once #851 lands.

Refs #831

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FMZyKmgYNAridSR5eqv999
Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 5, 2026
Main has been red since 4646693 (#605): two
commands::vex_consumed tests built for #738 assume the name-keyed
resolver never returns npm-aliased copies, which #605 changed. This is
the same test-only change as #851 and becomes a no-op once that lands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VSXCFoPbraq7rNKJpXEP2n
Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 5, 2026
main is red since #605: two commands::vex_consumed tests assumed the
name-keyed resolver never returns npm-aliased copies, but #605 taught it
to probe bundled store trees. This ports the tests-only fix from #851 so
this PR's CI goes green; it no-ops once main carries #851.

Assisted-by: Claude Code:claude-opus-5-5
Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 5, 2026
Main is red since #605: two vex_consumed tests assumed the copy
resolver never returns npm-aliased copies, which #605 changed. This
ports the test-only fix from #851 so this PR's CI can go green; it
becomes a no-op once #851 lands.

Assisted-by: Claude Code:claude-opus-5-5
Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 5, 2026
Main has been red since #605: two commands::vex_consumed tests assume
the name-keyed resolver never returns npm-aliased copies, but #605
taught it to probe bundled store trees. Port #851's test-only fix so
this PR's CI runs on a green base. It becomes a no-op once #851 lands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EgBZwmqgXLZaRGyfDFoWwp
Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 5, 2026
Since #605 landed, main fails two vex_consumed alias tests because the
name-keyed resolver now finds alias and bundled store copies itself.
This is #851's test-only fix, ported so this PR's CI can go green; it
becomes a no-op once #851 merges.

Assisted-by: Claude Code:claude-opus-5-5
Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 5, 2026
main has been red since #605 taught the npm copy resolver to probe
bundled store trees: two vex_consumed alias tests (#738) still assumed
the resolver never returns npm-aliased copies, so the CLI lib tests
fail on every PR's merge ref. This ports #851's tests-only fix so the
PR's CI reflects its own change; it no-ops once #851 lands on main.

Assisted-by: Claude Code:claude-opus-5-5
Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 5, 2026
main fails commands::vex_consumed::tests::hosted_expands_alias_only_copies
and hosted_reuses_expanded_npm_copies_and_merges_alias_variants since
#605 landed alongside #738; #851 fixes the tests. Carry the same change
so this PR's CI (coverage, test-release) is green; it no-ops once main
has it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DPHxnE5P1rkfCpHFFCwzFR
Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 5, 2026
main went red when #605 taught the name-keyed resolver to find pnpm
store copies, which the #738 alias tests assumed it missed. Same
change as #851; it no-ops once main carries it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NEpjVvY7X41jPuVuoiCLVz
@mikolalysenko Mikola Lysenko (mikolalysenko) added the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 5, 2026
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Burn-down agent: labeled Ready for review.


Generated by Claude Code

Mikola Lysenko (mikolalysenko) added a commit that referenced this pull request Oct 5, 2026
* Start fix for #806, #821

Assisted-by: Claude Code:claude-opus-5-5

* Unwind uv vendoring after a relock

After vendoring, an ordinary uv relock (`uv add --dev x`, `uv add y`)
re-serializes the lock arrays that hold our element: the dev group's
requires-dev line and `[manifest] overrides`. Revert matched those
arrays by their exact recorded text, so it saw drift and kept
uv.lock wired, but still reverted pyproject.toml. The pair then
failed `uv sync --locked` while `vendor --revert` reported success.

Revert now finds our unchanged element inside the live array under
the same key and restores or removes just that element, rendering
the array the way uv writes it. A pair gate also writes neither
file when any record is genuinely drift-kept, so pyproject.toml and
uv.lock always stay consistent.

Fixes #806, #821.

Assisted-by: Claude Code:claude-opus-5-5

* Test uv revert after a relock with real uv

Vendor six, run the uv command that re-serializes the lock array
around our element (`uv add --dev zipp` for a dev group, `uv add
idna` beside user overrides), then revert. Both files must be
unwired with no drift warning, and `uv lock --check` must pass.

Refs #806, #821.

Assisted-by: Claude Code:claude-opus-5-5

* Document uv revert after a relock

Refs #806, #821.

Assisted-by: Claude Code:claude-opus-5-5

* Anchor uv array reverts on their key

A [manifest] overrides record holds the bare array, and the old
convergence shortcut searched the whole lock for it. When the root
requires-dist happened to match the user's overrides array, revert
treated our element as already gone, left it in uv.lock and deleted
the artifact it points at. Every whole-array record now reverts
through its own key: an untouched array is restored verbatim,
otherwise just our element is.

Refs #806.

Assisted-by: Claude Code:claude-opus-5-5

* Fail closed when a uv lock array can't be read

Revert treated any miss locating a whole-array record as convergence,
including a key spelled differently or an unbalanced array. A lock
that still routed through the vendored wheel could then lose the
wheel. Only a key or section that is provably absent now counts as
converged. Anything unreadable is drift, which keeps both files and
the artifact.

Refs #806, #821.

Assisted-by: Claude Code:claude-opus-5-5

* Start fix for #840

Assisted-by: Claude Code:claude-opus-5-5

* Test uv revert after a declaration edit

A vendored uv revert writes back the lock specifier it recorded when
vendoring. If the user changed the package's requirement in
pyproject.toml in the meantime, the lock no longer matches and
`uv sync --locked` fails. These tests pin the expected behaviour
for requires-dist, requires-dev groups and [manifest] constraints.

Refs #840

Assisted-by: Claude Code:claude-opus-5-5

* Re-derive uv specifiers on vendored revert

When six is vendored, uv.lock records it as a path source with no
version specifier. If the user then changes six's requirement in
pyproject.toml (uv add "six>=1.16"), the lock stays byte-identical,
and vendor --revert, remove and rollback wrote back the specifier
recorded at vendoring time. The revert reported success, but
`uv sync --locked` then failed.

The revert now writes the specifier pyproject.toml declares now, using
the same derivation the hosted unwind uses. This covers requires-dist
(each extra separately), requires-dev groups and [manifest]
constraints. An unchanged declaration still restores byte-for-byte.
When uv's spelling can't be derived, such as a multi-clause range whose
clause order varies between uv releases, the revert keeps both files
and warns vendor_lock_entry_drifted instead of breaking the lock.

Fixes #840

Assisted-by: Claude Code:claude-opus-5-5

* Document uv revert after a declaration edit

Refs #840

Assisted-by: Claude Code:claude-opus-5-5

* Adapt uv specifier re-derivation to main

#625 on main changed the uv declaration reader to report each
optional-dependencies member's extra. The merge of main into this branch
no longer compiled. Use that reader for requires-dist instead of the
local extras walk. Dev groups now also pick up main's group-name
normalization and include-group expansion.

Refs #840

Assisted-by: Claude Code:claude-opus-5-5

* Pick the declaration a uv lock entry mirrors

When a package is declared twice, for example under two environment
markers, or directly and through an include-group, the revert kept the
recorded specifier whenever any one declaration still matched it. Edit
just one of them and the stale pin came back, with the same broken
`uv sync --locked` as #840.

The revert now picks the declaration by the entry's own marker, as the
hosted unwind does. Declarations that still disagree after that are
treated as drift, and both files are kept.

Refs #840

Assisted-by: Claude Code:claude-opus-5-5

* Tighten uv revert specifier re-derivation

Two cases Bugbot found on the vendored uv revert:

- A same-name declaration that isn't a plain version range, such as an
  extra pinned with ===, stopped every entry from following its edited
  declaration. Now only the entry whose own declaration is unreadable
  keeps its recorded spelling.
- After a bound was dropped, the restored { name = "six" } element
  also matched another dependency entry in uv.lock, so a drifted wiring
  could pass as already reverted. The check now looks only in the root
  unit's requires-dist array.

Refs #840

Assisted-by: Claude Code:claude-opus-5-5

* Format the uv revert re-derivation changes

Assisted-by: Claude Code:claude-opus-5-5

* Port #851: fix vex alias tests broken on main

main has been red since #605 (4646693). Two vex_consumed tests from
#738 assumed the name-keyed copy resolver never returns npm-aliased
copies, and #605 taught it to. This ports #851's tests-only fix
unchanged so this PR's coverage and macOS test jobs can go green; it
no-ops once #851 lands on main.

Assisted-by: Claude Code:claude-opus-5-5

---------

Co-authored-by: Claude <noreply@anthropic.com>
Mikola Lysenko (mikolalysenko) added a commit that referenced this pull request Oct 5, 2026
…850)

* Start refactor for #823

Assisted-by: Claude Code:claude-opus-5-5

* Spawn CLI test children via one hermetic builder

Test children inherited ambient SOCKET_* settings through 15 private
scrub_socket_env copies and 10 unscrubbed spawners, so a developer's
shell (SOCKET_DRY_RUN, SOCKET_OFFLINE, ...) could silently change what
a suite exercises. common/hermetic.rs now holds the one builder:
hermetic::command seeds and scrubs SOCKET_* and forces SOCKET_NO_CONFIG
and SOCKET_NO_UPDATE_CHECK; scrub_extra adds the opt-in yarn, pnpm and
venv sweeps. run_bin_with_env is built on it.

This moves the 8 copies and 8 unscrubbed spawners that no open fix PR
touches onto the builder and deletes those copies. spawn_env_hygiene
tests the builder's contract and ratchets the remaining copies and
bare binary spawns. Test-only; no production change.

Refs #823

Assisted-by: Claude Code:claude-opus-5-5

* Drop imports the hermetic move left unused

Assisted-by: Claude Code:claude-opus-5-5

* Spawn cli_dry_run_paths through the hermetic builder

Ambient SOCKET_DRY_RUN failed the real-apply leg of
apply_dry_run_with_real_patch_verifies_without_mutating; the cli target
now gives the same result with or without it.

Refs #823

Assisted-by: Claude Code:claude-opus-5-5

* Port #851 vex alias test fix from main breakage

main @ 4646693 (#605) broke two vex_consumed alias tests; the
coverage job fails on every PR. Same change as #851, so it no-ops
once that lands.

Assisted-by: Claude Code:claude-opus-5-5

---------

Co-authored-by: Claude <noreply@anthropic.com>
Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 5, 2026
Main is red since #605 (4646693): two commands::vex_consumed tests
assume the name-keyed resolver never returns npm-aliased copies, and
#605 taught it to find them. This fails socket-patch-cli --lib in
coverage and test on every PR. Port #851's test-only fix so this PR
can go green; it no-ops once #851 lands.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N8YeCUhdg2tKdqyyY7z3sV
Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 5, 2026
main has been red since #605 taught the name-keyed resolver to return
npm-aliased copies, which broke two vex_consumed tests added by #738.
Port #851's test update so this PR's CI goes green; it no-ops once
#851 lands on main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016uQyhodCtdJGrKaD7AAV1n
Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 5, 2026
The fix commit b3996a6 also reformatted 123 files it does not otherwise
touch (the output of cargo fmt --all on a tree main has not formatted).
Every one of those files is byte-identical to rustfmt run over main's
version, so this restores them to main. The PR now only touches the vlt
lock, redirect and heal code plus the ported #851 test fix, which keeps
the review small and stops the churn from conflicting with every other
open PR.

Co-Authored-By: Claude <noreply@anthropic.com>
Mikola Lysenko (mikolalysenko) pushed a commit that referenced this pull request Oct 5, 2026
main is red: since the store-copy change (#605) the npm resolver
already returns alias and nested-store copies, so two vex_consumed
tests that assumed an alias-free set fail on main and on this
branch. Same change as #851; it no-ops once main carries it.

Refs #335

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Burn-down agent: closing this one as already on main. #849 carried a cherry-pick of this commit and was merged as 1c8267d. crates/socket-patch-cli/src/commands/vex_consumed.rs on main (99f61d2) is now byte-identical to this branch's head 40dac07 (git diff 40dac07 origin/main -- …vex_consumed.rs is empty), so merging this PR would change nothing. Main's vex_consumed alias tests are fixed. Branch left in place.


Generated by Claude Code

@mikolalysenko Mikola Lysenko (mikolalysenko) removed the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 5, 2026
Mikola Lysenko (mikolalysenko) added a commit that referenced this pull request Oct 5, 2026
* Start fix for #796

Assisted-by: Claude Code:claude-opus-5-5

* Find Bundler 4 standalone installs in ./bundle

`bundle install --standalone` puts gems in ./bundle and the app loads
them through bundle/bundler/setup.rb. Bundler 2 also recorded the path
in .bundle/config, but Bundler 4 writes no config at all, so the gem
crawler never looked in ./bundle. Agent apply then patched an ambient
copy of the same gem (or said it was not installed), VEX attested
not_affected while the app ran the unpatched copy, and the hosted
stale-install warning stayed silent.

Probe ./bundle as an install root when bundle/bundler/setup.rb is
present, in the slot Bundler 2's recorded path used to take. Fixes #796.

Assisted-by: Claude Code:claude-opus-5-5

* Document the standalone bundle install root

List the Bundler 4 standalone ./bundle tree in the CLI contract's gem
install-root order, so the documented roots match what the crawler
probes.

Assisted-by: Claude Code:claude-opus-5-5

* Port #851: fix vex alias tests after #605

main is red since #605: two commands::vex_consumed tests assumed the
name-keyed resolver never returns npm-aliased copies, but #605 taught it
to probe bundled store trees. This ports the tests-only fix from #851 so
this PR's CI goes green; it no-ops once main carries #851.

Assisted-by: Claude Code:claude-opus-5-5

---------

Co-authored-by: Claude <noreply@anthropic.com>
Mikola Lysenko (mikolalysenko) added a commit that referenced this pull request Oct 5, 2026
* Start fix for #736

Assisted-by: Claude Code:claude-opus-5-5

* Read only the gem lock Bundler actually loads

A gems.rb project's gems.locked was invisible to the lock inventory,
ledger recovery read only Gemfile.lock, and VEX discovery read both
locks. A leftover redirected Gemfile.lock beside gems.rb + gems.locked
therefore made vex attest not_affected while bundle install installed
the unpatched gem from gems.locked.

Add one resolver for the lock Bundler loads (honouring BUNDLE_GEMFILE
and the app config) and route the inventory, gem_remotes, VEX
discovery and the hosted engine through it. VEX still reads the
ignored twin, but any Socket wiring there is diagnosed as
unattributable instead of attested.

Fixes #736

Assisted-by: Claude Code:claude-opus-5-5

* Test hosted engine on a gems.rb project

Assisted-by: Claude Code:claude-opus-5-5

* Avoid a single-element loop in the polyglot test

Assisted-by: Claude Code:claude-opus-5-5

* Note the gem lock reader fix in the changelog

Assisted-by: Claude Code:claude-opus-5-5

* Drop CHANGELOG entry from this PR

Release notes are written when a release is cut, from the merged PR
log and the code, so PRs no longer edit CHANGELOG.md.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Port #851: fix vex alias tests broken by store-copy merge

main is red since 4646693 (#605): two commands::vex_consumed tests
assumed the name-keyed resolver never returns npm-aliased copies, which
#605 changed. Same tests-only change as #851; it no-ops once main
carries it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012Ao6g9qAnawPfNxv11f3wM

---------

Co-authored-by: Claude <noreply@anthropic.com>
Mikola Lysenko (mikolalysenko) added a commit that referenced this pull request Oct 5, 2026
* Start fix for #632

Assisted-by: Claude Code:claude-opus-5-5

* Pin yarn catalog deps in hosted mode

A dependency declared "catalog:" in package.json was never patched
by scan --mode hosted: the resolutions entry was keyed by the lock's
expanded npm: range, but yarn matches resolutions before it expands
the catalog. The scan reported success, then yarn install --immutable
failed (YN0028) and a plain yarn install kept the unpatched release.

Also route name@catalog: / name@catalog:<named> for every
.yarnrc.yml catalog that maps the package to a pinned range. A re-run
adds the selector to a pin written by an earlier release.

Fixes #632

Assisted-by: Claude Code:claude-opus-5-5

* Test yarn catalog pins end to end

Add a real-yarn check that a fresh checkout of a hosted-pinned
catalog dependency installs the patched bytes under --immutable,
an in-process scan + rollback round trip, and document catalog
pins in the yarn berry hosted notes.

Assisted-by: Claude Code:claude-opus-5-5

* Drop unrelated rustfmt churn

cargo fmt --all also reformatted 127 files this fix doesn't touch
(main isn't rustfmt-clean). Restore them and the untouched hunks of
the edited files to main, so the PR only carries the catalog fix,
its tests and the docs note.

Assisted-by: Claude Code:claude-opus-5-5

* Keep unquoted yarn catalog ranges as their source text

berry_catalog_selectors parsed .yarnrc.yml catalogs into serde_json
Values, so an unquoted range like `1.10` became the number 1.1 and
never matched the lock's `npm:1.10`: the `catalog:` selector was
dropped while the pin was still confirmed. Yarn reads .yarnrc.yml with
the failsafe schema, so deserialize the catalog tables as string
tables instead, which keeps each scalar's source text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DPHxnE5P1rkfCpHFFCwzFR

* Port #851: fix vex alias tests broken by store-copy merge

main fails commands::vex_consumed::tests::hosted_expands_alias_only_copies
and hosted_reuses_expanded_npm_copies_and_merges_alias_variants since
#605 landed alongside #738; #851 fixes the tests. Carry the same change
so this PR's CI (coverage, test-release) is green; it no-ops once main
has it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DPHxnE5P1rkfCpHFFCwzFR

---------

Co-authored-by: Claude <noreply@anthropic.com>
Mikola Lysenko (mikolalysenko) added a commit that referenced this pull request Oct 5, 2026
* Start fix for #627

Assisted-by: Claude Code:claude-opus-5-5

* Refuse vendoring over a symlinked lockfile

Vendored mode renamed its rewritten lockfile (or package.json,
pnpm-workspace.yaml, nuget.config) over a symbolic link, turning a
shared lock into a detached copy: the link's target, the lock other
checkouts install from, stayed unpatched, and revert never restored
the link. Hosted mode already refused this.

The vendored group commit now refuses before writing anything when a
file it would change is a symlink or sits under a symlinked
directory. The run exits 1 with the same
redirect_symlinked_file_unsupported error hosted mode uses, and leaves
the link, its target and the vendor ledger untouched. A --dry-run
flags each symlinked wiring file with a
vendor_would_refuse_symlinked_file advisory.

Fixes #627

Assisted-by: Claude Code:claude-opus-5-5

* Gate only symlinked files, as hosted does

Writing into a symlinked directory goes through the link rather than
replacing it, so refusing it would newly break projects that link a
whole directory. Check the changed file itself, which matches the
hosted guard. Also add a real-yarn e2e for a symlinked yarn.lock.

Assisted-by: Claude Code:claude-opus-5-5

* Format the symlinked yarn.lock e2e

Assisted-by: Claude Code:claude-opus-5-5

* Warn about symlinked files in scan/get dry runs

scan and get --mode vendored --dry-run stop at the ledger preview and
never reach the vendor loop, so they gave no hint that the real run
would refuse a symlinked lockfile. The preview's would_vendor and
would_revendor rows now carry the same symlink warning that
vendor --dry-run emits, and human output prints it.

Assisted-by: Claude Code:claude-opus-5-5

* Narrow dry-run symlink warnings to real writes

The vendored dry run warned about symlinked files a vendored run only
reads (.yarnrc.yml, vlt.json, node_modules/.modules.yaml), which the
real run never writes and so never refuses. It also warned for
packages already in sync, whose re-run writes nothing. Both produced
false predictions of the symlink refusal.

The warning now covers only files a vendored run can rewrite, and
skips packages the dry run previews as already vendored.

Assisted-by: Claude Code:claude-opus-5-5

* Warn about symlinked pom.xml and hatch.toml too

The narrowed dry-run warning dropped files vendored Maven and Hatch
really rewrite: the root pom.xml, .mvn/maven.config and hatch.toml.
A symlinked root pom.xml was still refused by the real run with no
dry-run hint. Add them to the list of vendored write targets.

Assisted-by: Claude Code:claude-opus-5-5

* Port #851 fix for vex alias tests broken on main

main has been red since #605: two vex_consumed tests still assumed
the name-keyed resolver was alias-blind, so the CLI lib tests fail on
every branch built on main. This carries the same test-only change as
#851 and becomes a no-op once #851 lands.

Assisted-by: Claude Code:claude-opus-5-5

---------

Co-authored-by: Claude <noreply@anthropic.com>
Mikola Lysenko (mikolalysenko) added a commit that referenced this pull request Oct 5, 2026
* Start fix for #804

Assisted-by: Claude Code:claude-opus-5-5

* Fix rollback of pip-written pylock.toml

`pip lock` writes PEP 751's array-of-tables spelling
(`[[packages.wheels]]` with a `[packages.wheels.hashes]` sub-table),
but the hosted upstream restore only read inline `wheels = [{ ... }]`
arrays. Every pip sibling looked artifact-free, so `rollback`, `remove`
and the hosted -> vendored takeover always refused a pip lock with "no
sibling registry package shows ...", leaving users with a hosted patch
they could not undo.

The restore now reads artifacts in either spelling, writes the entry
back in the siblings' spelling, and, since pip records only the one
artifact it selected, restores only the release's wheel (or its sdist
when it has no wheel), refusing a release with several wheels. The
refusal no longer blames "this uv release" for a pip-written lock.

Fixes #804

Assisted-by: Claude Code:claude-opus-5-5

* Port vex alias test fix from #851

main has been red since #605 taught the npm copy resolver to probe
bundled store trees: two vex_consumed alias tests (#738) still assumed
the resolver never returns npm-aliased copies, so the CLI lib tests
fail on every PR's merge ref. This ports #851's tests-only fix so the
PR's CI reflects its own change; it no-ops once #851 lands on main.

Assisted-by: Claude Code:claude-opus-5-5

---------

Co-authored-by: Claude <noreply@anthropic.com>
Mikola Lysenko (mikolalysenko) added a commit that referenced this pull request Oct 5, 2026
* Start fix for #372

Assisted-by: Claude Code:claude-opus-5-5

* Accept vlt 1.3 brotli lock nodes

vlt 1.3 marks a lock node that fetches the registry's Brotli
(.tar.br) tarball with a new flag bit, 4, in slot [0]. socket-patch
only accepted flags 0-3, so hosted mode refused such a lock as "not
canonical" and exited 0 with nothing redirected, and vendored mode
failed with a misleading lockfile-version error.

Accept flags 0-7. When a pin or vendored wiring points a node at a
.tgz or local directory, clear the brotli bit as vlt would save it;
reverts put the recorded bit back with the original slots. The vlt
heal now reinstalls brotli prod and dev nodes like any other.

Fixes #372

Assisted-by: Claude Code:claude-opus-5-5

* Port #851: fix vex alias tests broken by store-copy merge

main is red since 4646693 (#605): two commands::vex_consumed tests
assumed the name-keyed resolver never returns npm-aliased copies.
Same test-only change as #851; it no-ops once main carries it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NhRxWtzEYpLyrByBegiiRy

* Drop unrelated cargo fmt --all churn from the vlt brotli fix

The fix commit b3996a6 also reformatted 123 files it does not otherwise
touch (the output of cargo fmt --all on a tree main has not formatted).
Every one of those files is byte-identical to rustfmt run over main's
version, so this restores them to main. The PR now only touches the vlt
lock, redirect and heal code plus the ported #851 test fix, which keeps
the review small and stops the churn from conflicting with every other
open PR.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Mikola Lysenko (mikolalysenko) added a commit that referenced this pull request Oct 5, 2026
* Start fix for #432

Assisted-by: Claude Code:claude-opus-5-5

* Pin npm aliases in the npm 6 lock mirror

A lockfileVersion 2 package-lock.json keeps a legacy `dependencies`
mirror for npm 6, which spells an alias install as
`"lp": {"version": "npm:left-pad@1.3.0"}`. Hosted scans matched mirror
nodes on a plain version only, so the alias node silently stayed on
the registry, and a lockfileVersion 1 alias lock pinned nothing at all
("no package-lock.json entry"). Rollback could not restore such a node
either.

Every reader of the legacy tree now decodes the alias through one
helper. Hosted scans rewire the alias node with the rest, and rollback
restores it. npm 6 fetches an aliased dependency from the registry
whatever `resolved` says, so under npm 6 the pinned lock fails closed
(EINTEGRITY) instead of installing unpatched bytes, and the run warns
`redirect_npm_legacy_alias_client`.

Refs #432

Assisted-by: Claude Code:claude-opus-5-5

* Vendor npm aliases in the npm 6 lock mirror

Vendoring skipped the v2 mirror node of an npm alias with
`vendor_legacy_alias_skipped`, so npm 6 installed the unpatched
registry tarball through it. npm 6 does install an alias node from a
`file:` resolved (checked against npm 6.14.18), so the node is now
rewired like every other mirror node and revert restores it.

Refs #432

Assisted-by: Claude Code:claude-opus-5-5

* Withhold VEX when the npm 6 mirror is unpatched

Lockfile-only VEX read a v2 lock's `packages` half only, so it attested
`not_affected` for a package whose legacy mirror (what npm 6 installs
from) still resolved to the registry, as locks written before this fix
do for npm aliases. Such a ref is now diagnosed as unattributable and
not attested. A lockfileVersion 1 alias node is read as an install of
its target package.

Fixes #432

Assisted-by: Claude Code:claude-opus-5-5

* Let a stale npm 6 mirror contest the sibling lock

When npm-shrinkwrap.json's legacy mirror still resolved a package from
the registry, VEX dropped the shrinkwrap's own ref but still attested
the same package from package-lock.json, although npm 6 installs from
the shrinkwrap. A mirror node off Socket now counts as resolving the
package elsewhere, so the sibling lock's ref is contested too.

Refs #432

Assisted-by: Claude Code:claude-opus-5-5

* Port vex alias test fix from #851

Main has been red since #605: two commands::vex_consumed tests assume
the name-keyed resolver never returns npm-aliased copies, but #605
taught it to probe bundled store trees. Port #851's test-only fix so
this PR's CI runs on a green base. It becomes a no-op once #851 lands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EgBZwmqgXLZaRGyfDFoWwp

---------

Co-authored-by: Claude <noreply@anthropic.com>
Mikola Lysenko (mikolalysenko) added a commit that referenced this pull request Oct 5, 2026
* Retry PDM backtest cases on transport errors

The PDM matrix runs against production PyPI and the public patch API.
Over the last 7 days 25 pdm-compatibility runs failed on one random
cell each, on unrelated PRs. The version, OS, shape, mode and check
differed every time (rescanIdempotent, appliedExactlyOne,
rescanAfterRelockApplies, ...). Each check judges a CLI scan, install
or rollback. `Run` retries a command once, and only on a non-zero
exit. The CLI usually reports an exhausted patch API fetch in its
JSON while exiting zero, so the cell just fails a later check.

Port backtest-poetry.py's case-level retry (#596). A case is re-run
from a fresh directory, at most three attempts, only when every
failed check recorded transport evidence from the operation it
judged. Evidence is a failed command's request error, PyPI give-up,
patch API 5xx or exhausted 429, or the same in the CLI's JSON error
records. Functional failures are never retried, even when a later
step raises a transport error. Failed attempts' logs go under
attempts/ and are uploaded. A failing case now prints its failed
checks' notes, since the job log alone never said why.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N8YeCUhdg2tKdqyyY7z3sV

* Judge PDM rollback and VEX checks by their run

Bugbot: the final hosted/vendored rollback checks, the unverifiable-
write rollback, the refused-lock VEX and the reverted-lock VEX runs
named no operation, so a transport failure there never made the case
retryable. installedBytesPatched fails together with a blipped pdm
sync and blocked the retry the same way.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N8YeCUhdg2tKdqyyY7z3sV

* Port #851: fix vex alias tests broken on main

Main is red since #605 (4646693): two commands::vex_consumed tests
assume the name-keyed resolver never returns npm-aliased copies, and
#605 taught it to find them. This fails socket-patch-cli --lib in
coverage and test on every PR. Port #851's test-only fix so this PR
can go green; it no-ops once #851 lands.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N8YeCUhdg2tKdqyyY7z3sV

---------

Co-authored-by: Claude <noreply@anthropic.com>
Mikola Lysenko (mikolalysenko) added a commit that referenced this pull request Oct 5, 2026
* Start fix for #709

Assisted-by: Claude Code:claude-opus-5-5

* Verify gems in out-of-tree bundle config paths

A .bundle/config "path" outside the project (bundle config set
--local path /opt/bundle) is refused as an install root because
apply writes there. That refusal also hid the root from the
read-only checks, so hosted scan gave no stale-install warning and
both the in-run --vex and a later `vex` attested not_affected while
bundler kept loading the unpatched gem from that path.

The refused root is now exposed as a verification-only store: the
hosted stale-install probe and vex's installed-copy lookup read it,
while apply and rollback still never write there. A stale copy
there gets the project-local remedy.

Fixes #709

Assisted-by: Claude Code:claude-opus-5-5

* Test hosted scan over an out-of-tree bundle path

Covers #709 end to end: a stale gem under a .bundle/config path
outside the project now warns with the project-local remedy, and the
same run's --vex does not attest it.

Assisted-by: Claude Code:claude-opus-5-5

* Run the out-of-tree bundle path probe test

The regression test for #709 was nested inside another test function,
so it compiled but never ran. Move it back to module level.

Assisted-by: Claude Code:claude-opus-5-5

* Honor BUNDLE_IGNORE_CONFIG for the bundle path

With BUNDLE_IGNORE_CONFIG set, bundler reads no config file, so a
.bundle/config path is neither an install root nor a root bundler
loads from. Discovery now skips the app config's BUNDLE_PATH in that
case, the same way the cache-path and Gemfile readers already do, so
leftover gems under that path no longer raise a stale-install
warning or fail VEX.

Assisted-by: Claude Code:claude-opus-5-5

* Port #851: fix vex alias tests broken on main

Main is red since #605: two vex_consumed tests assumed the copy
resolver never returns npm-aliased copies, which #605 changed. This
ports the test-only fix from #851 so this PR's CI can go green; it
becomes a no-op once #851 lands.

Assisted-by: Claude Code:claude-opus-5-5

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants