Skip to content

Yarn berry PnP refusal misses a live Plug'n'Play project whose .yarnrc.yml sets nodeLinker through ${VAR} interpolation: agent apply exits 0 "not installed" and lock-only vendor passes the PnP gate (regression from #978) #1100

Description

[agent] Found by the scheduled Yarn Berry (2+) bug-hunt routine (ledger #305).

Summary

Since #978 (fix for #975/#539), socket-patch decides yarn Plug'n'Play from the configured nodeLinker instead of from the .pnp.* loader. It reads that value as a literal string (yarnrc_scalar). Yarn itself expands ${NAME} / ${NAME:-default} in rc values. So a project whose .yarnrc.yml says nodeLinker: "${NODE_LINKER:-pnp}" (or "${L}" with L=pnp in the environment) is a real, installed PnP project by yarn's own account, and yarn config get nodeLinker prints pnp. socket-patch reads the literal ${NODE_LINKER:-pnp}, which isn't pnp, so it treats the live .pnp.cjs as stale:

Release 4.0.0 refuses the same installed tree correctly (it keyed on the loader), so the agent half is a regression from #978 (1f3f1f5).

Impact

A refusal that should fire doesn't, and the replacement message is wrong. The user is told the dependency isn't installed, with exit 0, instead of being told PnP isn't supported and pointed at yarn patch. CI that gates on the exit code sees success, and nothing is patched. VEX isn't affected: it still omits the package (package_not_found).

Repro (Linux, yarn 4.18.1 or 4.0.2 via @yarnpkg/cli-dist)

mkdir proj && cd proj
echo '{"name":"app","version":"0.0.0","private":true,"dependencies":{"left-pad":"1.3.0"}}' > package.json
printf 'nodeLinker: "${L:-pnp}"\n' > .yarnrc.yml
touch yarn.lock && yarn install          # writes .pnp.cjs, no node_modules
yarn config get nodeLinker               # -> pnp
# stage .socket/manifest.json + after blob for pkg:npm/left-pad@1.3.0 (package/index.js), offline
socket-patch apply --json                # main: status success, skipped package_not_installed, exit 0
# lock-only: copy package.json, yarn.lock, .yarnrc.yml and .socket to a fresh dir
socket-patch vendor --offline --json     # main: vendor_service_offline_conflict (the PnP gate passed)

Control: the same steps with nodeLinker: "pnp" give apply → yarn_pnp_unsupported, exit 1, and vendor → vendor_yarn_berry_unsupported.

Expected vs actual

docs/testing/yarn-berry-compatibility.md: "Plug'n'Play is decided by the configured linker … vendor refuses a lock-only PnP checkout up front", and apply refuses PnP with yarn_pnp_unsupported. The configured linker here is pnp (yarn's own config get), so both should refuse.

Cell yarn says main e61a845 release 4.0.0
nodeLinker: "${L:-pnp}", installed PnP, agent apply (4.18.1, twice; 4.0.2) pnp exit 0, package_not_installed yarn_pnp_unsupported, exit 1
nodeLinker: "${L}" with L=pnp, agent apply (4.18.1) pnp exit 0, package_not_installed not run
Same projects, lock-only vendor --offline pnp PnP gate passes (vendor_service_offline_conflict) n/a (#539 era)
nodeLinker: "pnp" control pnp refused correctly refused

The same literal parser also disagrees with yarn in the fail-closed direction. These are rarer and loud, and are listed only so a fix can cover them. Stub loader + node_modules fixture, yarn 4.18.1:

.yarnrc.yml / env yarn config get nodeLinker socket-patch apply
"nodeLinker": node-modules (quoted key) node-modules refused as PnP
nodeLinker: with node-modules on the next line node-modules refused as PnP
{nodeLinker: node-modules} (flow map) node-modules refused as PnP
env yarn_node_linker=node-modules (yarn lower-cases env keys; Linux/macOS) node-modules refused as PnP
nodeLinker: "${L:-node-modules}" with L=pnp pnp applies (fail-open, same as above)

OS: Linux only. Windows env keys are case-insensitive, so the last env row doesn't apply there.

Suspect code

  • crates/socket-patch-core/src/crawlers/pkg_managers.rs:203 rc_node_linker, and :252 yarn_linker_is_pnp (any value other than literal pnp counts as non-PnP).
  • crates/socket-patch-core/src/formats/yarn/berry_gates.rs:195 yarnrc_scalar (no ${…} expansion, and only a column-0 unquoted key: value line).
  • crates/socket-patch-core/src/crawlers/pkg_managers.rs:184: only the upper-case YARN_NODE_LINKER is read.

A fail-closed option: treat a value containing ${ as unknown, so it counts as PnP while a loader exists and is refused for lock-only vendoring. Or expand it from the environment the way yarn does.

No probe runs: Linux only. First bad commit: 1f3f1f5 (#978); release 4.0.0 is good.

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions