Skip to content

Fix Poetry 1.3 source-pin guard (#1447) - #1451

Open
Mikola Lysenko (mikolalysenko) wants to merge 4 commits into
mainfrom
agent/fix-poetry-13-source-pin-guard
Open

Mikola Lysenko (mikolalysenko) wants to merge 4 commits into
mainfrom
agent/fix-poetry-13-source-pin-guard

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator

LLM Description written by Claude Code:claude-opus-5-5

Fixes #1447

Summary

Hosted and vendored scans now refuse a source = "pypi"-pinned Poetry dependency when Poetry 1.3 wrote the lock, the same way they already refuse 1.4 – 2.2 locks. Before this, a 1.3 project got a rewired lock that poetry install 1.3 can't solve ("version solving failed"). The scan still exited 0, and its advisory said 1.3 installs it.

Root cause

source_constraint_breaks_writer (utils/poetry_lock.rs) judged the writer only by the version in the @generated by Poetry X.Y stamp. Poetry 1.3 writes that sentence without a version, so generated_by_version returned None and the guard never fired. The hosted rewriter, the vendored guard and the vendored → hosted takeover preflight all go through this one function, so a single fix covers all three.

Fix

  • New lock_writer_version: it reads the stamped version when there is one. Otherwise it reports 1.3 for lock-version = "2.0", because only 1.3 – 1.8 write that format and 1.4 – 1.8 always stamp their version. This is the same rule pre_1_4_writer already uses for the stale-install advisories, so the two can no longer disagree about an unstamped 2.0 lock. It also still refuses a 1.3 lock whose header was edited away, which is a case /code-review raised.
  • The guard range is now 1.3 – 2.2. The refusal text, the advisory ("1.1 – 1.2 and 2.3+ install it"), docs/testing/poetry-compatibility.md and CLI_CONTRACT.md now name the corrected range.
  • CI: added a Poetry 1.3.2 leg to the full-tier e2e_vex_build Poetry matrix. The issue points out that the matrix had no 1.3 leg.

Test evidence

Test Old guard New guard
utils::poetry_lock::tests::source_constraint_breaks_only_poetry_1_3_to_2_2_writers (1.3.2 fixture, stamped 1.3.0, versionless and unstamped 2.0, CRLF, 1.1/2.1 controls) FAILED ok
vendor::pypi_poetry::tests::source_constrained_dependency_is_refused_for_poetry_1_3_to_2_2 (vendored, 1.3.2 fixture) FAILED ok
patch::redirect::poetry::equivalence_tests::source_constrained_dependency_is_refused_for_poetry_1_3_to_2_2 (hosted, 6 pyproject shapes, fresh and already-redirected) FAILED ok
patch::redirect::pypi_takeover::tests::poetry_source_pinned_package_follows_the_writer (takeover, 1.3 lock) FAILED ok
e2e poetry::poetry_source_pypi_constraint_never_breaks_install on real Poetry 1.3.2 FAILED (hosted rewrote the lock) ok

The whole poetry:: e2e suite (4 tests) passes on real Poetry 1.3.2 locally:
SOCKET_PATCH_POETRY_BIN=… SOCKET_PATCH_POETRY_E2E_REQUIRED=1 SOCKET_PATCH_POETRY_E2E_VERSION=1.3.2 cargo test -p socket-patch-cli --all-features --test e2e_vex_build -- poetry:: --ignored

Local gate: cargo fmt --all -- --check and cargo clippy --workspace --all-features -- -D warnings are clean. cargo test --workspace --all-features --no-fail-fast passes except 13 tests that fail only because this sandbox runs as root (chmod-0555 / unremovable-file write-failure tests, which root bypasses) or has no network to patches-api (binary_shared_bundled_record_hosted_pin_is_managed). None of them touch the Poetry source-pin path. CI runs them as non-root.

Per-issue checklist

Review notes

/code-review high also suggested merging the two pre_1_4_writer copies (redirect/poetry.rs, vendor/pypi_poetry.rs) into lock_writer_version, and not re-parsing the lock to read its format. I left both out of this PR. They are pre-existing duplication, they now agree with the new helper, and the extra parse happens once per lock per run. The wrappers (npm/, pypi/, gem/) only dispatch to the binary, so they need no change.

🤖 Generated with Claude Code

https://claude.ai/code/session_01G22ffRqhurh7MjZ7GZA2HM


Generated by Claude Code

Assisted-by: Claude Code:claude-opus-5-5
Poetry 1.3 writes its lock header without a version, so the guard
from #1413 never recognized a 1.3 lock. Hosted and vendored scans
then rewired a `source = "pypi"` dependency into a lock that
`poetry install` 1.3 can't solve, exited 0, and the advisory said
1.3 installs it.

A 1.3 lock is recognized by the versionless header over
`lock-version = "2.0"`, which no other Poetry writes. The refusal
now covers 1.3 - 2.2 in hosted, vendored and the vendored-to-hosted
takeover, and the advisory, docs and CLI contract name the right
range.

Fixes #1447

Assisted-by: Claude Code:claude-opus-5-5
The full-tier Poetry legs skipped 1.3, the one release that writes a
versionless lock header, which is how #1447 slipped past the source
pin guard. Add a 1.3.2 leg so the real-installer tests cover it.

Refs #1447

Assisted-by: Claude Code:claude-opus-5-5
Only Poetry 1.3 - 1.8 write lock-version 2.0, and every one of them
refuses a source-pinned package once the lock sources it from a URL
or file. Key the 1.3 case on the lock format alone, so a 1.3 lock
whose header was edited away is still refused, and the writer reads
the same way the stale-install advisory already reads it. Tighten
the tests: a stamped 1.3.0 case, explicit headers in the takeover
test, and a sibling-lock filter that drops only its stale warning.

Refs #1447

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for 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 e161737. Configure here.

@mikolalysenko Mikola Lysenko (mikolalysenko) added the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 12, 2026
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Ready for review (burn-down agent)

  • Head: e1617371e433bb980c8a53525e1112304ff37f13
  • CI: 37/37 green, 50 path-skipped. No failures.
  • Bugbot: reviewed this head, no findings. No open review threads.
  • Reviewer: look at lock_writer_version in utils/poetry_lock.rs. It treats an unstamped lock-version = "2.0" lock as written by Poetry 1.3, and the hosted rewriter, the vendored guard and the takeover preflight all depend on that rule. The PR also adds a Poetry 1.3.2 leg to the full-tier e2e_vex_build matrix in ci.yml.
  • Slack announcement not sent: the Slack connector in this session has no send tool.

Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Final review brief

What it does. The guard refuses source = "pypi"-pinned Poetry locks that older Poetry can't install. It read the writer version from the @generated by Poetry X.Y stamp, but Poetry 1.3 writes that stamp with no version, so 1.3 locks slipped through and the next poetry install broke. The refused range widens from 1.4–2.2 to 1.3–2.2, and an unstamped lock-version 2.0 is treated as written by 1.3. Docs, tests and the CI e2e matrix (now with Poetry 1.3.2) are updated to match.

Risk: low. The change only widens a refusal guard by one Poetry minor version. When unsure it refuses: an unstamped 2.0 lock that Poetry 2.3+ would install, or one some other tool wrote, gets refused with remedy text and is never left in a broken install.

Look here

Verified

  • Read the full diff and checked these edge cases: lock 1.1 is not refused, lock 2.1 is decided by its stamp, lock 2.0 stamped or unstamped, CRLF, and an unparseable lock is not refused.
  • The new tests would fail without the fix: the fixture("1.3.2\") refusal, the vendor table's 1.3.2 → true and the takeover 1.3 case.
  • CHANGELOG.md is untouched and there are no debug leftovers.
  • CI: 37/37 green, 50 skipped, including the new 1.3.2 e2e.
  • Bugbot is clean on e161737.
  • A local cargo run didn't finish in this sandbox because of a toolchain install problem, so CI is the test evidence here.

Changes I made: none.

Non-blocking nits (fine as follow-ups)

  • The "unstamped 2.0 means 1.3" rule lives in three places: lock_writer_version, pre_1_4_writer and inline in vendor/pypi_poetry.rs. pre_1_4_writer could call lock_writer_version so the three can't drift apart.
  • A lock-2.1 file with a comment above the stamp is still not refused. That was already true before this PR.

Auto-merge is armed, so approving sends this straight to the merge queue.


Generated by Claude Code

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review

Projects

None yet

2 participants