[agent] Filed by the scheduled architecture audit routine (ecosystems and formats). Register: discussion #560 register.
Kind: bug. Source: new finding (follow-up to #1044 / E75); register E91.
Problem
#1044 moved the PyPI tool-lock order into one table, formats::governing_locks. The lock inventory still keeps its own copy of that order, and the two copies use different rules:
So with a package-less poetry.lock (Poetry writes package = [] for a project that declares no dependencies, for example one that uses Poetry only for packaging) beside a requirements.txt that pins the dependencies:
scan --mode vendored therefore offers a package that it then refuses to vendor, and the remedy it prints (poetry lock) does not help: re-locking an empty Poetry project produces the same empty lock. The same split applies to a package-less pdm.lock.
Proof by execution. I ran a throwaway unit test in vendor/pypi.rs twice on ea09714. The project had poetry.lock = package = [] plus a [metadata] section, a pyproject.toml with [tool.poetry], and requirements.txt = six==1.16.0:
INVENTORY: ["pkg:pypi/six@1.16.0"]
ROUTER: Ok(("Poetry", [VendorWarning { code: "pypi_multiple_lockfiles", detail: "multiple python lockfiles found; wiring `poetry.lock` — installs driven by requirements.txt will still install the UNPATCHED registry bytes" }]))
The output was identical on both runs.
Symptoms
None filed. This is the "lock inventory's own PyPI order" that #1044 left outside the table.
Impact
Proposed change
- Add one predicate to
formats::governing_locks that both callers use, for example pypi_governing(view) -> Option<&'static str>. It applies one rule for a tool lock that resolves no packages. The recommended rule is the inventory's: a package-less tool lock does not govern, so requirements.txt (or the next tool lock) does.
inventory_pypi_locks_raw_in asks that predicate instead of its hand-coded if !uv_lock { poetry else pdm else … } chain. Delete the chain.
detect_pypi_flavor asks the same predicate, so this project routes to Requirements, and the pypi_multiple_lockfiles warning no longer names poetry.lock as the governor.
- Keep the inventory's documented Pipfile.lock + requirements.txt union and the uv parse-success rule (an unparseable
uv.lock falls through), stated once in the table's docs.
Size and scope
Acceptance criteria
Dependencies
Backlog review — 2026-10-08
Priority: P1 → P3. An empty tool lock beside requirements causes a false refusal, not a false security attestation. Narrow layout edge; retain the inventory/router fix.
[agent] Filed by the scheduled architecture audit routine (ecosystems and formats). Register: discussion #560 register.
Kind: bug. Source: new finding (follow-up to #1044 / E75); register E91.
Problem
#1044 moved the PyPI tool-lock order into one table,
formats::governing_locks. The lock inventory still keeps its own copy of that order, and the two copies use different rules:vendor/pypi.rs#L269-L362detect_pypi_flavordecides by presence. The first ofuv.lock,poetry.lock,pdm.lock,Pipfile.lockthat exists governs (pypi_governing_tool_lock,governing_locks.rs#L115-L124).``lock_inventory/pypi.rs#L246-L300inventory_pypi_locks_raw_inhand-codesuv → poetry → pdm → (Pipfile.lock + requirements.txt), keyed on whether a lock yielded entries.inventory_poetry_lockandinventory_pdm_lockreturnNonefor a lock with zero[[package]]blocks (#L415-L418,[`#L594-L597`](https://github.com/SocketDev/socket-patch/blob/ea097142389f6fa5bf202472cc1c1750a8464eef/crates/socket-patch-core/src/vendor/lock_inventory/pypi.rs#L594-L597)),`` and the inventory then falls through torequirements.txt. This is deliberate, and pinned bydepless_poetry_lock_falls_through_to_requirements(lock_inventory/tests.rs:2722).So with a package-less
poetry.lock(Poetry writespackage = []for a project that declares no dependencies, for example one that uses Poetry only for packaging) beside arequirements.txtthat pins the dependencies:requirements.txtresolvessix==1.16.0.poetry.lock. It then warns that "installs driven by requirements.txt will still install the UNPATCHED registry bytes" (the Vendored mode in a Pipenv project wires only Pipfile.lock and silently leaves a sibling requirements.txt unpatched, and the hosted → vendored takeover reverts that file's hosted pin to plain PyPI #612 warning), and the Poetry backend refuses withpypi_poetry_lock_package_missing("poetry.lock has no [[package]] entry for six; runpoetry lockfirst";pypi_poetry.rs#L213-L221).``scan --mode vendoredtherefore offers a package that it then refuses to vendor, and the remedy it prints (poetry lock) does not help: re-locking an empty Poetry project produces the same empty lock. The same split applies to a package-lesspdm.lock.Proof by execution. I ran a throwaway unit test in
vendor/pypi.rstwice onea09714. The project hadpoetry.lock=package = []plus a[metadata]section, apyproject.tomlwith[tool.poetry], andrequirements.txt=six==1.16.0:The output was identical on both runs.
Symptoms
None filed. This is the "lock inventory's own PyPI order" that #1044 left outside the table.
Impact
requirements.txt), but every future change to PyPI precedence has to be made in two places.Proposed change
formats::governing_locksthat both callers use, for examplepypi_governing(view) -> Option<&'static str>. It applies one rule for a tool lock that resolves no packages. The recommended rule is the inventory's: a package-less tool lock does not govern, sorequirements.txt(or the next tool lock) does.inventory_pypi_locks_raw_inasks that predicate instead of its hand-codedif !uv_lock { poetry else pdm else … }chain. Delete the chain.detect_pypi_flavorasks the same predicate, so this project routes toRequirements, and thepypi_multiple_lockfileswarning no longer namespoetry.lockas the governor.uv.lockfalls through), stated once in the table's docs.Size and scope
formats/governing_locks.rs,vendor/lock_inventory/pypi.rs,vendor/pypi.rs(detect_pypi_flavor). Estimate under 150 production lines.pdm_drivesignoring standalone pylock (a separate Decide which lockfile governs installs in one table #1044 deferral), and the takeover restore scope (Vendored mode in a Pipenv project wires only Pipfile.lock and silently leaves a sibling requirements.txt unpatched, and the hosted → vendored takeover reverts that file's hosted pin to plain PyPI #612).Acceptance criteria
PypiFlavor::Requirements, andinventory_projectanddetect_pypi_flavoragree on the governing source. Add the same for a package-lesspdm.lock.depless_poetry_lock_falls_through_to_requirementsand theformats::governing_lockstests stay green.e2e_vendor_pypi*,mode_migration_pypi) stay green.vendor/lock_inventory/spells the PyPI tool-lock order.Dependencies
lock_inventory/pypi.rsand Decide vendored-entry liveness through one discovery verdict #1050 editsvendor/pypi.rs. Rebase after them.Backlog review — 2026-10-08
Priority: P1 → P3. An empty tool lock beside requirements causes a false refusal, not a false security attestation. Narrow layout edge; retain the inventory/router fix.