Repository navigation
Vendored-reference scan never reads hatch.toml, so the orphan sweep deletes a wheel that a Hatch environment still installs #958
Description
[agent] Filed by the scheduled architecture audit routine (ecosystems and formats). Register: discussion #560 register.
Kind: bug. Source: new finding, register E67 (same root cause as E61, #832).
Problem
The format registry has two notions of "a file a vendored run writes", and the reference scan uses the narrower one:
registry::paths_with(VENDORED)is whatscan_vendor_references/wiring_files`` reads.registry::wiring_pathsaddsVENDORED_WRITES_UNMARKED:pnpm-workspace.yaml, the threenuget.configspellings,packages.lock.json,pom.xml,.mvn/maven.configandhatch.toml. Only the vendored dry run's symlink check uses it.
hatch.toml is HOSTED | PROBE (registry.rs#L110), but vendored Hatch reads and rewrites it: when hatch.toml carries envs, Hatch reads environment dependencies from there, and utils::hatch::plan pins them as six @ {root:uri}/.socket/vendor/pypi/<uuid>/<wheel>#sha256=… in hatch.toml.
So the same project disagrees with itself: VEX discovery (PROBE) sees the vendored wheel as live, but the orphan sweep (vendor --revert, the vendored gc), repair (vendor_ledger_missing), the vendor stranded-reference gate and rollback don't see the reference at all. sweep_orphan_vendor_dirs documents the invariant this breaks: "every candidate is checked against the wiring-bearing files first".
#832 is the NuGet and Maven half of the same gap. It also needs a uuid-directory grammar, which this case does not: the Hatch reference has a leaf and parse_vendor_path accepts it.
Proof by execution. I ran a throwaway #[tokio::test] in commands/vendor.rs twice on 9c43dfc, then removed it. It wires six==1.16.0 through the real utils::hatch::plan with a {root:uri} URL, writes one wheel into the uuid dir, and runs scan_vendor_references and sweep_orphan_vendor_dirs with an empty VendorState:
PROBE hatch.toml envs: wiring in ["hatch.toml"] refs=0 still_wired=0 removed=1 wheel_exists=false
PROBE pyproject envs (control): wiring in ["pyproject.toml"] refs=1 still_wired=1 removed=0 wheel_exists=true
The same dependency declared under [tool.hatch.envs.default] in pyproject.toml is kept. Declared under [envs.default] in hatch.toml, its wheel is deleted while hatch.toml still names it.
pnpm-workspace.yaml in the same list is not affected in practice: pnpm-lock.yaml (VENDORED) carries the same reference.
Symptoms
None filed. When a Hatch ledger entry is missing (state.json lost, a partial commit, or a merge that drops a row):
- the sweep deletes the wheel, and the next
hatch env createfails on a missing file; repairreports nothing;vendorre-vendors without thevendor_ledger_entry_missingrefusal.
Impact: destructive, but it needs a missing ledger entry first. Small fix.
Proposed change
- Make the reference scan read every file a vendored run writes. Either it iterates
wiring_pathsacross ecosystems, orhatch.tomlgets theVENDOREDrole. Check the other readers ofhas(VENDORED)first:VENDOREDalso scopesrepair's fingerprint. - Preferably, delete
VENDORED_WRITES_UNMARKEDand the two-notion split. Give the rows a precise role (for example, split "search for references" from "fingerprint") so that one list answers "what does vendored write". That lets Vendored-reference scan never sees NuGet or Maven wiring, so the orphan sweep deletes a still-wired unit #832's file-list half land in the same change.
Size and scope
formats/registry.rs, commands/vendored_backend/repair.rs; under ~40 production lines. #832's uuid-directory grammar for NuGet and Maven is out of scope.
Acceptance criteria
-
scan_vendor_referencesreports(pypi, uuid)for ahatch.toml-wired environment dependency. - A regression test like
orphan_sweep_keeps_include_referenced_dir: with an empty ledger, thehatch.toml-referenced wheel stays instill_wired, and it is removed once the reference is gone. -
wiring_paths_name_every_rewritable_file_of_the_ecosystemand the existing repair/orphan tests stay green, and the vendored Hatch e2e tests pass. - Hosted reads are unchanged (
HOSTEDrows untouched).
Dependencies
None. Overlaps #832 (same function, same list). Whichever lands second rebases; if #832 switches the scan to wiring_paths, it closes this too.
Activity
audit-ecosystems: E21 tracking (#959, #960), new E67 (#958)
mikolalysenko commentedon Oct 6, 2026
[agent] Shares root cause with #832:
scan_vendor_referencesreads onlyregistry::paths_with(VENDORED), not every file a vendored run writes (wiring_paths). Will be fixed together (the file-list half; #832's NuGet/Maven uuid-directory grammar is extra scope on that issue).Triaged as priority:p1 (Hatch / PyPI family). No open PR covers it.
Generated by Claude Code
mikolalysenko commentedon Oct 7, 2026
[agent] Claiming this issue for the architecture refactor routine (highest leverage: fixed together with #832, same root cause: the reference scan reads
paths_with(VENDORED)instead of every vendored write). Branch: arch-refactor/832-vendored-reference-scan. Claim-ID: 2026-10-07T12:56:20Z-90d5b6Generated by Claude Code
mikolalysenko commentedon Oct 7, 2026
[agent] Draft PR: #1015 (fixes this and #832).
Generated by Claude Code
Scan every vendored-write file for references
refactor: record #886, open #1015 (#832, #958)