GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — Decide vendored-entry liveness through one discovery verdict by mikolalysenko · Pull Request #1050 · SocketDev/socket-patch Conversation
|
| target | passed |
|---|---|
--lib |
871 |
vendor_jvm_cli |
30 |
covgap_commands_scan_mod |
53 |
covgap_commands_vendor |
54 |
scan |
118 |
repair |
126 |
rollback |
38 |
vendor |
91 |
e2e_vex_lockfile |
323 |
in_process_vendor |
121 |
scan_vendor_e2e |
37 |
scan_vendor_requirements_unwired |
3 |
vendor_eject |
8 |
All green on head.
cargo clippy -p socket-patch-core -p socket-patch-cli --all-targets: no diagnostics in any file this PR touches. Toolchain 1.93.1 still flags pre-existing lints elsewhere (jvm_jar.rs, python_crawler.rs, redirect/mod.rs, test helpers), and those are on main too.
Keep-side coverage for every newly prunable ecosystem. Each successful, non-dry-run vendor through the vendor::test_support wrappers now asserts that the GC verdict on the entry it just wrote is not Some(false). In vendor::, that ran against real backend output for every ecosystem and flavor, with every verdict Some(true) (count per ecosystem/flavor):
| ecosystem / flavor | runs |
|---|---|
| cargo | 70 |
| composer | 45 |
| gem | 76 |
| golang | 23 |
| maven | 43 |
| nuget | 42 |
| jvm | 1 |
| npm (package-lock, bun, pnpm, pnpm-legacy, vlt, yarn-berry, yarn-classic) | 535 |
| pypi requirements | 44 |
| pypi uv | 13 |
| pypi poetry | 14 |
| pypi pdm | 23 |
| pypi pipenv | 11 |
| pypi hatch | 3 |
| pypi python-lock | 3 |
Regression tests, failing first.
vendor::npm_flavor::tests::unattributable_wiring_stays_in_use covers the v2 legacy mirror, a v3 nested git copy and a vlt other instance. With the new rule disabled it fails: left: Some(false), right: Some(true).
gc_tests::vendor_gc_keeps_an_entry_whose_wiring_is_only_unattributable: run_vendor_gc keeps a legacy-mirror entry, dry and wet. It fails with the rule disabled.
vex::discover::tests::lockless_pyproject_entries_stay_undecidable pins None.
Run on the merge-base c5be5d1 (test hunks only, in a throwaway worktree), these three failed as expected:
vendor_gc_reclaims_unused_composer_entry_and_keeps_a_wired_one (B19): unused_reverted was [].ledger_supplement_reports_a_bumped_composer_entry_unwired (B19): the entry was resurrected into packages.check_reports_jvm_trees_without_a_jvm_entry_beside_other_entries (B62): no JVM orphan event; only the npm entry's own failure.The B62 test now also asserts that the event's path is an existing LEDGER_OWNED_PATHS member.
commented_out_repository_is_not_wired and commented_out_source_is_not_wired (B61) fail with the old substring checks restored.
Linux and docker suites are left to CI.
scan_vendor_references (orphan sweeps, repair, rollback's ledger-less gate, vendor --check's unknown-wiring scan) is still a separate raw-text scan. Scan every vendored-write file for vendored references (#832, #958) #1015 (Vendored-reference scan never sees NuGet or Maven wiring, so the orphan sweep deletes a still-wired unit #832, Vendored-reference scan never reads hatch.toml, so the orphan sweep deletes a wheel that a Hatch environment still installs #958) is extending it now. Retiring it into discovery is the follow-up once Scan every vendored-write file for vendored references (#832, #958) #1015 merges. This PR touches neither repair.rs nor registry.rs.Discovery::vendor_entry_in_use. The pnpm part needs coordination with Fix 15 open pnpm issues across hosted, vendored and agent modes #1007's owner.entry_wired) from the reference check plus a layout-intact check, together with a single jvm::layout module and per-GAV JVM orphan detection (a tree dir with no matching JVM entry while other JVM entries exist).withheld and the unattributable rule match on uuid and mode. A uuid names one patch, so one package version, so matching on base_purl too would add nothing today.socket-patch-<uuid> key. Deciding "ours" by the source value (NuGet scan --mode vendored over a hosted pin skips the takeover (purl case mismatch), keeps the hosted feed wired, writes no vendor ledger and still reports success #553) is not addressed here.ecosystem_in_scope does not map "jvm" to maven (a separate audit finding).🤖 Generated with Claude Code
Note
High Risk
Changes prune GC, vendor --check, and scan supplement behavior across all ecosystems; incorrect liveness could revert live patches or leave stale ledger entries.
Overview
Unifies vendored-ledger liveness behind Discovery::vendor_entry_in_use, replacing separate per-ecosystem probes (dispatch_in_use_one, npm/cargo/pypi vendored_entry_in_use) so scan --prune, the vendored ledger supplement, and vendor --check share one verdict.
Discovery now records which lockfiles were read (Discovery::read) and cargo relock-pending wiring (withheld), and treats unattributable attestation drops and contested locks as keep (fail-safe) rather than unused. The prune GC loads one discovery snapshot before reverting entries so half-pruned locks cannot skew later probes.
Behavior fixes: Composer and other previously unprobed ecosystems can be reclaimed when install-deciding locks no longer wire them; JVM orphan detection runs when the ledger has no JVM entry (not only when empty); Maven/NuGet commented-out repository/source blocks no longer count as wired for the in-sync vendor path.
Vendor test helpers assert a freshly vendored entry is never Some(false) for the GC verdict.
Reviewed by Cursor Bugbot for commit e9bd805. Configure here.
Generated by Claude Code
Sorry, something went wrong.
There was an error while loading. Please reload this page.
The prune GC and scan's ledger supplement asked a per-backend probe (npm, cargo, pypi-requirements only) whether a vendored entry is still consumed, while vex and vendor --check judge liveness by discovery. Give discovery the evidence for a tri-state in-use answer for every ecosystem: - `Discovery::read` logs every guarded read, tagged with the ecosystem whose extractor read it, so "nothing wires it" can mean "unused" only once a lockfile of that ecosystem was actually read and parsed. - `Discovery::withheld` records wiring an extractor withholds as a ref although a file still routes through the patch: npm's and bun's in-lock contests and cargo copies whose lock lags (another tag, or an untagged override). Not attested, but still wiring the GC must keep. - `vendor_entry_in_use` = live, contested or withheld -> Some(true); a read lockfile of the ecosystem and nothing wiring it -> Some(false); no lockfile, or one that cannot be read or parsed -> None. JVM entries answer from their own layout (`entry_references`). Audit B19 (§3.B "is this vendored entry live"). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…re readers The vendored Maven backend treated any occurrence of its repository id in pom.xml as wiring, and the NuGet backend any occurrence of its source key in nuget.config, so a commented-out (or profile-scoped) repository or source took the in-sync hot path and the build resolved the unpatched package. Use the same masking the forward writers already use: `find_wireable_anchor` (comments and <profiles> masked) for the pom, and `parse_config_source_keys(blank_comments(..))` for the nuget.config <packageSources>. Audit B61. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
`vendor --check` reported committed JVM trees (maven2, gradle, coursier, their indexes, the generated sbt file) as `vendor_ledger_missing` only when the whole ledger was empty, so a project that also vendors another ecosystem never noticed a lost JVM half. Fire whenever no ledger entry is a JVM entry: an empty ledger still refuses as before, otherwise the orphan is recorded as a failed artifact event beside the other checks. The path list moves to one `jvm::apply::LEDGER_OWNED_PATHS` (replacing the CLI's inline list and `coursier_tree::ORPHAN_PATHS`), and the JVM path tables spell the tree roots through their named constants. Audit B62. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…gh one in-use verdict `dispatch_in_use_one` answered "is this vendored entry still consumed" for npm, cargo and the pypi requirements flavor only and kept every other entry (gem, golang, composer, nuget, maven/jvm, uv, poetry, pdm, pipenv, hatch, pylock) forever: `scan --prune` could never reclaim them and scan's ledger supplement resurrected them as discovered packages, never reporting `vendor_ledger_entry_unwired`, while `vendor --check` and vex already called them dead. Every caller now asks `Discovery::vendor_entry_in_use`: - run_vendor_gc (pass b) takes all verdicts from one discovery of the project before it reverts anything; - vendored_ledger_supplement reads the run's ProjectContext discovery (scan and get pass their context); - vendor --check's "dependency removed" remedy. Deleted: `dispatch_in_use_one` (CLI) and the per-backend dispatchers `vendor::npm_flavor::vendored_entry_in_use`, `vendor::cargo::vendored_entry_in_use`, `vendor::pypi::vendored_entry_in_use` and `pypi_requirements::requirements_entry_in_use` (4 in-use oracles -> 1; the pnpm/vlt structural probes stay for their revert guards). Their tests now assert the discovery verdict, with lock fixtures carrying the version an install records. Audit B19. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…ibutable The prune GC's in-use verdict read every vendored ref that attestation dropped as unused unless an extractor had recorded it as withheld, and only npm's in-pair contest, bun and cargo did. Every other unattributable drop (an unpatched copy in the same lock, npm's legacy `dependencies` mirror, a non-registry nested copy, vlt's other instances and bundled copies, a yarn git block, a version-less Go replace) made `scan --prune` unwire a vendored patch the package manager still installs. `vendor_entry_in_use` now keeps an entry whenever a file that mentions its uuid also carries a DIAG_REF_UNATTRIBUTABLE diagnostic. That one rule covers every extractor, so the npm and bun `withheld` pushes are gone; cargo's relock-pending record stays because cargo reports that case as DIAG_REF_INVALID. Mentions rejected as invalid (stale bun.lockb pool strings, vlt edge specs, cargo's leftover [patch] after a hosted takeover) still read as unused. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…e GC Each successful, non-dry-run vendor through the test_support wrappers now asks discovery's in-use verdict about the entry it just wrote and fails on Some(false). Every backend suite (npm flavors, pnpm, yarn, bun, vlt, cargo, composer, gem, golang, maven, nuget and every pypi flavor) thus pins the keep side of the GC against its real output. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
The B62 test now checks that the vendor_ledger_missing event names a LEDGER_OWNED_PATHS member that exists, not just any maven-tagged event. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
Bugbot Autofix prepared fixes for both issues found in the latest run.
Or push these changes by commenting:
@cursor push 002ca36d71
diff --git a/crates/socket-patch-core/src/vendor/jvm/apply.rs b/crates/socket-patch-core/src/vendor/jvm/apply.rs --- a/crates/socket-patch-core/src/vendor/jvm/apply.rs +++ b/crates/socket-patch-core/src/vendor/jvm/apply.rs @@ -36,13 +36,20 @@ }; /// Whether `entry` was written by this backend: it has wiring and every -/// record is one of this backend's kinds. +/// record is one of this backend's kinds, OR it's a maven-ecosystem entry +/// with a valid maven purl (covers empty-wiring reconstructed entries). pub fn is_jvm_entry(entry: &VendorEntry) -> bool { - !entry.wiring.is_empty() + if !entry.wiring.is_empty() && entry .wiring .iter() .all(|w| KINDS.contains(&w.kind.as_str())) + { + return true; + } + // Empty-wiring entries (e.g., from repair/reconstruction) that are + // maven-ecosystem with valid maven purls should still be treated as JVM entries + entry.ecosystem == "maven" && parse_maven_purl(&entry.base_purl).is_some() } /// Offline inputs have not been authenticated by independent registry checksums. @@ -827,7 +834,7 @@ } let (maven, gradle) = sides(&entry.wiring); (maven && maven_reactor::wired_checked(&read, &c).unwrap_or(true)) - || (gradle && gradle::references(&read, &c)) + || (gradle && gradle::references_checked(&read, &c).unwrap_or(true)) } /// The liveness proof `vex` needs: every half of the entry is wired, and diff --git a/crates/socket-patch-core/src/vendor/jvm/gradle.rs b/crates/socket-patch-core/src/vendor/jvm/gradle.rs --- a/crates/socket-patch-core/src/vendor/jvm/gradle.rs +++ b/crates/socket-patch-core/src/vendor/jvm/gradle.rs @@ -1197,6 +1197,42 @@ indexed && applied } +/// Checked variant of [`references`] that returns an error when files +/// cannot be read, for fail-closed GC ([`crate::vex::discover::Discovery::vendor_entry_in_use`]). +pub fn references_checked(read: ReadFn<'_>, c: &Coords<'_>) -> Result<bool, String> { + let gav = format!("{}:{}:{}", c.group_id, c.artifact_id, c.version); + let index_bytes = read(INDEX_REL).ok_or_else(|| format!("cannot read {}", INDEX_REL))?; + let index_text = String::from_utf8(index_bytes) + .map_err(|_| format!("{} is not valid UTF-8", INDEX_REL))?; + let rows = index_rows(&index_text) + .ok_or_else(|| format!("{} could not be parsed", INDEX_REL))?; + let indexed = rows.iter().any(|r| { + let cols: Vec<&str> = r.split('\t').collect(); + cols.first() == Some(&gav.as_str()) && cols.get(3) == Some(&c.uuid) + }); + + let wiring = WiringTarget::vendored(); + let mut applied = false; + let mut settings_found = false; + for rel in ["settings.gradle", "settings.gradle.kts"] { + if let Some(bytes) = read(rel) { + settings_found = true; + let text = String::from_utf8(bytes) + .map_err(|_| format!("{} is not valid UTF-8", rel))?; + let dsl = dsl::dsl_of(rel).unwrap_or(Dsl::Groovy); + if has_apply_line(&text, dsl, &wiring, "") { + applied = true; + break; + } + } + } + if !settings_found { + return Err("cannot read settings.gradle or settings.gradle.kts".to_string()); + } + + Ok(indexed && applied) +} + /// The liveness proof `vex` needs for this layout: `c` is /// [`references`]d, the script is ours (line endings aside), and a re-plan /// over the committed tree is refused nowhere and degraded nowhere.
You can send follow-ups to the cloud agent here.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
`scan --prune` reverts a JVM ledger entry when `entry_references` answers false, but the Gradle arm returned `gradle::references` as is: a malformed gradle-index.tsv, a non-UTF-8 settings file, or a file the reader could not read (permission error, link out of the checkout) all read as "not referenced", so the GC could delete a tree that is still wired. The maven/sbt/scala-cli arms already map undecidable to true. Add `gradle::references_checked`, which answers None when a file it decides by exists but cannot be parsed, and have `entry_references` treat that, and any read error the ProjectReader recorded, as still in use. `gradle::references` keeps its old answers for revert and attestation. Also pin with a test that a hand-stripped, empty-wiring `jvm` entry stays undecidable (None) for the prune GC. Co-Authored-By: Claude <[email protected]>
|
bugbot run Generated by Claude Code |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem 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 e9bd805. Configure here.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
[agent] Ready for review at
Generated by Claude Code |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Conflicts were main's rustfmt-only reflows of code this branch rewrote (vendored_ledger_supplement test calls now take a ProjectContext, dispatch_in_use_one moved behind the shared liveness probe, cargo vendored_from_patches keeps the relock_pending claim); kept this branch's versions. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Pick up #1093 so PR CI runs without the macOS legs. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Resolve golden.rs Discovery destructure: keep this PR's read/withheld fields and main's unwired_copies (#1033). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
There was an error while loading. Please reload this page.
Brings in #1044 (governing lock table), #1035, #1038, #1083, #724. Drop the BUN_LOCK/BUN_LOCKB/NPM_LOCKS imports #1044 added to npm_flavor.rs: their only user, vendored_entry_in_use, is removed by this PR (liveness now comes from Discovery::vendor_entry_in_use). This unused import failed clippy in the merge group. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Resolve conflicts with #1050 (vendored-entry liveness): keep its fail-closed Gradle references_checked and ledger-owned orphan check, and route both through vendor::jvm::layout (LEDGER_OWNED_PATHS replaces ORPHAN_PATHS there, re-exported from jvm::apply; settings files come from layout::GRADLE_SETTINGS_FILES). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
maven_repo.rs: keep this PR's deletion of the legacy same-GAV <repository> backend; #1050's B61 change (comment-aware "already wired" check via find_wireable_anchor) and its test only touched that deleted backend. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Resolve the conflicts with main's move of vendored in-use detection into discovery (#1050) and its PurlKey rename (#1045): - npm_flavor.rs: drop the branch's vendored_entry_in_use, now that Discovery::vendor_entry_in_use replaces it. Bun discovery already picks the live lock through bun_text_lock_drives, so the #735 regression test now runs through the shared in_use helper with a minimist entry. - lock_inventory/tests.rs: keep both sides' new tests. - vendor.rs: key the Bun reinstall dedupe on PurlKey. Assisted-by: Claude Code:claude-opus-5-5
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01KWAYXWPgb6FHP1jeYTCPcA
Resolve the three conflicts by keeping both sides: - scan/policy.rs: keep the PR's dir_markers/lock_markers split (lock markers decide nested roots for #554) and re-apply main's #1032 change inside dir_markers: the JVM manifest fallback now reads vendor::jvm::layout::JVM_PROJECT_MARKERS, with main's is_file comment. - vex/discover/mod.rs: Discovery keeps the PR's `shadowed` refs (#828) alongside main's `read` and `withheld` evidence (#1050). Shadowed refs carry DIAG_REF_UNATTRIBUTABLE, so vendor_entry_in_use already keeps their entries through unattributable_mention. - vex/discover/testing/golden.rs: destructure all three fields. Co-Authored-By: Claude <[email protected]>
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.