Repository navigation
Fix pnpm YAML settings read by drifted readers (#1918) - #1973
Conversation
Assisted-by: Claude Code:claude-opus-5-5
The pnpm settings readers decode quoted values and duplicate keys differently depending on the caller (#1918). These tests pin the pnpm YAML reading: '' is one quote, an unpaired quote is kept, the last top-level key wins and a trailing comment is not the value. Assisted-by: Claude Code:claude-opus-5-5
A quoted value in pnpm-workspace.yaml, .yarnrc.yml or a YAML .modules.yaml was decoded differently depending on which feature read it: modulesDir: 'it''s' or lockfileDir: "a'" could resolve to different paths, and a duplicated packageManager key in .modules.yaml was read as the first value by one reader and the last by another (#1918). Every top-level settings scalar now goes through formats::pnpm::workspace::top_level_scalar, which decodes the value with the file's own YAML single/double-quote decoders (last key wins, unpaired quotes kept, empty kept as empty). .modules.yaml is read once, by formats::pnpm::modules_yaml::field, for the crawler's virtualStoreDir, the version gate and the redirect upstream. The three private unquote helpers are gone. Assisted-by: Claude Code:claude-opus-5-5
Review follow-ups for #1918: a double-quoted value with an escaped quote before " #" is no longer cut at the quote, YAML escapes (surrogate pairs, \x, \U, \b) decode as pnpm reads them and a malformed one is not guessed, a block scalar header is not read as the value, and the trustLockfile, registries and yarn nested readers use the same decoder instead of trimming quote characters. Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
There was a problem hiding this comment.
✅ 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 fab9b08. Configure here.
|
Ready for review at
Generated by Claude Code |
Final review briefWhat it does Risk: low. Read-only parsing of settings files, no writes changed. The behaviour shifts only for odd inputs (quoted values with Look here
Verified
Changes I made: none Open questions: none. The lockfile-grammar unquotes are deliberately left for a follow-up, as the description says Auto-merge is armed, so approving sends this straight to the merge queue. Generated by Claude Code |
Wenxin Jiang (Wenxin-Jiang)
left a comment
There was a problem hiding this comment.
[agent] Reviewed fab9b0815f88ab5e2ccf9aa2dff270f992ae1729. Checked shared YAML scalars, escapes, duplicate-key precedence and pnpm version/store/config readers. No major correctness issues found. Validation: source and regression-test review plus the available exact-head CI checks; no local test pass is claimed.
LLM Description written by Claude Code:claude-opus-5-5
Fixes #1918
What changes for users
A quoted value in
pnpm-workspace.yaml,.yarnrc.ymlor a pre-pnpm-10 YAMLnode_modules/.modules.yamlnow reads the same no matter which feature readsit. Before this PR,
modulesDir: 'it''s',lockfileDir: "a'"or a duplicatedpackageManager:key could resolve to different directories, registries orpnpm versions depending on the caller (crawler, hosted governing-root lookup,
version gate, redirect upstream, trust guidance).
Root cause
"Read the last top-level
key:of a pnpm YAML settings file and unquote it" waswritten several times on top of the shared
formats::pnpm::workspace::top_level_key,each copy with its own unquote rule:
workspace::yaml_top_level_value:trim_matches(['"', '\''])(strips unpairedand repeated quotes, leaves
''doubled)governing_root::unquote_scalar: one matched pair, no''decodingpnpm_layout::unquote_yaml_scalar:''+ JSON escapes.modules.yamlread three ways (npm_crawler,formats/pnpm/version.rs,redirect/upstream/npm.rs): first key vs last key, comment kept vs strippedhosted/guidance.rstrustLockfile(first key,trim_matches) and the nestedregistries:/ yarnnpmScopesreaders (trim_matches), found by/code-review highFix
formats::pnpm::workspace::top_level_scalar(text, key): last top-level key,value decoded by
yaml_scalar(the file's own single/double-quote decoders);empty is
Some(""), a block-scalar header (|,>-) isNone.yaml_top_level_valueis that with empty read as unset.double_quotednow decodes YAML 1.2 escapes (\x,\uincl. surrogate pairs,\U,\b, ...) and refuses malformed ones;strip_commenthonours\"so anescaped quote before
#no longer truncates the value.formats::pnpm::modules_yaml::field: the one.modules.yamlreader (JSON onpnpm 10+, the shared scalar reader before) used by the crawler's
virtualStoreDir, the version gate and the redirect upstream.governing_root,pnpm_layout,guidance(trustLockfile),yaml_block_valueand
yaml_path_inroute through the shared decoder; the three private unquotehelpers and
pnpm_spec_version's re-trim are gone.Not in this PR
The issue's later audit comment also points at the pnpm lockfile grammar's
one-pair unquotes (
grammar::unquote,lines::unquote_value) and the twopnpm@xversion parsers. Those read lock entries and version specs, not settingsscalars, and have many callers; folding them is a separate change.
Tests (red → green)
Written first on
0295a9d, all four failed on main:yaml_top_level_valueworkspace::tests::yaml_top_level_value_decodes_yaml_quotingit''sgoverning_rootlockfileDirworkspace_lockfile_dir_reads_the_top_level_key../it''s.modules.yamlpackageManager (version gate)version::tests::modules_yaml_pin_reads_the_last_key.modules.yamlvirtualStoreDir (crawler)npm_crawler::tests::modules_yaml_virtual_store_dir_uses_the_shared_reader.pnpm # defaultPlus
workspace::tests::top_level_scalar_table(plain, comment, escapes, surrogates,'', empty, unpaired quotes, malformed escape, block scalar, BOM, CRLF),modules_yaml::tests::reads_json_and_yaml_records_alike,guidance::tests::workspace_trust_plan_reads_the_last_key_as_yaml,upstream::npm::tests::nested_settings_values_decode_yaml_quoting.Local gates:
cargo fmt --all -- --check: okcargo clippy --workspace --all-features -- -D warnings: okcargo test --workspace --all-features --no-fail-fast: all pass except tests thatfail identically on
mainin this container (it runs as uid 0, so chmod-basedwrite-failure tests can't fail writes, and the live patch API is unreachable):
core
copy_tree/vlt_heal/pypi_poetry/pypi_requirementswrite-failure tests,in_process_redirectx3,repairx2,e2e_bun_lockbx2 (live blob fetch, see Fix bun.lockb nightly e2e reaching the live blob API #1905).Checked on
origin/mainfor the core andin_process_redirectones.e2e_safety_pnpm --ignored(4 ok),e2e_redirect_pnpm_build --ignored --skip pnpm_pinned_matrix(11 ok, pnpm 10.28),scan_pnpm_relocated_store_cwd_e2e(41 ok),in_process_redirect_pnpm(23 ok).npm/,pypi/,gem/) change needed: they only dispatch to the binary./code-review high
9 findings. Fixed: escaped-quote comment truncation, missing escapes / surrogate
pairs,
registries:/yarn nested trim,trustLockfilefirst-key read,\u+041accepted, block-scalar header read as value,
pnpm_spec_versionre-trim, theversion.rs one-line wrapper. Not fixed: "
.modules.yamlparsed into a full JSONtree per call": the callers already did that before this PR, so it's not a
regression, and caching the record is a separate perf change.
https://claude.ai/code/session_011f9aeRTcq7ezZiUt7xTkrb
Generated by Claude Code