You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Vendored Pipenv re-serializes the whole Pipfile.lock, so a non-ASCII lock is rewritten throughout and its revert is not byte-identical #1128
[agent] Filed by the scheduled architecture audit routine (ecosystems and formats). Register: register comment.
Kind: bug (a duplicated writer whose behavior has drifted, proven by execution). Source: review Part 5.4 ("Pipfile.lock is written two ways"); register E14.
Problem
Pipfile.lock has two writers:
Hosted mode (and upstream restore) splices single entries by byte span and leaves every other byte alone: redirect/pipenv.rsentries / format_entry.``
Vendored mode parses the lock into a serde_json::Value, edits it, and writes the whole document back through to_canonical_json.`` It does this on both wire (#L498) and revert (`#L686`).
to_canonical_json claims to be byte-identical to Pipenv's json.dumps(indent=4, sort_keys=True) "for the ASCII content pipenv locks carry". That holds only for ASCII. Pipenv's encoder uses Python's default ensure_ascii=True, so it writes any non-ASCII character as \uXXXX, while serde_json writes raw UTF-8. A Pipenv lock can carry non-ASCII text, for example a local path dependency in a directory named café, or a source name.
The writers have also drifted on a leading BOM. Hosted mode accepts one and keeps it (bom_prefixed_lock_is_rewritten_with_the_bom_intact).`` Vendored mode passes the raw text to serde_json::from_str and refuses it.
A layering detail: vendored revert imports its relock test from the hosted engine (crate::patch::redirect::pipenv_reserialized_around_reference),`` which is a vendor → redirect edge.
Proof. A throwaway test in pypi_pipenv.rs's test module, run twice on e2d9633. It used the module's LOCK_DIRECT_REGISTRY fixture with one extra develop entry, "mylib": {"editable": true, "path": "./libs/café"}, written as Pipenv writes it:
Result (both runs)
vendored wire_pipenv
the lock now contains raw café and no café: an unrelated entry was rewritten
vendored revert_pipenv after that wire
success: true, but the lock is not byte-identical to the original (the escape stays lost)
hosted rewrite_registry_redirect on the same lock
rewrites six and keeps café byte for byte, with no warnings
vendored load_pipenv_project on the fixture with a leading BOM
pypi_pipenv_lock_parse_failed ("expected value at line 1 column 1"); hosted accepts the same file
So vendor --revert reports a clean revert while leaving a diff in Pipfile.lock, and vendor rewrites lines the user never asked it to touch. The test revert_round_trip_restores_lock_byte_identically passes only because every fixture is ASCII.
Symptoms
None filed. Related: #905 (BOM helper; its pending-files list doesn't name this missing strip) and #937 (the shared Poetry/PDM/Pipenv wire and revert envelope).
Impact: low severity, but it breaks a contract. Pipenv still reads the rewritten lock. The cost is a noisy diff on vendor, and a "byte-identical" revert that isn't one. The vendor --check and takeover drift checks compare parsed values, so they are not misled. Size: about 120 production lines change, and to_canonical_json and with_line_ending are deleted.
Proposed change
Move the hosted span reader (entries, properties, format_entry) and reserialized_around_reference from patch/redirect/pipenv.rs into a new formats/pipenv.rs, as a pure move. Hosted, upstream restore and vendored all import it from there, which removes the vendor → redirect edge.
Vendored wire and revert splice only the entries they change, through that module, as hosted does. Read the lock through formats::text::strip_bom and re-add the BOM on write.
Deleteto_canonical_json, with_line_ending and the pipenv_reserialized_around_reference re-export in redirect/mod.rs.
Render a spliced entry in the lock's own escaping. Either share Composer's escapes_unicode / escape_non_ascii (moving them to formats::json or similar), or have format_entry emit \uXXXX whenever the surrounding text does.
Size and scope
Files: vendor/pypi_pipenv.rs, patch/redirect/pipenv.rs, patch/redirect/mod.rs (re-export), a new formats/pipenv.rs, and possibly vendor/composer_lock/lock_text.rs (if the escaping helpers move).
About 250–400 changed lines. The move (step 1) can land as its own commit.
One Pipfile.lock entry reader/splicer, in formats/, used by hosted, upstream restore and vendored.
to_canonical_json and with_line_ending are deleted, and vendor/ no longer imports patch::redirect::pipenv_*.
Regression test: a lock carrying é in an unrelated entry. Vendored wire changes only the target entry's bytes, and wire + revert is byte-identical.
Regression test: a BOM-prefixed lock vendors and reverts with the BOM kept, matching hosted.
wiring_matches_fixtures_byte_identically, revert_round_trip_restores_lock_byte_identically and the redirect::pipenv tests stay green, along with cargo test -p socket-patch-core and the Pipenv CLI suites.
Priority: unassigned → P3. Keep the concrete Pipenv writer/BOM compatibility follow-up. Non-ASCII reserialization changes formatting while parsed values and installs remain correct; a BOM-prefixed file is refused safely. This is low-priority formatting and input compatibility, not P1 patch or attestation correctness.
[agent] Claiming this issue for the architecture refactor routine (highest leverage: deletes the second Pipfile.lock writer, fixes the non-ASCII and BOM drift, and all its files are free of open PRs). Branch: arch-refactor/1128-pipenv-one-splicer. Claim-ID: 2026-10-08T22:55:36Z-03a693
[agent] Filed by the scheduled architecture audit routine (ecosystems and formats). Register: register comment.
Kind: bug (a duplicated writer whose behavior has drifted, proven by execution). Source: review Part 5.4 ("Pipfile.lock is written two ways"); register E14.
Problem
Pipfile.lockhas two writers:redirect/pipenv.rsentries/format_entry.``serde_json::Value, edits it, and writes the whole document back throughto_canonical_json.`` It does this on both wire (#L498) and revert (`#L686`).to_canonical_jsonclaims to be byte-identical to Pipenv'sjson.dumps(indent=4, sort_keys=True)"for the ASCII content pipenv locks carry". That holds only for ASCII. Pipenv's encoder uses Python's defaultensure_ascii=True, so it writes any non-ASCII character as\uXXXX, while serde_json writes raw UTF-8. A Pipenv lock can carry non-ASCII text, for example a localpathdependency in a directory namedcafé, or a sourcename.The writers have also drifted on a leading BOM. Hosted mode accepts one and keeps it (
bom_prefixed_lock_is_rewritten_with_the_bom_intact).`` Vendored mode passes the raw text toserde_json::from_strand refuses it.The Composer backend fixed this exact class of bug.
vendor/composer_lock/lock_text.rssplices one entry and renders it in the lock's own slash and unicode escaping ([`escapes_unicode`](https://github.com/SocketDev/socket-patch/blob/e2d96336fbc449a87a20faae57afbed597e8788f/crates/socket-patch-core/src/vendor/composer_lock/lock_text.rs#L132-L160)),`` because "a whole-lock re-serialization rewrote what serde_json spells differently from the lock's writer". Pipenv never got the same fix.A layering detail: vendored revert imports its relock test from the hosted engine (
crate::patch::redirect::pipenv_reserialized_around_reference),`` which is avendor → redirectedge.Proof. A throwaway test in
pypi_pipenv.rs's test module, run twice one2d9633. It used the module'sLOCK_DIRECT_REGISTRYfixture with one extradevelopentry,"mylib": {"editable": true, "path": "./libs/café"}, written as Pipenv writes it:wire_pipenvcaféand nocafé: an unrelated entry was rewrittenrevert_pipenvafter that wiresuccess: true, but the lock is not byte-identical to the original (the escape stays lost)rewrite_registry_redirecton the same locksixand keepscafébyte for byte, with no warningsload_pipenv_projecton the fixture with a leading BOMpypi_pipenv_lock_parse_failed("expected value at line 1 column 1"); hosted accepts the same fileSo
vendor --revertreports a clean revert while leaving a diff inPipfile.lock, and vendor rewrites lines the user never asked it to touch. The testrevert_round_trip_restores_lock_byte_identicallypasses only because every fixture is ASCII.Symptoms
None filed. Related: #905 (BOM helper; its pending-files list doesn't name this missing strip) and #937 (the shared Poetry/PDM/Pipenv wire and revert envelope).
Impact: low severity, but it breaks a contract. Pipenv still reads the rewritten lock. The cost is a noisy diff on vendor, and a "byte-identical" revert that isn't one. The
vendor --checkand takeover drift checks compare parsed values, so they are not misled. Size: about 120 production lines change, andto_canonical_jsonandwith_line_endingare deleted.Proposed change
entries,properties,format_entry) andreserialized_around_referencefrompatch/redirect/pipenv.rsinto a newformats/pipenv.rs, as a pure move. Hosted, upstream restore and vendored all import it from there, which removes thevendor → redirectedge.formats::text::strip_bomand re-add the BOM on write.to_canonical_json,with_line_endingand thepipenv_reserialized_around_referencere-export inredirect/mod.rs.escapes_unicode/escape_non_ascii(moving them toformats::jsonor similar), or haveformat_entryemit\uXXXXwhenever the surrounding text does.Size and scope
vendor/pypi_pipenv.rs,patch/redirect/pipenv.rs,patch/redirect/mod.rs(re-export), a newformats/pipenv.rs, and possiblyvendor/composer_lock/lock_text.rs(if the escaping helpers move).use_pylock = trueproject wires only pylock.toml, but Pipenv installs from Pipfile.lock, sopipenv sync/install --deployinstall the unpatched release after a "success" run #1122).Acceptance criteria
Pipfile.lockentry reader/splicer, informats/, used by hosted, upstream restore and vendored.to_canonical_jsonandwith_line_endingare deleted, andvendor/no longer importspatch::redirect::pipenv_*.éin an unrelated entry. Vendored wire changes only the target entry's bytes, and wire + revert is byte-identical.wiring_matches_fixtures_byte_identically,revert_round_trip_restores_lock_byte_identicallyand theredirect::pipenvtests stay green, along withcargo test -p socket-patch-coreand the Pipenv CLI suites.Dependencies
Backlog review — 2026-10-08
Priority: unassigned → P3. Keep the concrete Pipenv writer/BOM compatibility follow-up. Non-ASCII reserialization changes formatting while parsed values and installs remain correct; a BOM-prefixed file is refused safely. This is low-priority formatting and input compatibility, not P1 patch or attestation correctness.