Skip to content

Poetry and PDM lock rewrites flip a mixed-line-ending lock's edited unit in opposite directions #695

Description

[agent] Filed by the scheduled architecture audit routine (ecosystems and formats). Register: E54.

Kind: bug. Source: new finding (register E54), part of review Part 5.4's "one line-ending policy" (E16).

Problem

toml_edit re-emits every newline as LF, so each Python lock rewriter has to restore line endings after rendering. The Poetry and PDM rewriters do this with two different rules, and they flip the edited package unit of a mixed-line-ending lock in opposite directions.

Both rewriters then splice only the changed fragments, the package unit and its boundary, back into the original text. So the rule decides the line endings of the unit's lines, including the lines the rewrite didn't otherwise change. Hosted (redirect/poetry.rs, redirect/pdm.rs) and vendored (vendor/pypi_poetry.rs, vendor/pypi_pdm.rs) share these functions, so both modes are affected.

Proof by execution (a throwaway integration test on 045d7ec, run twice, not committed). Input: the repository's own fixture (tests/fixtures/poetry/2.4.3/poetry.lock or tests/fixtures/pdm-native/2.29.2.lock), converted to CRLF, with the name = "urllib3" line of the patched unit left LF. Each was rewritten for a hosted urllib3 1.26.18 wheel:

input CRLF / LF lines output CRLF / LF lines edited unit's lines
rewrite_poetry_lock 22 / 1 24 / 0 all CRLF
rewrite_pdm_lock 21 / 1 12 / 8 all LF, inside a CRLF file

On the same input shape, Poetry normalizes the unit to the majority ending, while PDM turns the unit's CRLF lines into LF (8 LF lines in a CRLF file). That leaves a CRLF lock with a block of LF lines, which shows up as whole-unit churn in git diff. Rollback replays the recorded fragments, so it restores the bytes exactly in both cases. The defect is the drift in the forward rewrite and the duplicated rule.

Symptoms

None filed. #467 is the same class of bug for yarn classic (mixed CRLF/LF converted to CRLF).

Impact: low severity. Only locks that already mix line endings are affected, and only the rewritten unit changes. It is the third and fourth spelling of the "toml_edit emits LF" fix (Part 5.4 lists line_endings refusing mixed files, python_lock's rule and cargo_manifest's LCS alignment), and each new rewriter picks a different one.

Proposed change

Give both rewriters one rule. Since only fragments are spliced, the rule that never flips an untouched line is per fragment: render each replacement fragment with the line ending that dominates the original fragment it replaces (an LF-only or CRLF-only file then behaves exactly as today).

Size and scope

  • utils/poetry_lock.rs, utils/pdm_lock.rs, utils/python_lock.rs. About 30 production lines plus tests.
  • Out of scope: Cargo's reconcile_line_endings, the npm family, and the repo-wide line-ending policy (E16).

Acceptance criteria

  • On the mixed-line-ending input above, Poetry and PDM leave every line outside the rewritten bytes with its original ending, and give the inserted lines the replaced unit's dominant ending.
  • LF-only and CRLF-only fixtures are byte-identical to today (existing native_formats_rewrite_and_reverse_byte_exactly and the Poetry fixture tests, CRLF variants included).
  • Rollback is still byte-exact on the mixed input, for both formats and both source types (url hosted, file/path vendored).
  • cargo test -p socket-patch-core is green.

Dependencies

Independent. It is simpler after #694 (the shared fragment engine), because then it is fixed in one place.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent:claimedagent:triagedarch-auditFiled by a scheduled architecture audit routine (see the architecture review discussion)bugSomething isn't workingpriority:p1

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions