feat: vendor/scan/apply overhaul — lockfile-driven discovery, verified auto-fetch, mismatch-tolerant apply#115
Open
Mikola Lysenko (mikolalysenko) wants to merge 17 commits into
Open
feat: vendor/scan/apply overhaul — lockfile-driven discovery, verified auto-fetch, mismatch-tolerant apply#115Mikola Lysenko (mikolalysenko) wants to merge 17 commits into
Mikola Lysenko (mikolalysenko) wants to merge 17 commits into
Conversation
The patches API serves scoped purls percent-encoded (pkg:npm/%40scope/name@1.0.0) and scan stores them verbatim as manifest keys, but neither the npm crawler nor the vendor coordinate parser decoded them — so apply/vendor reported scoped packages as 'package not installed', and detect_prunable saw every encoded entry as prunable. - utils/purl.rs: percent_decode_purl_component (strict, all-or-nothing, fail-safe passthrough), normalize_purl + purl_eq (compare/display only, never path construction) - npm_crawler parse_purl_components, vendor parse_npm_purl (NpmCoords now owns decoded name/version; base_purl stays verbatim for ledger/ manifest key parity), parse_jsr_purl: decode AFTER /-and-@ splits, BEFORE the is_safe_* guards — %2e%2e/%2f cannot smuggle traversal - detect_prunable + purl_matches_identifier compare normalized forms - human output shows the decoded purl; JSON keeps verbatim keys Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…y-applied events The vendor stage is a private copy and every apply write path is hash-gated to exactly afterHash, so a beforeHash mismatch (a patch built against different bytes than the installed artifact, or a package already patched in place by apply) no longer fails the vendor: the stage is overwritten with the verified patched content and the overwrite surfaces as a vendor_content_mismatch_overwritten warning event. Missing patch-target files still fail closed without --force (force's silent NotFound skip would pack an artifact without the fix). - shared force_apply_staged / missing_existing_patch_files / mismatch_overwrite_warnings policy helpers in vendor/mod.rs, used by all npm flavors (via stage_patch_pack) + cargo/composer/gem/pypi/ golang backends; dry runs predict the same outcome - vendor.rs: gate the already_vendored rewrite on entry.is_none() — the first vendor of an in-place-applied package now emits Applied (it packed + rewired this run) instead of a miscounted skip - scan --vendor: pre-prompt baseline check annotates mismatched packages before the confirm prompt (best-effort, read-only) - --force narrowed to missing-file tolerance + variant-probe bypass; CLI_CONTRACT.md documents the new warning code Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A user-authored override/resolution that pins the package to exactly the version being vendored (Flowise: pnpm.overrides 'tar-fs': '3.1.0') no longer refuses with vendor_override_conflict. The pin's key is kept (its spelling and quoting preserved on both pnpm surfaces — pnpm hard-requires the package.json and lock override maps to agree), its VALUE is rewritten to the file:.socket/vendor/... spec, and the pinned value is recorded as the wiring original so every revert path (--revert, reconcile, remove) restores the user's pin verbatim. - pnpm: classify_pkg_override (Insert / Ours / Takeover) replaces the boolean conflict checks; effective key threads through EditCtx, apply_pkg_override and edit_overrides; revert restores originals in place instead of deleting. Ranges, different versions, parent>child selector chains, and duplicate same-name keys still refuse, now with a hint that exact pins are taken over. - yarn berry: bare-name resolutions pin equal to the version is taken over symmetrically (KIND_RESOLUTION records the original). - npm/yarn-classic/bun wire the lock only (no override surface), so no conflict exists there to take over. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
scan --prune previously blanket-exempted vendored purls, so nothing
ever cleaned unused vendored state: dropped patches kept their
artifacts and overrides forever, removed dependencies stayed redirected,
and orphan uuid dirs were only swept by vendor --revert.
The prune pass now runs a vendored-state GC first (under the apply
lock; contention degrades to a skip, never a scan failure):
(a) entries whose patch is gone from the manifest are reverted
(same stale test as the vendor flows' reconcile_dropped);
(b) entries whose dependency left the lockfile graph are reverted and
their manifest entries dropped, feeding the same pass's blob sweep.
Per-flavor in-use probes: pnpm scans packages:/snapshots: blocks
for the artifact (the mirrored overrides: declaration alone is not
usage); package-lock/yarn/bun probe the lock text for the uuid dir
(those flavors wire resolutions into the lock itself). None =
cannot determine = keep, fail-safe; detached entries are exempt
(lockfile-invisible by design);
(c) orphan .socket/vendor/<eco>/<uuid> dirs are swept (extracted from
run_revert into a shared sweep_orphan_vendor_dirs).
JSON gc gains revertedVendoredEntries/removedVendorOrphanDirs (wet)
and revertableVendoredEntries/vendorOrphanDirs (preview, which also
mirrors the wet pass's manifest drops so blob counts agree); human
output gains a GC summary line. CLI_CONTRACT.md updated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ne lifecycle - scan_vendor_e2e: full pipeline with the API's percent-encoded scoped purl form (download -> vendor lookup against node_modules/@scope -> lock rewiring -> prune exemption); interactive pre-prompt baseline annotation + auto-force warning; scan --prune reverting an unused vendored entry (ledger + manifest + artifact + lock all reconciled) - clippy: too_many_arguments allow on stage_patch_pack, JsrPurlParts type alias Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Read-only inventories of the dependency set a lockfile resolves, independent of what is installed: name/version/purl plus the lock's artifact URL and content verifier (typed LockIntegrity: SRI, yarn sha1 fragment, berry cache-zip checksum, sha256 hex, go.sum h1 — the latter two for the ecosystems that follow). Powers scan's lockfile supplement and vendor's missing-package fetch. Covers all five npm flavors via detect_npm_lock_flavor (package-lock/ shrinkwrap, pnpm v9, yarn classic, yarn berry, bun). Fail-soft per entry, fail-closed per value (names/versions path-guarded; git/file/ link/workspace specs and our own vendored entries excluded; duplicate instances dedup preferring a verifier). lookup() bridges percent- encoded manifest purls. Reuses the wiring backends' parsers via pub(super) visibility bumps. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Downloads the artifact a lockfile entry resolves (lock-recorded URL, else the conventional npm registry URL; SOCKET_NPM_REGISTRY override), verifies it against the lock-recorded integrity FAIL-CLOSED before any disk write (strongest hash of a multi-hash SRI; yarn sha1 fragment; sha256 hex), and extracts to a private tempdir the vendor pipeline can stage from. Entries with no verifier are refused without any network I/O (Unverifiable). Hardening: http(s)-only, download/decompression/entry-count/entry-size caps, regular-files-only extraction with first-component strip + is_safe_relative_subpath (fail-closed on traversal-bearing tarballs, nothing half-extracts), exec bits preserved so the deterministic re-pack keeps bin scripts executable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…overy
vendor: a manifest patch whose package has no installed copy is now
satisfied automatically (no flag) instead of failing with
package_not_installed:
- already-vendored purls stage from their own committed artifact,
sha256-verified against the vendor ledger (fresh-clone re-vendor and
in-sync runs work offline, no registry traffic);
- otherwise the lockfile-resolved pristine artifact is fetched
(lock-recorded URL else conventional registry URL), verified against
the lock's integrity FAIL-CLOSED, and staged from a private tempdir —
the project tree is never touched.
Reason codes: vendor_fetched_missing (skip-warning beside the Applied
event), vendor_fetch_failed (distinct Failed, suppresses the duplicate
not-installed skip), vendor_fetch_unverifiable (no lock integrity →
calm skip). --offline keeps the calm skip and names the lockfile as
the would-be source.
scan: discovery now supplements the installed-tree crawl with
(a) lockfile-only dependencies — warned '[NOT INSTALLED]' in the table
+ a stderr note, JSON lockfileOnlyPackages + packages[].notInstalled,
counted as scanned so a wiped node_modules no longer prunes
lockfile-listed entries, partitioned out of --apply BEFORE download
(calm skipped/package_not_installed records, exit 0, no manifest
writes) while --vendor passes them to the auto-fetch; and
(b) vendored-ledger entries — the committed artifact IS the dependency,
so updates[] detection and scan --vendor keep working on a fresh
clone before any install.
scan --json --vendor now vendors a completely fresh clone end-to-end
(e2e-proven, second run already_vendored).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…aging tests Berry locks never hash the tarball — the checksum is sha512 of the deterministic cache zip. The fetch rebuilds that zip from the fetched bytes via the same spike-pinned berry_zip recipe the wiring uses and compares the 10c0/<hex> value fail-closed (foreign cacheKeys are Unverifiable). Plus unit coverage for stage_local_artifact's ledger-sha256 gate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Cargo.lock [[package]] inventory: crates.io-sourced entries carry
their sha256 .crate checksum (Sha256Hex); workspace members skipped,
git/custom-registry sources discovery-only. Fetch from
static.crates.io (SOCKET_CRATES_REGISTRY override), verify, extract
({name}-{version}/ top dir) — feeds vendor_cargo_crate's pristine_src.
- go.sum inventory: module-zip h1: lines (the /go.mod manifests-only
lines skipped). Fetch from the module proxy (SOCKET_GOPROXY, else the
standard GOPROXY's first non-direct element, else proxy.golang.org)
with Go's !uppercase path escaping; verify the dirhash Hash1/HashZip
in memory BEFORE extraction (algorithm validated against a live
sum.golang.org lookup for golang.org/x/text@v0.14.0); extract with
the explicit module@version/ prefix (module paths contain slashes, so
a first-component strip would be wrong) — feeds vendor_go_module.
- lookup() generalized across ecosystems; inventory_project() returns
the union the scan supplement and vendor auto-fetch consume.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…etch - composer.lock packages[]/packages-dev[]: zip dists with their sha1 shasum (frequently empty → discovery-only); names lowercased to the packagist form, pretty leading v dropped; path dists (ours) excluded. Fetch verifies sha1 and strips the variable zipball top dir. - Gemfile.lock GEM/specs + bundler 2.6 CHECKSUMS sha256 (older locks discovery-only); the GEM remote drives the /downloads/<gem> URL. Platform-suffixed specs skipped (unsupported for vendoring). The fetched .gem (plain tar) is sha256-verified whole, then data.tar.gz extracts at the root (no prefix strip). - pypi: uv.lock registry packages with a pure py3-none-any wheel carry a fetchable URL + sha256; poetry.lock and ==-pinned requirements.txt contribute discovery-only entries (PEP 503-normalized names). The unzipped wheel is a site-packages-shaped stage for the pypi backend. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
scan's lockfile supplement now consumes inventory_project (npm-family, Cargo.lock, go.sum, composer.lock, Gemfile.lock, uv/poetry/requirements) with per-ecosystem counts; the vendor auto-fetch pass likewise serves every inventoried ecosystem. CLI_CONTRACT.md gains the lockfile- supplement and vendor-auto-fetch sections + the three reason codes; README notes the fresh-clone flow; the exact-shape empty-scan contract test pins the additive lockfileOnlyPackages field; the cargo build e2e scrubs ambient CARGO_TARGET_DIR from child builds. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…efault; --strict restores the hard error
A file whose on-disk content matches NEITHER the patch's beforeHash nor
its afterHash previously hard-failed the in-place apply (the flatted
case: a patch built against non-registry bytes made plain apply
unusable). The default now overwrites such files with the FULL verified
patched content and continues:
- core: apply_package_patch's force bool becomes MismatchPolicy
{Warn (default) | Strict | Force}. Warn promotes HashMismatch to
Ready keeping the warning signature (expected/current hashes); the
diff strategy self-disables on a wrong base (partial patches are
skipped, as they must be) and the archive/blob writes stay hash-gated
to exactly afterHash — a tolerated mismatch lands verified patched
bytes or fails, never silent corruption. Missing pre-existing files
still fail closed (only Force skips them).
- CLI: global --strict (env SOCKET_STRICT) restores the fail-closed
behavior across apply/get/scan --apply/the hook/go redirects
(--force overrides it); plumbed through DownloadParams into the
nested applies. Vendor staging is unaffected (already auto-forces
into its private stage).
- Each overwrite logs a content_mismatch_overwritten warning to stderr
and rides the JSON envelope as a Skipped warning event beside the
package's Applied event.
- Since the full content lives in the afterHash blob and the default
--download-mode diff may not have staged it, a pre-apply pass probes
for mismatches and downloads the missing blobs by hash (offline runs
warn and let those files fail).
Live-verified: pristine flatted@3.3.1 + its bad-baseline patch now
applies 6/6 files via blob with per-file warnings (exit 0);
apply --strict exits 1 with the old error and leaves files untouched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The 'Patched packages' summary and the no-matching-installed-package warning printed manifest keys verbatim (pkg:npm/%40scope/...); show the decoded form like the scan/vendor output does. JSON keeps verbatim keys. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t/blobs or temp files Vendor flows (vendor, scan --vendor, --detached) no longer persist patch content anywhere on disk: a vendored project's .socket holds only manifest.json and vendor/. - core: PatchSources.mem_blobs overlay, checked before the on-disk blob read in the apply pipeline's blob strategy. - core: harvest_artifact_blobs — re-stage afterHash blobs from the committed vendor artifact itself (uuid-matched against the ledger, every blob self-verified by its own git-sha256), so in-sync re-runs and fresh clones of vendored projects stage with no network. - cli: stage_vendor_sources_in_memory replaces the disk stager in all vendor flows; missing content is fetched per patch via the proxy-aware patch-view endpoint straight into memory. - cli: DownloadParams.persist_blobs — scan passes !args.vendor so the scan --vendor download phase writes only the manifest. - e2e: .socket-stays-lean assertions (manifest mode, detached, fresh clone) + no-blobs detached idempotency; core harvest unit tests (tgz, dir-shaped, stale-uuid, escaping-path fail-closed). - docs: CLI contract "Patch sources stay in memory" section. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…reconstruction repair now owns the vendored-artifact lifecycle: artifacts referenced by the ledger and/or rewired lockfiles but missing or corrupt on disk are rebuilt fail-closed, and a wholesale-deleted .socket/vendor (state.json included) is reconstructed from the lockfile references alone. Core: - ArtifactHealth + check_vendored_artifact (vendor/verify.rs): per-file afterHashes plus a whole-file sha256 cross-check against the ledger for file-shaped artifacts. - recover_lock_entry (lock_inventory): the pre-vendor registry fragment recovered from the wiring originals (npm/pnpm/yarn/berry/bun fragments, composer dist, gem checksum line, uv wheel, cargo entry.lock); golang rides the unrewired go.sum. - wired_vendor_integrity + fetch_npm_unverified + artifact_matches_integrity: the REWIRED lock's recorded integrity of our packed tarball is the trust anchor — reconstruction can fetch pristine unverified and still land only bytes that reproduce the wired integrity (tamper => removed, fail). - Artifact-only rebuild branches in the composer/cargo/gem/golang/pypi backends: wired-but-broken artifacts rebuild in place with NO lock write and NO ledger re-record (fixes the latent original-clobbering full-path re-run); golang in-sync re-runs now record nothing; uv same-uuid re-runs are an InSync hot path instead of a refusal. - pnpm: fail-closed duplicate-mapping-key guard for half-edited locks in edit_packages/edit_snapshot_rekey. - Memory stager: a diff archive alone is no longer a sufficient vendor source (auto-force can need full after-blobs a diff cannot produce). CLI: - repair_vendor.rs: ledger health pass, lockfile-reference reconstruction (uuid recovered from the contract's path rule; manifest record else the patch view API => detached entry with the record embedded), rebuilds via the normal vendor dispatch + the pristine-source ladder, post-verified against the recorded fingerprint. Offline rebuilds run when fully local. - repair: manifest_not_found softened when vendor traces exist; step 1 skips vendored/lockfile-referenced entries (a vendored project's repair never re-litters .socket/blobs|diffs). - vendor auto-fetch: a MISSING committed artifact falls through to the ledger-recovered registry fetch instead of failing; corrupt stays loud. - Envelope: PatchAction::Rebuilt + summary.rebuilt (omitted while zero). Tests: repair_vendor_e2e (12 scenarios incl. tampered-pristine rejection, offline both ways, detached, no-ledger and no-manifest reconstruction), per-backend wired-missing-copy rebuilds, health matrix, fragment recovery per wiring kind, pnpm colon-key scanner unit, half-drifted lock guard. Live-verified on Flowise: 19/19 fresh vendor with a lean .socket, deleted artifact rebuilt byte-identically, and a 14/14 full reconstruction from nothing but the rewired pnpm lockfile. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…g corrupted the lock Every "ours" probe in the pnpm backend matched ANY same-name .socket/vendor path, so a project vendoring the same package at several versions (Flowise: three fast-xml-parser, five minimatch patches) had each version's edit treat its siblings' entries as its own stale wiring: override values were clobbered to the wrong tarball and packages/ snapshots rekeys spliced duplicated mapping keys — which pnpm hard-rejects (ERR_PNPM_BROKEN_LOCKFILE), discovered live when repair's reconstruction re-dispatched all versions in sequence. - EditCtx::is_ours / both is_ours_key block probes / the override classification + lock-side mirror check now require the vendor path's leaf to be THIS name-version.tgz (any uuid — stale-uuid refresh unchanged); sibling-version vendored entries are skipped as coexisting. - edit_packages/edit_snapshot_rekey fail closed when BOTH the registry-keyed and our file:-keyed entry exist (a half-edited lock): refusing beats splicing a duplicate key. - Regression tests: multi-version vendor coexistence (per-section duplicate-key audit), integrity-drift refresh stays single-keyed, half-drifted duplicate guard. Live-verified on Flowise end to end: scan --vendor (16/16) → pnpm install --frozen-lockfile → rm -rf .socket → repair (16/16 reconstructed from the lockfile alone) → frozen install again, exit 0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Single combined branch (supersedes #112, #113, #114 — all closed). Branch
feat/vendor-overhaulis checked out in the main repo for local testing;mainis fully merged in. 14 commits, reviewable commit-by-commit; each landed with its own tests green.Part 1 — Vendor UX fixes (was #112)
Driven by the Flowise
scan --vendorfailures (vendored 7/10,--prunenever cleaned vendored state):pkg:npm/%40scope/…); the crawler and vendor coordinate parsers now decode them — fixes "package not installed" for scoped packages in bothapplyandvendor, and stops--prunefrom seeing encoded manifest entries as always-prunable. Decoding happens after the/-and-@splits and before the path-safety guards (RED tests prove%2e%2e-style encoded traversal still rejects).vendor_content_mismatch_overwritten. Missing files still fail closed without--force. First vendor of an already-apply-patched package now reportsapplied(was a miscounted skip)."tar-fs": "3.1.0") is taken over in place — same key on both pnpm surfaces (map-parity), pinned value recorded as the wiringoriginal, every revert path restores it verbatim. Ranges/selector-chains still refuse.scan --prunenow reverts manifest-dropped vendored entries, reverts entries whose dependency left the lockfile graph (per-flavor in-use probes; detached exempt; undeterminable → keep) and drops their manifest entries, and sweeps orphan uuid dirs. JSONgcgainsrevertedVendoredEntries/removedVendorOrphanDirs(+ preview names).Part 2 — Lockfile-driven discovery + verified auto-fetch (was #113)
Makes
scan/scan --vendor/vendorwork on a completely fresh clone, across ecosystems:resolvedURL else conventional registry URL10c0/cache-zip checksum, rebuilt from the fetched tarball via the deterministicberry_ziprecipe.crateSOCKET_GOPROXY→GOPROXY→ proxy.golang.org),!caseescapingh1:dirhash in memory — algorithm validated against a live sum.golang.org lookupdist.url(zipball top dir stripped)dist.shasum(empty → never fetched){remote}/downloads/{gem}CHECKSUMSsha256 (bundler ≥ 2.6)py3-none-anyonly)Unverifiable entries are never fetched (
vendor_fetch_unverifiable→ calmpackage_not_installed); attempted-and-failed is a distinctvendor_fetch_failed; successes warnvendor_fetched_missing.--offlinerefuses calmly. Hardened download/extraction: http(s)-only, size/entry caps, traversal-guarded, exec bits preserved.Cargo.lock,go.sum,composer.lock,Gemfile.lock,uv.lock/poetry.lock/pinnedrequirements.txtand adds not-installed dependencies —[NOT INSTALLED]table marker + stderr note, JSONlockfileOnlyPackages+packages[].notInstalled, counted as scanned for--prune(wiped node_modules no longer prunes lockfile-listed entries), partitioned out of--applybefore download (calm skip, exit 0, no manifest writes), passed through to--vendor's auto-fetch. Vendored-ledger entries also stay discoverable pre-install.Part 3 — Mismatch-tolerant apply (was #114)
A file matching neither beforeHash nor afterHash previously hard-failed the in-place apply (the flatted@3.3.1 bad-baseline case). Now by default:
content_mismatch_overwritten) + Skipped warning event beside the package's Applied event,--download-mode diffmay not have staged them),--forceskips it),--strict(envSOCKET_STRICT) restores fail-closed;--forceoverrides it. Core:apply_package_patch'sforce: boolbecameMismatchPolicy { Warn | Strict | Force }.Test plan
cargo test --workspace --all-features: 101/102 suites green — the one red is the pre-existing non-blockingsetup_matrix_composeraspirational suite ("BASELINE GAP", untouched here).cargo clippy --workspace --all-featuresclean.scan --vendorwent from "Vendored 0; 2 failed; 1 refused" to 10/10 vendored;pnpm install --frozen-lockfileaccepts the takeover surgery; vendored flatted installs at afterHash 6/6.scan --vendor --yesdiscovers, fetches from registry.npmjs.org integrity-verified, vendors;npm ciinstalls the patched bytes; second run isalready_vendored.scan --apply --yesnow applies 2/2 (flatted warns + applies via blob; the%40-encoded SDK resolves);apply --strictexits 1 with the old error, files untouched.h1:dirhash verified against sum.golang.org forgolang.org/x/text@v0.14.0.Known upstream issue (not fixed here)
The flatted@3.3.1 patch record (uuid
5cac955f-eab1-4d29-8f4f-c408a6cc9647) has beforeHashes matching no published flatted artifact — to report to the patch team; this PR makes the CLI handle it gracefully either way.repair owns the vendored-artifact lifecycle (added 2026-06-12)
.socket/blobs|diffsor temp files — content is staged in memory (PatchSources.mem_blobs), and re-runs harvest it from the committed artifact itself. A vendored project's.socket/holds onlymanifest.json+vendor/.repairrebuilds missing/corrupt vendored artifacts (health = per-file afterHashes + the ledger's whole-file sha256) through the normal vendor backends — wired hot paths rebuild the artifact only, lockfiles stay byte-identical, the ledger entry is not re-recorded. Pristine sources: installed copy (offline-capable) → lockfile-verified registry fetch → the pre-vendor registry fragment recovered from the ledger's wiring originals..socketis gone entirely, the lockfile's.socket/vendor/<eco>/<uuid>/…references drive recovery — record from the manifest or the patch API (detached entry with the record embedded), pristine fetched unverified from the conventional registry and the deterministically REBUILT tarball verified against the integrity the rewired lockfile records (tamper ⇒ removed, fail).rebuiltaction +summary.rebuilt; error codesvendor_artifact_{missing,corrupt,rebuilt,rebuild_failed,unrepairable},vendor_uuid_mismatch.repairno longer errorsmanifest_not_foundwhen vendor traces exist.Live-verified on Flowise:
scan --vendor(16/16) →pnpm install --frozen-lockfile→rm -rf .socket→repair(16/16 reconstructed from the lockfile alone) → frozen install again, all exit 0.Out of scope (documented)
.npmrc/auth registry config (env overrides + lock-recorded URLs are the escape hatches); pipenv/pdm lock parsing; platform wheels / platform gems; npm v1 legacy locks; range-pin takeover.🤖 Generated with Claude Code