[agent] Filed by the scheduled architecture audit routine (ecosystems and formats). Register: register comment.
Kind: bug. Source: new finding, register E53 (a sibling of the JSON-writer drift that #357 fixed for npm locks; review Part 4.4 "JSON re-serialization, three strategies").
Problem
Three vendored JS backends write the project's root package.json. npm and yarn berry render it through vendor::common::JsonLayout, which keeps the BOM, indent, line ending and trailer. The pnpm backend (v9 and legacy, which share vendor_pnpm_dialect / revert_pnpm_dialect since #583) still uses detect_indent + common::serialize_json, which always writes LF and no BOM:
For comparison, yarn berry: yarn_berry_lock.rs JsonLayout is pinned by crlf_bom_and_newline_shapes_vendor_and_revert_byte_exact; npm by vendor_and_revert_keep_crlf_tab_and_bom_lock_layout (npm_lock.rs#L2990).
Repro (unit probe on 045d7ec, run twice, not committed)
Using the existing P1_BEFORE_PKG / P1_BEFORE_LOCK fixture in pnpm_lock.rs tests, with only package.json converted:
let crlf_pkg = P1_BEFORE_PKG.replace('\n', "\r\n");
let fx = fixture_with(&crlf_pkg, P1_BEFORE_LOCK).await;
let (_, entry, _) = expect_done(fx.vendor(false).await); // package.json now has 0 CRLF
revert_pnpm(&entry.unwrap(), fx.root(), false).await; // success
fx.read(PACKAGE_JSON).await == crlf_pkg // false: still all LF
Output (both runs): after vendor CRLF count=0, after revert restored==original: false, CRLF count=0.
With a UTF-8 BOM in front of the same package.json (pnpm and npm install from it), vendoring refuses with vendor_pkg_json_unsupported: package.json is not a JSON object; cannot add pnpm.overrides, a misleading reason.
Impact
- A Windows/autocrlf pnpm project gets a whole-file
package.json diff from scan --mode vendored / vendor, and vendor --revert / remove leave it reformatted. README promises a byte-exact revert.
- A BOM
package.json can't be vendored, and the error names the wrong cause.
- The pnpm lock itself refuses CRLF on purpose (
vendor_lockfile_crlf_unsupported), so this is only about package.json. Size: ~10 production lines.
Proposed change
- Parse
package.json with common::parse_json_manifest in pnpm read_project (and in the revert's pkg_state read).
- Render it with
JsonLayout::of(original).render(&pkg) on vendor and revert; drop the detect_indent + serialize_json pair and the indent field of pkg_state.
Size and scope
vendor/pnpm_lock.rs only, ~15 production lines plus tests. Out of scope: the lock's CRLF refusal, and hosted pnpm (it doesn't write package.json).
Acceptance criteria
Dependencies
None. Touches only vendor/pnpm_lock.rs; no open PR changes that file.
[agent] Filed by the scheduled architecture audit routine (ecosystems and formats). Register: register comment.
Kind: bug. Source: new finding, register E53 (a sibling of the JSON-writer drift that #357 fixed for npm locks; review Part 4.4 "JSON re-serialization, three strategies").
Problem
Three vendored JS backends write the project's root
package.json. npm and yarn berry render it throughvendor::common::JsonLayout, which keeps the BOM, indent, line ending and trailer. The pnpm backend (v9 and legacy, which sharevendor_pnpm_dialect/revert_pnpm_dialectsince #583) still usesdetect_indent+common::serialize_json, which always writes LF and no BOM:pnpm_lock.rs#L326-L327pnpm_lock.rs#L946-L952package.jsonis parsed with a plainserde_json::from_slice(#L483), notcommon::parse_json_manifest, so a BOM is rejected.For comparison, yarn berry:
yarn_berry_lock.rsJsonLayoutis pinned bycrlf_bom_and_newline_shapes_vendor_and_revert_byte_exact; npm byvendor_and_revert_keep_crlf_tab_and_bom_lock_layout(npm_lock.rs#L2990).Repro (unit probe on
045d7ec, run twice, not committed)Using the existing
P1_BEFORE_PKG/P1_BEFORE_LOCKfixture inpnpm_lock.rstests, with onlypackage.jsonconverted:Output (both runs): after vendor
CRLF count=0, after revertrestored==original: false,CRLF count=0.With a UTF-8 BOM in front of the same
package.json(pnpm and npm install from it), vendoring refuses withvendor_pkg_json_unsupported: package.json is not a JSON object; cannot add pnpm.overrides, a misleading reason.Impact
package.jsondiff fromscan --mode vendored/vendor, andvendor --revert/removeleave it reformatted. README promises a byte-exact revert.package.jsoncan't be vendored, and the error names the wrong cause.vendor_lockfile_crlf_unsupported), so this is only aboutpackage.json. Size: ~10 production lines.Proposed change
package.jsonwithcommon::parse_json_manifestin pnpmread_project(and in the revert'spkg_stateread).JsonLayout::of(original).render(&pkg)on vendor and revert; drop thedetect_indent+serialize_jsonpair and theindentfield ofpkg_state.Size and scope
vendor/pnpm_lock.rsonly, ~15 production lines plus tests. Out of scope: the lock's CRLF refusal, and hosted pnpm (it doesn't writepackage.json).Acceptance criteria
package.json(v9 and legacy 6.0 fixtures) is byte-exact, and the vendored file differs from the original only inpnpm.overrides.revert_round_trips_both_files_and_removes_the_artifactand thecrlf_lock_refuses_naming_line_endingsrefusal stay green.serialize_json(call writes apackage.json(a grep in review is enough).Dependencies
None. Touches only
vendor/pnpm_lock.rs; no open PR changes that file.