v5 WS1+WS2: ledger-free hosted mode, upstream-restore rollback, vendor ejects hosted projects - #280
Conversation
Hosted scan keeps its edits and records in memory only; the lockfiles are the record of a redirect. Replays the parked WIP (which was snapshotted on an older tree) as just its hosted.rs delta. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…laying the ledger Imports the stopped local WS1 agent's work-in-progress (backup/local-v5- ledger-free-hosted): core patch::redirect::upstream re-resolves npm-family, cargo and golang registry entries for every hosted pin vex::discover finds in the lockfiles, and rollback/remove/vendor route their hosted legs through it instead of the redirect ledger. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
v5 keeps no hosted ledger, so every reader that consulted .socket/vendor/redirect-state.json now reads the hosted pins lockfile discovery finds: - list shows each hosted pin with the lockfiles wiring it (details.lockfiles); a pre-v5 ledger only supplies the details of pins it still describes. - scan's updates[] fold, redirectState block and the agent-flow hosted_wiring_retained probe read the pins. - The hosted-over-vendored takeover classifier reads the pins; the vendored-over-hosted ledger reconcile (vendor_supersedes_redirect) is gone: once the lock routes a package to .socket/vendor/ no hosted state is left to go stale. - vex treats a malformed pre-v5 redirect ledger as an advisory. scan/mod.rs unit tests still need rewriting (WIP). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
… scan fold - get's vendored lock-text gates read the lockfiles' hosted pins instead of the redirect ledger (DownloadParams carries --patch-server-url for it). - repair's hosted-only no-op fires for hosted lockfile pins (or a pre-v5 ledger). - The in-memory hosted engine neither reads nor emits .socket/vendor/redirect-state.json. - Disk hosted scan no longer folds edits into a throwaway ledger; its records feed only the stale-install probes and in-run VEX. - The cargo vendor backend's hosted_redirect_live refusal names rollback / git checkout instead of a ledger. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
…s upstream first WS2: standalone `vendor` with no manifest now takes its patch set from the lockfiles' hosted pins (purl + the uuid in each hosted URL), fetches each record from the API, vendors it into .socket/vendor/ through the same step `scan --mode vendored` and `get --mode vendored` use, and rewires the lock from hosted to vendored. Without hosted pins it keeps the no-manifest no-op. The vendor takeover now restores the upstream registry entry before vendoring for every ecosystem, not only cargo/npm/golang, so the vendor ledger always records the upstream entry as its original and `vendor --revert` returns to upstream rather than to hosted. A pin whose upstream entry cannot be restored is refused with the checkout remedy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
…tate v5 hosted mode keeps no ledger, so the scan/mod.rs unit tests now make a purl "hosted" by writing a lockfile that pins the hosted URL instead of planting .socket/vendor/redirect-state.json: - overlap / classify_overlap_takeover tests use hosted package-lock, yarn (classic + berry), bun and cargo sparse-index pins; the non-default-host test configures --patch-server-url and pins that an unconfigured host is no pin. - New behavior pinned: a pre-v5 ledger on disk is never hosted state; a lock routed to vendored (npm or cargo) yields no pin and no overlap; an edits-only state names no package; a half-migrated project whose locks name both sides stays silent; the redirectState block has no ledger/ledgerKey fields. - hosted_wiring_retained_purls / redirect_state_json tests read the lockfile-derived state, keeping the probe's own liveness gate covered. - Removed tests of retired behavior: note_vendor_supersedes_redirect reconcile (wet/npmrc/dry-run/no-op/persist-failure), the edits-only fallback (degraded ledger, vlt tilde keys) and following the vendored remediation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
Add the RubyGems and Composer restorers to the v5 ledger-free hosted
unwind (`redirect::upstream`).
Gem (Gemfile.lock / gems.locked + Gemfile / gems.rb): a converged Socket
GEM section is removed and its spec moved back, in name order, into the
upstream section (the single remaining one, else the manifest's global
source or rubygems.org, else refused as ambiguous); a bundler <= 2.1
merged section loses only the Socket remote; the DEPENDENCIES `!` pin is
dropped; CHECKSUMS is re-pinned from the rubygems.org compact index. The
Gemfile source block becomes `gem "n", "v"[, opts]` again (the original
constraint is not derivable), or is removed with its DEPENDENCIES entry
only when provably a transitive append. The pre-2.6 mixed state is undone
when a pin is supplied, keeping the untouched lock's own constraint.
Composer: dist {type,url,reference,shasum} and the dropped source block
are rebuilt from packagist p2 metadata (composer/2.0 minified, expanded),
cross-checked against the lock's dist.reference, in the lock's indent,
slash style and line endings. Non-packagist entries are refused.
UpstreamClient gains cached rubygems and packagist lookups
(SOCKET_RUBYGEMS_URL, SOCKET_PACKAGIST_URL).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
Add the Python family to the v5 hosted -> upstream restore: Pipfile.lock, requirements.txt, Hatch direct references (pyproject.toml / hatch.toml), poetry.lock, pdm.lock, and uv.lock / PEP 723 script locks / PEP 751 pylock files with their paired pyproject / script metadata. Each restorer rewrites only entries whose reference is a hosted URL for an in-scope patch uuid and re-derives what the hosted rewrite overwrote from PyPI's JSON API (UpstreamClient::pypi_files, base overridable with SOCKET_PYPI_JSON_API, cached like the other lookups). Where a field is not derivable the pin is refused instead of guessed: requirements hash-checking mode that no other line settles, a Pipfile.lock index that is not PyPI, PDM locks without cross_platform (or uv locks) when the release ships platform- or interpreter-specific wheels, uv locks with no sibling registry package to show the artifact shape, several or non-PyPI registries, exclude-newer / no-binary / no-build filtering, multi-clause uv specifiers with no spelling evidence, uv 0.2 [[distribution]] locks, offline runs and registry failures. The golden harness round-trips the native Poetry (1.0-2.4), PDM (every supported lock_version) and Pipenv fixtures plus synthetic requirements/Hatch/uv/pylock projects through the real hosted rewriter; the shared requirements golden restores modulo the grant's name casing and the uv golden (no registry sibling) is refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
vlt-lock.json: slot [2] from npm dist.integrity, slot [3] per the lock's own convention (same-era default-registry siblings, else DepID era and options.registries), every hosted instance of name@version together. Maven (no network): base versions back, added dependencyManagement entries, socket-patch repositories and emptied wrappers removed, trusted-checksum lines dropped and .mvn files deleted only when nothing but hosted content is left; module poms and stray suffix uses refuse. NuGet: socket-patch source and mapping removed, a mapping that only fans * out to every source dropped; packages.lock.json contentHash re-derived from nuget.org's catalog packageHash (SOCKET_NUGET_URL), refusing when the restored config does not resolve the id from nuget.org alone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
…e the cargo guard test rollback in a project whose only state is a stale pre-v5 redirect-state.json (no manifest, no vendor ledger, no hosted pin in the lockfiles) removes that file and exits 0 instead of failing on the missing manifest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
The in-run `scan --mode hosted --vex` attestation read its hosted records from the ledger the run had just written. With no ledger, the run's fetched records reach the VEX builder in memory (VexBuildParams hosted_records), merged over any pre-v5 ledger's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
A package vendored over hosted wiring before v5 recorded the hosted fragment as its pre-vendor original, so reverting it wired the lock back to the patch server. After a wet revert, any hosted pin on a reverted purl is restored to its upstream registry entry (warning vendor_revert_restored_upstream; a refused restore is a failed event, hosted_restore_failed, exit 1). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
|
CI is expected to be red for now. This draft PR removes the hosted ledger (
Generated by Claude Code |
Mikola Lysenko (mikolalysenko)
left a comment
There was a problem hiding this comment.
v5 review: this is the critical hosted-pivot PR, and it needs fixes before landing. Reviewed/tested 5eadb006 and checked the subsequent 2b331c1 delta; the findings below are unchanged.
I reproduced three management-path problems (inline), even though all 36 upstream_restore_golden tests pass. The missing coverage is the complete CLI lifecycle, especially fresh checkouts and offline behavior.
For simplification, this branch currently adds another family of per-format inverse parsers while retaining old ledger replay machinery. Its reviewed diff was +12,100/-2,827 lines, including a checked-in 586-line upstream/client.rs.orig. Delete that backup, route restore through #281's shared models, and audit removal of unused ledger writers/replay paths while preserving a small legacy reader. Verify external library consumers before deleting exported APIs.
Make hosted→vendored a single planned transition: acquire/verify the replacement, plan edits against a staged project view, then commit. Avoid restoring the live project to upstream before knowing that vendoring can succeed. The current failure path still commits accumulated restore edits, so add failure-injection coverage proving a failed eject preserves hosted protection.
Use raw lockfile wiring as state and VEX eligibility as a separate judgment of that state. Reusing an attestation-filtered list as the entire management inventory loses information.
Update README/CLI_CONTRACT/CHANGELOG with the actual ledger-free, rollback, offline and eject guarantees in the same PR. These still describe hosted ledger persistence at the reviewed head.
Validation: built the CLI; localhost HTTP probes for offline and fresh-Cargo eject; conflicting-lock list/vendor/rollback probes; 36 core upstream restoration goldens passed. Full cross-platform matrix not run locally.
…y request Review follow-ups: - core HostedInventory keeps raw hosted wiring apart from attributable pins: a hosted identity discovery recognizes but cannot attribute (locks that disagree, a malformed reference, a lockless registry pin) is contested wiring. rollback (unscoped), remove, list and vendor refuse around it with hosted_wiring_contested, naming the files and the git checkout remedy, instead of reporting a bare project. - vendor's eject refuses offline (flag or env, wet or dry) with offline_eject_unavailable before it builds any request. - Drop the stray upstream/client.rs.orig merge backup. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
Review follow-ups on the hosted -> vendored eject: - Every patch record is fetched first; one the API cannot serve refuses the whole eject (eject_refused) before anything is touched. - The upstream restore is planned first (a dry resolve of every pin); a pin that cannot be restored refuses the whole eject with its remedy. - A dry run stops at the verified plan (eject_planned events) and writes nothing, not even .socket/. - The wet run takes the apply lock once, snapshots every file the eject can touch (root files, pin and restore files, cargo/maven config, the vendor ledger, vendored uuid dirs), restores the pins upstream BEFORE the vendor engine inventories sources (so a fresh checkout with nothing installed resolves the pristine registry package), vendors, and on any failure puts the snapshot back (eject_rolled_back): a failed eject leaves the project hosted, byte for byte. Adds failure-injection and dry-run coverage. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
…restore Hosted state is the lockfile pin: rollback/remove restore the default upstream registry entry (mock npm registry via SOCKET_NPM_REGISTRY), a refused pin (offline, registry 404, missing integrity) fails closed with the git-checkout remedy, and a pre-v5 ledger is never replayed but retired once no pin remains. Ledger-persist failure tests become restored-lockfile write failure tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
…ontract - in_process_rollback_hosted (+ vlt): hosted pins come from the lockfiles and are restored from a mock npm registry; per-pin refusal, scoped restores, legacy-ledger retirement (never replayed), preserve-state, and the vlt heal following restored pins. A vlt project re-locked onto the registry has no hosted state left to roll back. - coverage_fix_rollback_ecosystem_scoped_replay -> ..._scoped_hosted: --ecosystems never restores another ecosystem's pin nor retires the pre-v5 ledger while a pin remains. - e2e_golang_hosted_state: rollback and vendor takeover restore go.sum from a mock checksum database; offline refuses. - redirect_npm_allow_remote: no ledger records the .npmrc edit; restore deletes only a pristine scaffold .npmrc and reports a kept allow-remote=all line (npm_allow_remote_left). - hosted_symlinked_files, repair_invariants: no ledger is written; a lockfile-pin-only project takes the redirect_only_project skip. - vex_pipenv_pip_steps: a reverted checkout with no ledger is the plain manifest_not_found error; apply --vex fetches the record online when no ledger supplies it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
…ract Hosted scan/get write no .socket/vendor/redirect-state.json: assertions on the ledger become "no ledger" plus lockfile facts; pre-v5 (incl. corrupt) ledgers are pinned as ignored and left byte-identical; ledger-write failure tests become "a read-only .socket/vendor or a squatting dir no longer blocks the run"; re-runs are pinned idempotent on the lock bytes. Rollback round trips now name the mock host with --patch-server-url and mock the upstream registry (SOCKET_NPM_REGISTRY / SOCKET_PYPI_JSON_API); berry CRLF/BOM, bun digestless, pnpm, poetry, pdm and pipenv restores are checked against the pristine locks. bun.lockb rollback pins the refusal. Manifest-less VEX legs attest from lock + API, and use a synthesized pre-v5 ledger as the extra local record source for the offline and redirect_unwired legs. scan redirectState/hosted_wiring_retained/updates are pinned to lockfile pins; vendor_supersedes_redirect is gone. vlt_hosted_common gains assert_no_ledger, legacy_record_from_view and write_legacy_ledger (additive). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
- remove's hosted_revert_failed message is the restore's own refusal (it already names the pin and the remedy) instead of wrapping it twice. - Retiring a pre-v5 ledger prunes the emptied .socket/vendor/. - The in-run hosted VEX summary says patches are attested from their patch records, not from a ledger. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
list: hosted entries are lockfile pins (details.mode hosted, details.lockfiles, no details.ledger); a pre-v5 ledger only details a matching pin and never lists a record by itself. vex: a malformed pre-v5 redirect ledger is the redirect_ledger_corrupt warning, the run proceeds and the file is left byte-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
v5 hosted mode writes no .socket/vendor/redirect-state.json, so the shared harnesses (vex_pdm_hatch_common, vex_pipenv_pip_common, vex_e2e_common/bun and vlt, npm_e2e_common/manifestless) now assert the hosted wiring run left NO ledger, while vendored keeps its .socket/vendor/state.json cells. Hosted cells that relied on the ledger now pin the new contract: offline with no local record is record_unavailable, online attests from the API, and a reverted hosted lock leaves nothing to discover. vendor over hosted pins is the eject flow, so the manifest-less embedded cells drive apply for hosted checkouts and a new cell pins the eject path. One focused cell shows a committed pre-v5 ledger still lets a hosted pin attest offline (new additive helpers: write_legacy_redirect_ledger, assert_no_hosted_ledger, LEGACY_REDIRECT_LEDGER). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
Poetry 1.0/1.1 record `[metadata.files] <name> = []` against today's PyPI JSON API, while locks written earlier list every release file. The hosted rewrite replaced either with the one-entry patched array, so rollback could not tell them apart and always re-derived the full release list: the backtest's `direct` and `crlf` shapes (literal `urllib3 = []`) failed rollbackRestoresLockBytes on Poetry 1.0.10 and 1.1.15. The rewriter now keeps that bit in the patched entry's layout: one file per line (Poetry's own rendering) when the original listed files, inline when it was `[]`; a re-run keeps the layout it finds. The restore reads it back and writes the full release list or `[]`. Adds a golden round-trip over every native fixture generation (1.0.10 to 2.4.3), LF and CRLF, alongside the existing populated-shape one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
The berry matrix still asserted the pre-v5 .socket/vendor redirect ledger after a hosted flow. v5 hosted mode writes none: the offline cell now expects record_unavailable, a reverted hosted checkout discovers nothing (exit 2 manifest_not_found), and manifest-less apply --vex is a calm no-op. The yarn4 pnpm-linker and workspaces fresh checkouts copy .socket/ only when it exists, as the node-modules berry test already does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
A populated lock-1.0/1.1 [metadata.files] entry now keeps Poetry's multi-line layout after the hosted rewrite (so rollback can tell it from an originally empty one); the pin-spelling matrix strips that form too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
The hosted Pipfile.lock rewrite dropped each entry's `index`, and the ledger-free upstream restore then guessed it from sibling registry entries (none -> the PyPI source name; all siblings index-less -> none). Pipenv's own choice cannot be re-derived from the lock or the Pipfile: for the same Pipfile it depends on the release and the locking environment (measured with real `pipenv lock`): shape 2018.11.26 2020-2022 2023.12.1-2026.8.0 direct pypi pypi pypi extras table pypi pypi (none) marker-excluded pypi (none) (none) transitive (none) (none) (none) So the backtest's rollbackRestoresLockBytes failed on 2022.12.19 extras (transitive pysocks sibling has no index -> index dropped) and on 2022.12.19 / 2026.8.0 marker-excluded (no siblings -> "pypi" added), and rollbackAfterRelockRetires failed on 2026.8.0 marker-excluded: the relock hybrid (our `file` kept, `version` and registry `hashes` restored, no `index`) was restored with an `index` Pipenv never wrote. The hosted rewrite now keeps `index` exactly as Pipenv wrote it (present or absent) and only drops `version`; the restore carries the entry's `index` back unchanged instead of choosing one, refusing when it (or the Pipfile's explicit index) does not name a PyPI source in `_meta.sources`. A `file` entry carrying `index` installs the referenced wheel itself (direct_url.json present) on Pipenv 2018.11.26, 2020.11.15, 2021.11.23, 2022.12.19, 2023.12.1, 2024.4.1, 2025.1.3 and 2026.8.0 (`install --deploy`, `sync`, `verify`), and a marker-excluded one stays uninstalled; Pipenv 7-11 ignore `index` on a `path` entry (convert_deps_to_pip skips it for file/path deps). Tests: real Pipenv 2018.11.26 / 2022.12.19 / 2026.8.0 locks for the extras and marker-excluded shapes (tests/fixtures/pipenv-shapes) round trip byte for byte in LF and CRLF, and the 2026.8.0 marker-excluded relock hybrid restores the pristine bytes. The backtest's allCategoriesRewritten now expects hosted entries to keep the pristine `index` (vendored still drops it). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
|
This update is at 08c30b7. It fixes most of the CI failures from dc634b3; the Product fixes (hosted rollback must restore the lockfile byte for byte)
Test and harness fixes
Still open
Local checks: clippy is clean, and the full workspace test suite passes except 18 tests that assume a non-root user and fail only because this sandbox runs as root. Generated by Claude Code |
With the hosted ledger gone, `vendor` over a live hosted pin in a binary bun.lockb (Bun 0.8.1-1.1.x's default lock, Bun 1.2's legacy lock) was refused `redirect_revert_failed`: format_of() mapped bun.lockb to Unsupported, so the backtest matrix's hosted-then-vendored cell exited partial_failure. The upstream restore gains a bun.lockb restorer (patch/redirect/upstream/bun_lockb.rs). It rebuilds each hosted remote-tarball record as Bun's npm registry record for name@version, from the registry's dist.tarball / dist.integrity (SOCKET_NPM_REGISTRY aware), via the new BunLockb::set_registry_package. That function re-interns the URL (re-using the original pool offset), drops the hosted URL string from the pool tail and re-derives the metadata hash. The staged restore view now carries binary files. To make the rebuild byte-exact, set_package keeps the registry record's inactive bytes (padding, semver) when it writes a remote tarball record. Early writers leave uninitialized padding there (Bun 0.8.1), so this matters. A re-pin to a later grant's URL now drops the superseded URL from the pool. A record without the retained bytes is rebuilt the way Bun writes one; prerelease versions are refused in that case. The rebuild is exact for every fixture writer except a format-1 lock (kept promoted) and workspace locks (behaviors kept normalized). So only the vendor takeover and eject opt in (RestoreOptions::bun_lockb): their vendor ledger records the rebuilt record, and `vendor --revert` returns the pre-hosted bytes. `rollback` / `remove` keep refusing a hosted bun.lockb pin with the `git checkout -- bun.lockb` remedy, as the harness's rollbackLockbRefused expects. An offline vendor still refuses. Tests: core restorer tests over every real fixture, codec tests for the rebuild (retained and zeroed records, re-pin), a hermetic CLI test (vendor_eject_bun_lockb.rs: takeover, eject, rollback and offline refusals on Bun 0.8.1 / 1.1.38 / 1.2.0 locks), and the real-Bun e2e_bun_lockb takeover now vendors online and reverts byte-exact. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
On Windows, Path.write_text turned the injected bun.lock's LF into CRLF, so the LF pre-injection bytes could never match the (EOL-preserving) upstream restore: custom-registry hosted rollbackOriginalFiles failed on Windows only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
The bun backtest's binary job failed only for the format-1 writers (Bun 0.1.1 / 0.1.6, readers 0.5.9 and 1.4.2) after 65aa074: the hosted rewrite promotes a format-1 lock to format 2 (format 1 has no URL column), and the new native upstream restorer rebuilt the registry record inside that promoted lock. The vendor ledger then recorded the promoted bytes as its pre-vendor original, so `vendor --revert` returned a format-2 lock and e2e_bun_lockb's "the revert restores the pre-hosted bytes exactly" assertion failed. A promoted lock is byte-for-byte indistinguishable from one Bun 0.1.7+ wrote (same pool order, same metadata hash), and hosted mode keeps no ledger, so the restorer could not tell. The codec now marks a lock whenever an edit had to normalize it: seven magic bytes and a flag byte in the last eight bytes of the root package's resolution (the root resolution's value union, which no Bun reader reads; early writers leave uninitialized bytes there, and 1.4.2 / 0.5.9 read such locks). For a promoted lock those bytes are new, so the mark overwrites nothing. promote_legacy_format sets NORMALIZED_FORMAT_1; normalize_workspace_behaviors sets NORMALIZED_WORKSPACE when it changes a dependency behavior or workspace literal. The vendor ledger's layout_original path normalizes the original the same way, so its exact revert is unchanged. The bun.lockb upstream restorer then: - demotes a NORMALIZED_FORMAT_1 lock back to format 1 once every hosted record is rebuilt (BunLockb::demote_legacy_format, which drops the URL column and the URLs the promotion appended to the pool, and succeeds only if promoting the result again reproduces the lock byte for byte). Otherwise the pins are refused with the `git checkout -- bun.lockb` remedy. - refuses a NORMALIZED_WORKSPACE lock outright with that remedy, since clearing the workspace behavior bit cannot be undone. It no longer takes the lock over non-exactly. Tests: the upstream restorer's byte-exact test now covers 0.1.1 / 0.1.6. Workspace-normalized extension locks refuse, and so does a lock whose mark carries an unknown flag. The codec rebuild test checks the exact demotion. vendor_eject_bun_lockb adds the 0.1.1 / 0.1.6 takeover with an exact revert and a workspace-lock refusal. Locally with real Bun, the 1.4.2 reader x 0.1.1 / 0.1.6 writer cells now pass the takeover and the exact revert. They then stop at the 0.5.9 legacy reader, which segfaults on any networked install in this sandbox. The 1.1.45 / 1.2.0 / 1.4.2 cells pass everything except the `extensions` shape, which needs api.github.com (403 here). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
The Windows leg runs the same suite ~1.6x slower than macOS. On the base branch it already took 34m40s of the flat 35-minute budget, and with this PR's added tests it was cancelled at the limit mid-run (no failures). Linux and macOS keep 35 minutes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
The Windows test leg's CRLF checkout (core.autocrlf) turned the LF-committed tests/fixtures/poetry, pipenv and pipenv-shapes locks into CRLF, so the byte-exact upstream-restore round trips (and their derived CRLF variants, which became \r\r\n) and the Poetry VEX pin-spelling test failed on Windows only. Mark them -text like the other captured-lock fixtures. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
|
CI status after ba7034f / fc9a52a:
Generated by Claude Code |
|
CI on fc9a52a has finished, and the only two red checks also fail on
Still open from earlier: owner sign-off on the Pipenv contract change (hosted Generated by Claude Code |
686e5fb
into
release/v5-prerelease
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 3 potential issues.
Bugbot Autofix is ON, but it could not run because the branch was deleted or merged before autofix could start.
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit fc9a52a. Configure here.
| Err(e) => return Err(e), | ||
| }; | ||
| files.push((rel, bytes)); | ||
| } |
There was a problem hiding this comment.
Eject snapshot reads hang on FIFOs
High Severity
EjectSnapshot::take reads every project file it may restore with bare tokio::fs::read. A FIFO or device at a lockfile or config path blocks the wet eject in open(2) before the snapshot exists, so the run never reaches the planned restore or rollback.
Reviewed by Cursor Bugbot for commit fc9a52a. Configure here.
| hosted_leg | ||
| .failed | ||
| .push(("hosted_wiring_contested".to_string(), refusal)); | ||
| } |
There was a problem hiding this comment.
Rollback writes then fails contested
High Severity
Unscoped rollback restores attributable hosted pins first, then fails the run when contested wiring remains. restore_upstream has already flushed, so a failed rollback leaves some locks upstream and the contested files still hosted. vendor eject refuses this layout before any write.
Additional Locations (1)
Triggered by learned rule: Safety refusals for unsupported configurations must run before calm early-returns
Reviewed by Cursor Bugbot for commit fc9a52a. Configure here.
| ) | ||
| .into_iter() | ||
| .map(|pin| canonical_purl(&pin.purl)) | ||
| .collect(); |
There was a problem hiding this comment.
Vendored refusals miss contested pins
Medium Severity
lock_text_refusals_for and hosted_state_from_lockfiles still build hosted claims from HostedPin::all, which only sees VEX-eligible refs. Contested hosted wiring is omitted, so those packages are lock-text-gated or classified as not hosted instead of taking the restore/takeover path.
Additional Locations (1)
Triggered by learned rule: Inventory/discovery must propagate unsupported-layout diagnoses as typed errors, never swallow into None
Reviewed by Cursor Bugbot for commit fc9a52a. Configure here.
…odels Conflicts resolved toward #280's model: the yarn fragment-kind helper goes with the ledger, the rollback fixture keeps base's shape, and the scan test imports follow base. #280's new upstream restore now reads through the format models this branch introduced: the Cargo.lock restore splices source/checksum at CargoLock's spans instead of the deleted block walker, and the gem, maven, nuget, composer and pnpm readers are imported from formats::. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BYsX2VfVNKHvfFAJnFryc
…setup-and-ui Conflicts resolved toward #280's model: no hosted ledger, rollback/remove/ vendor restore hosted pins via patch::redirect::upstream. This branch's changes stay on top: `setup` removed, human text says "hosted" and drops warning codes, one numbered Next steps block, empty `list` exits 0 (the contested-wiring error from #280 still exits 1), shared cancel line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PQaKzoW5dSw9u5pgAAvVRj
… and docs #280 added tests and contract lines with the pre-WS8 human strings ("Would redirect", "<purl> redirected, but its patch record ...", `Warning (<code>): ...`). Switch them to this branch's conventions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PQaKzoW5dSw9u5pgAAvVRj
…sted-engine Resolved toward #280's model: v5 hosted mode keeps no ledger. - hosted::ledger (the redirect-ledger merge, in-memory load and serializer) is deleted; neither the disk flow nor the in-memory engine reads or writes .socket/vendor/redirect-state.json. - Ledgers' hosted store is now the hosted records: the lockfiles' hosted pins (or a run's fetched records), plus a pre-v5 ledger read only for migration. hosted_vendored_overlap drops the edits-only fallback. - list, scan's updates[] and rollback run the shared owner rule over the manifest and the vendor ledger and take the hosted pins from the lockfiles; updates[] keeps #280's precedence (manifest > pins > vendor ledger). scan reads the pins through ProjectContext's one discovery. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KHZ8uzdXfkG2zH8ZYDG8ju
* Remove the setup subcommand and its install hooks (v5 WS7) `socket-patch setup` (and --check/--remove/--exclude) is gone, with every install hook it wired: npm postinstall/dependencies scripts, the socket-patch[hook] .pth wheel, the in-tree Bundler plugin + Gemfile block, and Composer post-install/update scripts. `apply` stays; agent mode in CI is `scan --mode agent` once, then `socket-patch apply` after each install. Deleted: commands/setup.rs, core setup/** and the setup-only package_json helpers, the setup tests and setup-matrix suites, the setup-e2e feature, the setup-matrix CI job, tests/setup_matrix and scripts/setup-matrix.sh. vex's install-hook "Property 7" filter goes with it. The socket-patch-hook wheel and socket-patch-bundler gem are dropped from the build and publish workflows (sources kept, frozen, pending an owner decision). Also the plan's small follow-ups: drop the core crate's deprecated re-export aliases (and the CI grep that guarded them), the unused utils::process::tool_command, the vacuous e2e_cargo/e2e_golang CI rows, add the merged 01019627 and 9c2b4925 gem patches to the vendored production e2e, and retire the backtest-poetry "known crawler gap" label. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PQaKzoW5dSw9u5pgAAvVRj * Streamline the patch UI (v5 WS8) - `-h` lists about eight options per command (`cli_command()` marks the rest hide_short_help; `--help` is unchanged); `scan --apply/--vendor` are hidden (still accepted). - Human warnings drop the `(code)` tag (`Warning: …`, `GC: skipped: …`); JSON keeps every code. Error lines keep theirs. - Human text says "hosted", not "redirect" (JSON keys unchanged). - npm's allow-remote notice is one line; `--verbose`/JSON keep the full policy text. - One `ui::next_steps` renderer for hosted and vendored results. - Hosted and vendored `get` never prompt: top-ranked patch per package, like scan, in JSON too. Agent-mode `get` keeps its picker and confirm. - `list` with nothing to list says `No patches in this project. Run \`socket-patch scan\`.` (exit codes unchanged: 1 missing, 0 empty). - One cancel line (`ui::CANCELLED`) and one paid upsell (`ui::PAID_UPGRADE`). - `get`'s self-enforced flag conflicts and `rollback --one-off` exit 2, like every other usage error. Docs: CLI_CONTRACT (human output conventions, exit codes, get prompts), README, CHANGELOG [Unreleased], v5 plan status. Tests updated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PQaKzoW5dSw9u5pgAAvVRj * Pin the real-vlt get_and_remove leg to --mode agent `get <uuid>` defaults to hosted since v5, so the leg's in-place patched/pristine assertions need agent mode spelled out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PQaKzoW5dSw9u5pgAAvVRj * Match the hosted-ledger persist-failure wording in two covgap tests These chmod-guarded tests skip under root, so the WS8 wording change ("hosted redirect ledger" -> "hosted ledger") only showed up in CI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PQaKzoW5dSw9u5pgAAvVRj * Empty list exits 0; group help by task; document the setup upgrade Review follow-ups: - `list` on a project with no manifest and no ledger record is an empty list: exit 0, the empty-project line (human) or the success envelope with `events: []` (`--json`). Only an unreadable or invalid manifest fails. Hosted mode writes no manifest, so this is the normal case. - Root help groups the commands by task (patch, undo, ship, agent mode) instead of calling get/rollback/remove "older agent-mode commands"; the subcommand list follows the same order. `-h` keeps --cwd, --ecosystems and --offline, and moves `scan --prune` to --help. - README gains "Upgrading from `setup`": move to hosted or keep agent mode, and the exact hook to delete per ecosystem. The CHANGELOG and the frozen hook/plugin READMEs link it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PQaKzoW5dSw9u5pgAAvVRj * Carry the hosted wording and code-free warnings onto #280's new tests and docs #280 added tests and contract lines with the pre-WS8 human strings ("Would redirect", "<purl> redirected, but its patch record ...", `Warning (<code>): ...`). Switch them to this branch's conventions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PQaKzoW5dSw9u5pgAAvVRj * Give the cargo safety VEX baseline a vulnerability to attest With setup's install-hook filter gone, the manifest-backed agent-mode cargo patch attests, but the staged minimal manifest carries no vulnerabilities, so vex ended no_applicable_patches (exit 1). Add one vulnerability to the entry before the baseline run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PQaKzoW5dSw9u5pgAAvVRj --------- Co-authored-by: Claude <noreply@anthropic.com>


Implements WS1 and WS2 of
docs/design/v5-plan.md. Draft.WS1: ledger-free hosted mode
scan/get --mode hostedno longer write.socket/vendor/redirect-state.json. Hosted runs write only lockfile edits.patch::redirect::upstream: for each hosted pin found in the lockfiles (vex::discoverrefs: purl, uuid, file), it restores the default upstream registry entry by re-resolving the registry artifact. When it can't (fields it can't derive, offline runs, non-PyPI Pipenv indexes, and so on), it refuses and tells the user to rungit checkout -- <lockfile>. It never replays recorded fragments.WS2:
vendorejects hosted projectsvendortakes the patch set from the lockfile's hosted pins, restores upstream first, vendors those patches into.socket/vendor/, and rewires the lockfiles from hosted to vendored.vendor --revertgoes back to upstream.bun.lockbhosted pins are rebuilt natively for the takeover. Format-1 locks are demoted back to format 1 exactly, and workspace-normalized locks are refused. rollback/remove keep refusing hostedbun.lockbpins with thegit checkout -- bun.lockbremedy.Status
bun.lockbfor vendor), vlt, cargo, golang, composer, gem, maven, nuget and PyPI (requirements, uv, pylock, Poetry 1.0-2.x, PDM, Pipenv), or a documented refusalPipfile.locknow keeps Pipenv's ownindex(comment 5862392841)CI
Everything is green except two checks that also fail on
release/v5-prerelease: Windowscovgap_commands_scan_mod, and vltinstall-proof(vlt_pinned_matrix_agent_get_and_remove). Details in comment 5864860663.🤖 Generated with Claude Code
https://claude.ai/code/session_018tncU7u7FK48tubFNA12UQ
Note
High Risk
Major semver release that rewrites hosted rollback/remove/vendor behavior and multi-ecosystem lockfiles with registry-dependent upstream restore, so incorrect restore logic could leave projects in a bad install state.
Overview
v5 breaking change: hosted
scan/getno longer write.socket/vendor/redirect-state.json— only lockfile and registry-config edits persist, and pre-v5 ledgers are ignored for planning and retired on full rollback.Rollback,
remove, and vendored takeover now discover hosted pins from lockfiles and restore each to a re-resolved upstream registry entry via corepatch::redirect::upstream(replacing ledger replay andhosted_revert_unsupported). Refusals are fail-closed withgit checkout -- <files>guidance; binary hostedbun.lockbstays refused on rollback/remove while vendor eject can rebuild it.list,vex, and scan discovery treat hosted state as lockfile wiring (details.lockfiles, slimmerredirectState); legacy redirect ledgers are read-only for extra record detail or warnings, not for unwind.vendorwithout a manifest ejects hosted projects: fetch records, upstream-restore, then vendor — all-or-nothing with rollback on failure.get/vendored flows treat hosted pins via lockfile discovery (patch_server_url);vendor_supersedes_redirectis removed.Docs (README, CHANGELOG, CLI_CONTRACT), Windows CI timeout (50m), and
-textgitattributes for Poetry/Pipenv fixtures align with byte-exact lock tests.Reviewed by Cursor Bugbot for commit fc9a52a. Configure here.