[agent] Filed by the scheduled architecture audit routine (ecosystems and formats). Register: discussion #560 register.
Kind: refactor. Source: Part 6.2 ("fabricated CrawledPackages"); register E36, child 1 of #1112.
Problem
scan turns lock-inventory entries and vendor-ledger entries into fake installed packages:
scan/discovery.rs#L111-L133 crawled_from_purl sets path: cwd.join("node_modules").join(name_part) for every ecosystem. A lockfile-only pkg:cargo/serde@1.0.0 or pkg:pypi/six@1.16.0 gets <cwd>/node_modules/serde or <cwd>/node_modules/six.
- It is called by
lockfile_supplement (#L88) and vendored_ledger_supplement (#L215).``
Because the paths are fake, the caller keeps parallel sets that say which entries to distrust:
supplement_purls (scan/mod.rs#L1807-L1835),`` which exists only so the PATH-scope filter (#L1926-L1962) can skip them;
lockfile_only.purls, consulted through lockfile_only_contains at scan/mod.rs 801, 2821 and 3121 and at discovery.rs 299.
Every new consumer of all_crawled that reads .path (for example gradle_cache::installed_copies(&pkg.path, …) in preverify_vendor_baselines, or is_gradle_version_dir(&p.path) at scan/mod.rs 1206) has to remember that some paths are placeholders. Today these happen to degrade safely, because Maven and Gradle never reach the inventory.
Proposed change
- Add a scan-local candidate type, for example:
enum Candidate { Installed(CrawledPackage), LockOnly { purl, eco }, Vendored { purl, eco } }
Alternatively, keep CrawledPackage for crawler output only and carry the supplement as purl-only records beside it.
- The PATH-scope filter matches
Installed only. "Not installed" is matches!(c, Candidate::LockOnly { .. }).
- Delete:
crawled_from_purl, supplement_purls and the LockfileSupplement::packages / LedgerSupplement::packages vectors of fabricated packages.
- Keep
lockfile_only.purls only where API-spelled purls are matched (lockfile_only_contains bridges the percent-encoding and composer padding). Where the candidate itself is at hand, derive the answer from the candidate.
- No behavior change: same JSON (
lockfileOnlyPackages, notInstalled, the [NOT INSTALLED] marker, path_scope_excluded_supplements), same exit codes.
Size and scope
crates/socket-patch-cli/src/commands/scan/{mod.rs,discovery.rs}, plus any scan helper typed on &[CrawledPackage] that receives supplements. Estimate about 150–300 changed production lines.
- Out of scope: the core inventory model (later children of the tracking issue),
apply.rs's own lockfile_resolved set, and crawler changes.
Acceptance criteria
Dependencies
Backlog review — 2026-10-08
Consolidated into #1112. The retained tracker(s) preserve this issue’s implementation scope and acceptance criteria. Closing this separate scheduling item as not planned, not as completed.
Explicit candidate-type child of the unified inventory tracker; one parent issue is enough to schedule it.
[agent] Filed by the scheduled architecture audit routine (ecosystems and formats). Register: discussion #560 register.
Kind: refactor. Source: Part 6.2 ("fabricated
CrawledPackages"); register E36, child 1 of #1112.Problem
scanturns lock-inventory entries and vendor-ledger entries into fake installed packages:scan/discovery.rs#L111-L133crawled_from_purlsetspath: cwd.join("node_modules").join(name_part)for every ecosystem. A lockfile-onlypkg:cargo/serde@1.0.0orpkg:pypi/six@1.16.0gets<cwd>/node_modules/serdeor<cwd>/node_modules/six.lockfile_supplement(#L88) andvendored_ledger_supplement(#L215).``Because the paths are fake, the caller keeps parallel sets that say which entries to distrust:
supplement_purls(scan/mod.rs#L1807-L1835),`` which exists only so the PATH-scope filter (#L1926-L1962) can skip them;lockfile_only.purls, consulted throughlockfile_only_containsatscan/mod.rs801, 2821 and 3121 and atdiscovery.rs299.Every new consumer of
all_crawledthat reads.path(for examplegradle_cache::installed_copies(&pkg.path, …)inpreverify_vendor_baselines, oris_gradle_version_dir(&p.path)atscan/mod.rs1206) has to remember that some paths are placeholders. Today these happen to degrade safely, because Maven and Gradle never reach the inventory.Proposed change
CrawledPackagefor crawler output only and carry the supplement aspurl-only records beside it.Installedonly. "Not installed" ismatches!(c, Candidate::LockOnly { .. }).crawled_from_purl,supplement_purlsand theLockfileSupplement::packages/LedgerSupplement::packagesvectors of fabricated packages.lockfile_only.purlsonly where API-spelled purls are matched (lockfile_only_containsbridges the percent-encoding and composer padding). Where the candidate itself is at hand, derive the answer from the candidate.lockfileOnlyPackages,notInstalled, the[NOT INSTALLED]marker,path_scope_excluded_supplements), same exit codes.Size and scope
crates/socket-patch-cli/src/commands/scan/{mod.rs,discovery.rs}, plus any scan helper typed on&[CrawledPackage]that receives supplements. Estimate about 150–300 changed production lines.apply.rs's ownlockfile_resolvedset, and crawler changes.Acceptance criteria
crawled_from_purlandsupplement_purlsare deleted, and noCrawledPackageis constructed incommands/scan/outside tests.scan/discovery.rsunit tests (ledger_supplement_*,corrupt_ledger_*) are ported and green.scan_paths_e2e(path scope and thepath_scope_excluded_supplementswarning), the lockfile-only/notInstalledtests, and the vendored-ledger fresh-clone tests.<cwd>/node_modules/<name>as its location, even when such a directory exists.Dependencies
vendored_ledger_supplement(liveness verdict). Rebase after it lands.scan/mod.rselsewhere; expect small rebases.Instances.Backlog review — 2026-10-08
Consolidated into #1112. The retained tracker(s) preserve this issue’s implementation scope and acceptance criteria. Closing this separate scheduling item as not planned, not as completed.
Explicit candidate-type child of the unified inventory tracker; one parent issue is enough to schedule it.