WS3: one lockfile model per ecosystem - #281
Conversation
clippy -D warnings rejects the dead before_hash/after_hash fields. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BYsX2VfVNKHvfFAJnFryc
formats::pnpm owns the pnpm lock grammar in every generation (shrinkwrap, 5.x block, 5.4/6.0/9.0 flow, Rush nested locks): PnpmLock::parse once, then entries(), resolves(), wired_refs(), wired_integrity(), vendored_in_use(), plan_hosted() and the restore_upstream() hook. The redirect rewriter's pnpm leg, the lock inventory, lockfile discovery, repair's integrity anchor and flavor sniff, the vendored v9/legacy planners and get's pnpm-PnP probe all read through it instead of their own walkers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BYsX2VfVNKHvfFAJnFryc
The hosted candidate list, repair's vendored-reference search space, lockfile discovery's vendored-liveness probe, the in-memory engine's root markers and file ecosystems, the npm flavor probe guard and pnpm detection now filter one table instead of keeping five parallel lists. The hosted candidate order is pinned by value. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BYsX2VfVNKHvfFAJnFryc
formats::cargo owns the lock read model (LockedPackage, v1 [metadata] checksums, [[patch.unused]], dependency references) behind CargoLock: entries() for the inventory, packages() for lockfile discovery and the vendor probes, dependents() for the hosted planner's unpinnable-dependents refusal (which re-parsed the lock with its own walker), vendored_in_use() for the vendored-copy claim, and the restore_upstream() hook. The hosted planner's Cargo.lock splice moves to formats::cargo::hosted with the line-grammar probes the rewriter reads with (is_locked, locked_versions), replacing three copies of the header probe. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BYsX2VfVNKHvfFAJnFryc
formats::composer owns the entry walk (ComposerLockPackage, now with its ownership gate wired_to) behind ComposerLock: entries() for the inventory and packages() for lockfile discovery and the vendored backend. The vendored backend's entry_is_wired and repair's recorded-fragment reader, which read dist fields straight off the JSON, go through the entry model. The hosted planner's byte scanner moves verbatim to formats::composer::hosted; its equivalence oracle stays green. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BYsX2VfVNKHvfFAJnFryc
vendor::gemfile_lock becomes formats::gem: GemfileLock::parse plus entries() for the inventory (moved out of lock_inventory::gem, which keeps only its view I/O and ledger recovery's remote set), the restore_upstream() hook, and gem_download_url. The hosted planner's lock-source convergence moves verbatim to formats::gem::hosted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BYsX2VfVNKHvfFAJnFryc
The blob-retention test that reads before_hash/after_hash is #[cfg(target_os = "macos")]; allow the fields as dead elsewhere instead of dropping them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BYsX2VfVNKHvfFAJnFryc
|
CI status on 1c849f3 — one failure was this PR's, now fixed; the rest are inherited from
I have no job re-run available beyond the push itself, so 9c078fa is the re-run. Generated by Claude Code |
formats::yarn owns the one yarn.lock grammar decision: sniff_grammar (the head sniff the vendor flavor probe, the lock-inventory view and repair's reference flavor each re-derived) and is_berry_lock (the whole-file check the hosted rewriters and discovery share; the classic vendored backend's refusal gate now uses it too, so a BOM'd berry lock no longer slips past it). formats::bun::BunTextLock is the gate + split + packages parse five bun.lock readers copied; each keeps its own refusal wording. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BYsX2VfVNKHvfFAJnFryc
wired_vendor_integrity scanned yarn.lock and bun.lock with a six-line forward window from any line naming the artifact. It now reads the entry models lockfile discovery uses: live classic blocks' integrity, berry checksum (yarn 4.0.x bare hex promoted under cacheKey 10c0), and bun's tarball tuple. That fixes a berry block whose carried dependencies pushed checksum out of the window (no anchor), a bare-hex checksum (no anchor), a shadowed classic block being read, and bun's digest-less re-save borrowing the next package's sha512 (a wrong anchor). Entries that disagree yield no anchor. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BYsX2VfVNKHvfFAJnFryc
Mikola Lysenko (mikolalysenko)
left a comment
There was a problem hiding this comment.
v5 review at 9c078faa — the shared format registry is useful, but this remains an intermediate consolidation. I did not confirm a new functional regression in the moved code.
Please make “one model / parse once” the implementation boundary, not just the directory layout:
PnpmLock::vendored_in_use()walks the text again after parsing; Cargo still has separate TOML and hosted text grammars. Inventory, wiring, forward rewrites and upstream restoration should use one parsed representation with byte spans, preserving CRLF and existing format support.formats::LockModelcurrently exposesFORMATplus a default unsupportedrestore_upstream(), while #280 adds the actual inverse parsers elsewhere. Wire one real format end to end before keeping a public placeholder abstraction beside a second parser family.- There is a reproducible inherited Gem checksum bug worth closing here. The reader accepts uppercase digests/additional digest tokens, but the hosted writer at
patch/redirect/mod.rs:5063–5070only matches a lowercase terminalsha256. Its insertion fallback then adds a second conflicting checksum. A probe of the unchanged writer produced one row for lowercase, two for uppercase, and two forsha256 + sha512, with no warnings. This is already present in #277, not introduced by this PR. Use the shared Gem checksum parser in the writer and add those round-trip fixtures.
That last case is the practical payoff of consolidation: a format should not be accepted by discovery but misread by its writer. Keep all supported package-manager versions; remove duplicate grammars rather than compatibility coverage.
Validation: diff/source review and an isolated Rust checksum probe; no full workspace test run.
The hosted writer found the dep's CHECKSUMS row with a regex that only matched a lowercase terminal sha256, while the discovery reader accepts uppercase digests, extra digest tokens and bare entries. Any of those fell through to the insert branch and added a second, conflicting row (which the reader then treats as no pin). The writer now locates the entry with formats::gem's split_checksum_entry inside the CHECKSUMS section and replaces that row in place, keeping its line ending and recording the old row verbatim for revert. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BYsX2VfVNKHvfFAJnFryc
PnpmLock computes its vendored-uuid set at parse time with one walk over the packages/snapshots block keys (the vendored planners' key grammar, each line's \r dropped). pnpm_entry_in_use memoizes the same set per lock bytes instead of keeping LockIndex's copy and the LF-only vendored_in_use_lines scan. A CRLF lock now answers like its LF twin (in use) instead of undeterminable; the unwired-revert guard still refuses. formats::LockModel with its default-unsupported restore_upstream() is removed: hosted upstream restoration belongs to the ledger-free hosted workstream and should land on these models, not beside them as a placeholder. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BYsX2VfVNKHvfFAJnFryc
|
Thanks for the review. Pushed as of 9301a39:
Not done yet: one span-carrying representation for cargo (and likewise pnpm's hosted span grammar vs. the vendored planners'
The known behavior change: non-canonical locks (comments or reordered keys inside a block), which the text grammar reports as Generated by Claude Code |
vendor::maven_pom becomes formats::maven and nuget_config's routing reader becomes formats::nuget; the NuGet config file names and the stat-only same-file check stay in vendor::nuget_config with the callers' I/O. Lockfile discovery, their only reader, imports the models directly; the purity guards follow the files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BYsX2VfVNKHvfFAJnFryc
CargoLock::parse now reads the lock with toml_edit's spanned Document and records each [[package]]'s header, version/source/checksum value spans and string values, every table header, the [root] strings and the [metadata] lines. The hosted planner (CargoLock::plan_hosted) splices at those spans, and the rewriter's is_locked / locked_versions probes answer from the same parse, so the separate `[[package]]\nname = ...` text grammar, its regexes and block walkers are gone. The previous line-grammar planner is kept test-only as the oracle: randomized v1 and v3/v4 locks (plus twins, [root], sourceless v1, bare blocks, 1.2k-block locks) plan to identical bytes and FileEdits for every package, a re-run, and a second package over the result. The one allowed difference is a lock the old grammar could not read (a final block with no newline after `version`), which the span planner now finds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BYsX2VfVNKHvfFAJnFryc
|
Restack request from the v5 coordinator (please act now). #280 (ledger-free hosted) is now merged into
|
|
Owner decision (via coordinator): land the families done so far. The cargo span model and the remaining 'parse once' work become follow-up PRs, since the review items are design refinements, not regressions. Priority now: merge the base (#280), resolve conflicts, get green, and mark ready. |
…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
|
Ready for landing. Head: Restack. The three conflicts were resolved toward #280's model, with no ledger or fragment-replay paths reintroduced:
#280's new
CI on
Locally
Follow-ups are listed in the PR body, including F09 and F16 from the #286 review. Generated by Claude Code |
|
#283 landed on release/v5-prerelease as 06437d2; please merge origin/release/v5-prerelease again, resolve conflicts, get green, and keep it ready. Generated by Claude Code |
…models #283 deletes commands/repair_vendor.rs (this branch's edits there go with it: its flavor sniffs were removed upstream) and moves the vendored wiring list into vendored_backend::repair, which now derives it from formats::registry's VENDORED files as the deleted copy did. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BYsX2VfVNKHvfFAJnFryc
|
Merged
Generated by Claude Code |
|
Bun
Every failing run looks the same:
It is an intermittent macOS runner/harness problem in Generated by Claude Code |
|
CI finished on
Generated by Claude Code |
|
I merged The vlt
This isn't caused by this PR: #279's run 36452380239 on the same base fails the same five tests with the same error on the same versions, and #279 doesn't touch lock models. Neither Generated by Claude Code |
|
v5 coordinator: #281 is next to land, but these block it.
When both are done this PR lands on the next coordinator run. Generated by Claude Code |
|
Merged 28cebf7 is the base's fix for both reds on
Checks on
I'll post a CI summary once Generated by Claude Code |
|
The The failing suite is
Why I'm not proposing a patch yet: I tried making Generated by Claude Code |
|
CI settled on The only reds are ones the base also fails. Each is listed below with the base run that shows the same failure. Green
Red, also red on the base
Locally on this head
Generated by Claude Code |
73c0c4f
into
release/v5-prerelease
Brings in WS3 (#281, one lockfile model per ecosystem). It merges cleanly with the rollout; scan, rollout, policy and memory suites pass unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Brings in #281 (one lockfile model per ecosystem). No conflicts; clippy and the policy, memory, parity and scan suites pass. Claude-Session: https://claude.ai/code/session_01BKsyzefGhAnPkYmXCwq3H3 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Keep this branch's deletion of the setup-only package_json module (#281 had only repointed find.rs at the format registry), and take #281's registry-backed npm_family in constants.rs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PQaKzoW5dSw9u5pgAAvVRj
…into v5/one-hosted-engine #281's format-registry changes to the hosted flow land where this branch moved that code: core hosted::engine derives REDIRECT_CANDIDATE_FILES and file_ecosystem from formats::registry, hosted::guidance re-exports the pnpm lock-version sniffs from formats::pnpm, and the in-memory root detection reads registry::root_marker. The CLI hosted_memory redirect.rs #281 edited is already gone on this branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KHZ8uzdXfkG2zH8ZYDG8ju
Implements the landed part of WS3 in
docs/design/v5-plan.md: one pure model per lock format undercrates/socket-patch-core/src/formats/<fmt>/, plus oneformats::registry()table of which files carry wiring. The branch includesrelease/v5-prerelease(#280, ledger-free hosted) as of b0a6aa8. Per the owner's decision, the remaining WS3 items are follow-ups, listed below.What lands
pnpm (
formats::pnpm):PnpmLockcovers every lock generation (shrinkwrap, 5.x block, 5.4/6.0/9.0 flow, Rush). It backs:entries(),resolves(),wired_refs()andwired_integrity();vendored_in_use(), computed at parse time and CRLF-tolerant;plan_hosted().The redirect rewriter, lock inventory, lockfile discovery, repair, both vendored planners and get's pnpm-PnP probe all read through it.
registry (
formats::registry()): one table, which the following now derive from:REDIRECT_CANDIDATE_FILES(pinned by value);WIRING_FILES;npm_family::FILES.cargo lock (
formats::cargo):CargoLock::parseis the one parse. It uses toml_edit's spannedDocumentand backs:entries(),packages(),dependents(),vendored_in_use(),is_locked()andlocked_versions();plan_hosted(), which splices at the recorded spans.The separate text grammar is deleted. An oracle test compares the old planner, kept test-only, with the new one. On randomized v1 and v3/v4 locks both produce identical bytes and
FileEdits. v5 WS1+WS2: ledger-free hosted mode, upstream-restore rollback, vendor ejects hosted projects #280's upstream Cargo.lock restore now splices at the same spans.composer (
formats::composer):ComposerLock/ComposerLockPackage. The hosted scanner moves toformats::composer::hostedverbatim.gem (
formats::gem):GemfileLockplus the hosted lock-source convergence. Fixes the inherited CHECKSUMS writer bug: an uppercase, multi-digest or bare row used to get a second, conflicting row. It is now replaced in place, with round-trip tests for LF and CRLF.yarn/bun (
formats::yarn,formats::bun):is_berry_lock, which also closes a BOM gap;BunTextLockprelude;maven/nuget readers moved to
formats::mavenandformats::nuget. v5 WS1+WS2: ledger-free hosted mode, upstream-restore rollback, vendor ejects hosted projects #280's upstream restore imports them from there.The placeholder
LockModel::restore_upstream()has been removed; upstream restoration is v5 WS1+WS2: ledger-free hosted mode, upstream-restore rollback, vendor ejects hosted projects #280's.Validation (b0a6aa8)
cargo clippy --workspace --all-targets --all-features -- -D warnings: clean.cargo test --workspace --all-features --no-fail-fast -j4: 10,218 passed. The 20 failures are all write-failure or chmod tests that cannot fail when run as root in the sandbox. The same set fails on the base branch.e2e_redirect_cargo_buildnow passes (fixed by v5 WS1+WS2: ledger-free hosted mode, upstream-restore rollback, vendor ejects hosted projects #280).install-proof(vlt_pinned_matrix_agent_get_and_remove), fixed on v5: removesetup(WS7) + patch UI streamlining (WS8) #279;covgap_commands_scan_mod.Follow-ups (not in this PR)
CargoRegistryPinsvs discovery'sdependency_entries, and four[package]readers.lines::grammar are still two grammars.formats::golang.nuget_package_source_keysvsparse_config_source_keysfind_maven_dependency_matchesvsparse_pomrewrite_nuget's lock walk vsnuget_lock_entriespatch::redirect::upstream/*builds a second per-format grammar outsideformats/. Its restore readers should move onto the format models; cargo is done here.🤖 Generated with Claude Code
https://claude.ai/code/session_018BYsX2VfVNKHvfFAJnFryc
Note
Medium Risk
Large refactor of lock parsing, hosted redirects, and install narrowing; behavior is intended to be equivalent but mistakes in resolution or file lists could mis-route patches or skip grants.
Overview
Introduces
socket-patch-core/src/formats/as the single place for lock/wiring knowledge: per-ecosystem models (notablyPnpmLock,CargoLock,ComposerLock, extendedGemfileLock, plus yarn/bun sniff helpers) and a sharedformats::registry()table of which project files are hosted redirect candidates, vendor wiring targets, root markers, and npm flavor probes.CLI and in-memory hosted stop maintaining parallel file lists and ad-hoc sniffs:
REDIRECT_CANDIDATE_FILESand repair’s wiring scan come fromregistry::HOSTED/VENDORED; pnpm trust-policy helpers and yarn/pnpm grammar detection are re-exported or delegated to the format modules.get’s pnpm-PnP hosted narrowing drops the localpnpm_lock_resolvestext probe in favor ofPnpmLock::parse+resolves()(tests move to core).Hosted redirect wires pnpm through
formats::pnpm::plan_hostedinstead of an in-redirectrewrite_pnpm_lock; cargo/composer/gem hosted logic is relocated or rebuilt on spanned/byte-accurate models (cargo gets a unifiedCargoLock::plan_hosted; composer hosted scanner lives underformats::composer::hosted). The oldnpm_family::FILEStable inconstantsis removed in favor of registry role flags, with a pinned-value test guarding the hosted candidate list.Reviewed by Cursor Bugbot for commit b0a6aa8. Configure here.