fix(scan): surface yarn PnP refusal instead of silent success-0 across all modes - #201
Open
Mikola Lysenko (mikolalysenko) wants to merge 2 commits into
Open
fix(scan): surface yarn PnP refusal instead of silent success-0 across all modes#201Mikola Lysenko (mikolalysenko) wants to merge 2 commits into
Mikola Lysenko (mikolalysenko) wants to merge 2 commits into
Conversation
…efusal On any yarn Plug'n'Play project, all three scan modes exit 0 / status=success / scannedPackages=0 with no warning (the crawler leg is empty because node_modules is absent, and the lockfile-supplement leg swallows the flavor probe's PnP diagnosis), and apply exits 0 with the calm noManifest status because the documented yarn_pnp_unsupported refusal sits below the noManifest early-return — unreachable when scan never wrote a manifest. These tests fail on current main (7 failed / 20 passed / 3 ignored): - scan --json --yes --mode agent|hosted|vendored on a PnP fixture must surface an explicit warnings[] refusal (exit semantics deliberately unchanged) and touch no file - human-mode scan must print the refusal to stderr - the pnpm node-linker=pnp twin surfaces its own diagnosis - apply --json / human with NO manifest must refuse loudly with the existing yarn_pnp_unsupported envelope, not silent noManifest Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s all modes Root cause (verified on da58dee): - vendor/npm_flavor.rs detect_npm_lock_flavor correctly diagnoses PnP loaders (vendor_yarn_berry_unsupported / vendor_pnpm_pnp_unsupported) before any lockfile sniff, but lock_inventory.rs inventory_npm_lock matched Err(_) and fell through to the Rush fallback returning None, DISCARDING the diagnosis — so scan's lockfile-supplement leg came back empty with no warnings channel. - Under PnP node_modules/ is absent, so the crawler leg is also empty; scan hit the package_count==0 early-return and printed status=success / scannedPackages=0 in all three modes (hosted: plus an empty redirect block). - apply's YarnBerryPnP check sat BELOW the noManifest early-return, so the documented loud yarn_pnp_unsupported refusal was unreachable when scan (which cannot discover PnP packages) never wrote a manifest. Fix: - core: inventory_npm_lock now returns Result<Option<..>, UnsupportedNpmLayout>, propagating the two PnP diagnosis codes; every other probe error keeps the Rush fallback and the calm Ok(None). New inventory_project_diagnosed exposes the refusals; inventory_project keeps its signature. - scan: an additive top-level warnings[] ({code, detail}, omitted when empty) rides both the empty-discovery and the non-empty (polyglot) JSON envelopes, plus a stderr 'Warning (<code>): ...' line on the human path. Codes: yarn_pnp_unsupported (same vocabulary as apply's refusal, remedy 'yarn patch <pkg>') and pnpm_pnp_unsupported (the node-linker=pnp twin, pnpm remedies). Exit code and status field are DELIBERATELY unchanged (exit 0 / success — same posture as hosted refusals, which exit 0 with redirected=0). - apply: the YarnBerryPnP refusal is hoisted above the noManifest early-return (extracted into refuse_yarn_pnp, same envelope + exit 1 as the with-manifest gate). CLI_CONTRACT.md documents the carve-out. Tests: the previous commit's 7 RED e2e tests now pass (scan agent/ hosted/vendored JSON warning + no-mutation, human stderr, pnpm twin, apply no-manifest JSON + human refusal); the lock_inventory unit test that pinned the swallow now asserts the propagated diagnosis. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
Bugbot Autofix is ON. A cloud agent has been kicked off to fix the reported issues.
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit cd894fe. Configure here.
This was referenced Aug 19, 2026
Mikola Lysenko (mikolalysenko)
enabled auto-merge (squash)
August 19, 2026 14:57
Wenxin Jiang (Wenxin-Jiang)
approved these changes
Aug 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

P0: yarn Plug'n'Play projects silently no-op in every mode
On any yarn PnP project (yarn 2/3/4, plain PnP and zero-install), all three modes silently did nothing:
scan --json --yes --mode agent|hosted|vendoredexited 0 withstatus: "success",scannedPackages: 0, no warnings (hosted additionally printed an emptyredirectblock). The user believed they were protected; nothing was checked.apply --jsonexited 0 withstatus: "noManifest"— the documented loud refusal (errorCode: yarn_pnp_unsupported, pinned bytests/e2e_safety_yarn_pnp.rs) was unreachable, because scan (which cannot discover PnP packages) never writes a manifest.Matrix evidence (adversarially verified, 11 cells, one root cause)
Empirically reproduced with real production patch data on yarn 2.4.3 / 3.8.7 / 4.6.0 across agent/hosted/vendored × plain-PnP/zero-install: every PnP cell was a silent success-0 no-op +
applynoManifest. The 18 supported (node-modules linker) cells passed, and yarn 2/3 nm refusal paths were clean — this is PnP-specific, one root cause. Intact repro dirs live underworktrees/yarn-matrix-e2e/target/matrix/(e.g.y4-agent/y4-agent-pnp,y4-hosted/y4-hosted-zero).Root cause (at da58dee)
crates/socket-patch-core/src/vendor/npm_flavor.rs:125-149—detect_npm_lock_flavorcorrectly diagnoses PnP via the.pnp.*markers (vendor_yarn_berry_unsupported/vendor_pnpm_pnp_unsupported) before any lockfile sniff.crates/socket-patch-core/src/vendor/lock_inventory.rs:109-115—inventory_npm_lockmatchedErr(_)and fell through to the Rush fallback returningNone, discarding the diagnosis, so the lockfile-supplement discovery leg (scan/discovery.rs) returned empty with no warnings channel.node_modules/is absent, so the crawler leg is also empty; scan hit thepackage_count == 0early-return (scan/mod.rs) and printed success before any mode-specific gate ran.apply.rs,detect_npm_pkg_manager→YarnBerryPnP) sat below the noManifest early-return, so it never fired without a manifest.Fix
inventory_npm_locknow returnsResult<Option<…>, UnsupportedNpmLayout>, propagating the two PnP diagnosis codes instead of conflating "unsupported layout" with "no lockfile". Every other probe error (missing lock, bun.lockb, unsupported versions) keeps the Rush fallback and the calmOk(None). Newinventory_project_diagnosedexposes the refusals;inventory_projectkeeps its signature (vendor/repair callers unchanged).warnings[]({code, detail}objects, omitted when empty) on both the empty-discovery and the non-empty (polyglot) JSON envelopes, plus a stderrWarning (<code>): …line on the human path — in all three modes. Codes:yarn_pnp_unsupported(one vocabulary with apply's refusal; remedyyarn patch <pkg>) andpnpm_pnp_unsupported(thenode-linker=pnptwin, surfaced through the same channel).YarnBerryPnPrefusal is hoisted above the noManifest early-return (extracted intorefuse_yarn_pnp; identical envelope + exit 1 as the with-manifest gate), so a PnP checkout refuses loudly even when no manifest exists.warnings[]channel and the deliberate noManifest carve-out.Exit semantics — deliberately conservative
statusunchanged (exit 0 /success+ warnings entry). Precedent: hosted refusals already exit 0 withredirected: 0(known, deliberately-unchanged residual from the pnpm sweep). The warning is the machine-readable signal that nothing was checked.yarn_pnp_unsupportedsemantics (exit 1) — now also on the no-manifest path. Note: a PnP project with an install hook and no manifest previously "passed" installs silently; it now fails them, exactly as it already did once a manifest existed.Tests (TDD, RED first — see commit order)
7e442ba) adds 7 e2e tests that fail on main (7 failed / 20 passed / 3 ignored): scan agent/hosted/vendored JSON warning + whole-tree no-mutation snapshot, human-mode stderr, the pnpm twin, apply no-manifest JSON + human refusal.lock_inventoryunit test that pinned the swallow (unsupported_flavors_yield_none) intopnp_layouts_propagate_the_diagnosis_instead_of_yielding_none(PnP → propagated diagnosis; genuinely-None cases kept). The unit rewrite rides the fix commit because the new assertion cannot compile against the old signature.cargo test -p socket-patch-core(22 binaries, 0 failures incl. 2265 lib tests), full defaultcargo test -p socket-patch-cli(0 failures;e2e_safety_yarn_pnp27 passed),cargo clippy --workspace --all-features -- -D warningsclean. Formatted only touched files (nocargo fmt --all).y4-agent-pnp): old binary → silent success-0 + noManifest exit 0; new binary →warnings[]: yarn_pnp_unsupportedin all three modes (hosted keeps its empty redirect block) andapply --json→error.code: yarn_pnp_unsupported, exit 1; no files touched, no.socket/created.🤖 Generated with Claude Code
Note
Medium Risk
Behavior change for PnP projects: install-hook
applywithout a manifest now exits 1 instead of silent 0, and scan contract adds machine-readable warnings while keeping success exit codes—intentional but user-visible for CI and hooks.Overview
Fixes a P0 where yarn and pnpm Plug'n'Play projects looked healthy while npm deps were never discovered: empty crawler + lockfile inventory that swallowed PnP diagnosis produced
scanexit 0 /scannedPackages: 0with no warnings, andapplyfell through to calmnoManifestexit 0.Core —
inventory_npm_locknow returnsResultand propagatesUnsupportedNpmLayoutfor PnP loaders instead of treating them like “no lockfile.”inventory_project_diagnosedexposes those refusals alongside other ecosystems’ lock entries;inventory_projectunchanged for vendor/repair callers.scan — Lockfile supplement collects layout refusals and surfaces them as additive top-level
warnings[]({code, detail}) on JSON envelopes (empty and polyglot non-empty paths) plusWarning (<code>): …on stderr. Codes align with apply:yarn_pnp_unsupported,pnpm_pnp_unsupported. Exit code andstatusstay exit 0 /success(same posture as hosted refusals).apply — Yarn berry PnP detection runs before the missing-manifest early return via shared
refuse_yarn_pnp, so PnP checkouts get exit 1 /yarn_pnp_unsupportedeven when scan never created.socket/.CLI_CONTRACT.mddocuments the scan warning channel and the noManifest carve-out. E2e coverage ine2e_safety_yarn_pnp.rspins all three scan modes, human stderr, pnpm twin, and no-manifest apply.Reviewed by Cursor Bugbot for commit cd894fe. Configure here.