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
Hosted scan/get from an npm workspace member still pins nothing and exits 0 when the root's workspaces glob uses braces or a character class (packages/{a,b}, packages/[a-c]), because the #884 refusal's matcher doesn't support them #1071
[agent] Found by the scheduled npm bug-hunt routine (ledger #302).
Summary
#901 (fixing #884) refuses a hosted scan / get run from an npm workspace member with redirect_workspace_lockfile_elsewhere. It does this by matching the member's path against the root package.jsonworkspaces patterns. The matcher in governing_root.rs only understands *, ? and **. npm resolves workspaces with full glob syntax, so it also accepts brace sets (packages/{a,b}) and character classes (packages/[a-c]). For roots that use those, the member isn't recognised. The run then falls back to the exact #884 failure: it finds the member's copy, sees no lock in the member directory, pins nothing, prints "Switched 0 packages" and exits 0. A later npm ci installs the unpatched package.
Impact
Running from a member of such a workspace still reports success while the project stays unpatched. That is the silent failure #884 was meant to close. Vendored mode from the same member fails closed (exit 1), so only hosted is affected.
Repro (Linux, npm 10.9.4, local mock of the patch API serving one free patch for left-pad@1.3.0)
mkdir -p p/packages/a p/packages/b &&cd p
echo'{"name":"root","version":"1.0.0","workspaces":["packages/{a,b}"],"dependencies":{"left-pad":"1.2.0"}}'> package.json
echo'{"name":"a","version":"1.0.0","dependencies":{"left-pad":"1.3.0"}}'> packages/a/package.json
echo'{"name":"b","version":"1.0.0"}'> packages/b/package.json
npm install # npm treats packages/a and packages/b as workspaces; a gets a nested left-pad@1.3.0cd packages/a
socket-patch scan --mode hosted --yes --api-url $MOCK --org o --api-token fake --patch-server-url $MOCK# Switched 0 packages to hosted patches; rewrote 0 files.# Warning: No package-lock.json / npm-shrinkwrap.json present# exit 0cd ../.. && rm -rf node_modules packages/a/node_modules && npm ci
head -c 30 packages/a/node_modules/left-pad/index.js # unpatched upstream bytes
With "workspaces": ["packages/*"] (or packages/a*) the same run is correctly refused with redirect_workspace_lockfile_elsewhere, exit 1.
Expected vs actual
Expected (CLI_CONTRACT.md, redirect_workspace_lockfile_elsewhere): when "the nearest ancestor package.json whose workspaces … matches the directory holds package-lock.json…", the run is refused before any write with exit 1, and the message names the workspace root. The directory is a workspace npm installs from the root lock, so that condition holds.
Actual: exit 0 and status: success, with redirected empty. The root package-lock.json is byte-identical, and the next npm ci installs unpatched bytes.
Matrix (Linux, main 05ecc6e; each cell run twice)
npm
packages/{a,b}
packages/[a-c]
packages/a* (control)
7.24.2
fail: scan 0, get 0, nothing pinned
fail
pass (refused, exit 1)
8.19.4
fail
fail
pass
10.9.4
fail
fail
pass
12.2.0 (Node 24.21)
fail
fail
pass
In every cell, get <uuid> --mode hosted from the member behaves like scan, and scan --mode vendored from the member exits 1 (fails closed). In every failing cell, npm recorded the member as a workspace in the lock and npm ci installed unpatched bytes. macOS and Windows weren't probed, but the matcher is platform-independent.
First bad version
This isn't a regression. Before #901 (v4.0.0 and earlier) every member run behaved this way. The fix covers only the simple-glob subset.
Suspect code
crates/socket-patch-core/src/hosted/governing_root.rs:363workspaces_include / :388path_glob_matches / :398segment_glob_matches: no {a,b} alternation and no [...] classes, so segment_glob_matches compares { and [ literally. :297 then continues, as if the ancestor weren't a workspace root.
The same matcher also decides yarn and Bun workspaces roots, which accept the same globs. I tested npm only.
[agent] Triage: priority:p1 (npm). Confirmed on main (05ecc6e): segment_glob_matches in crates/socket-patch-core/src/hosted/governing_root.rs only handles *, ? and **, and workspaces_include does no brace expansion, so packages/{a,b} and packages/[a-c] never match a member. No open PR touches it.
[agent] Claiming this issue (with #942; shared root cause: the hosted workspace-member refusal in governing_root.rs only recognises package.json workspaces matched with a partial glob grammar). Branch: agent/fix-workspace-member-refusal-globs. Claim-ID: 2026-10-07T18:20:55Z-6e5d91
Activity
mikolalysenko commentedon Oct 7, 2026
[agent] Triage: priority:p1 (npm). Confirmed on main (05ecc6e):
segment_glob_matchesincrates/socket-patch-core/src/hosted/governing_root.rsonly handles*,?and**, andworkspaces_includedoes no brace expansion, sopackages/{a,b}andpackages/[a-c]never match a member. No open PR touches it.Generated by Claude Code
mikolalysenko commentedon Oct 7, 2026
[agent] Claiming this issue (with #942; shared root cause: the hosted workspace-member refusal in governing_root.rs only recognises package.json workspaces matched with a partial glob grammar). Branch: agent/fix-workspace-member-refusal-globs. Claim-ID: 2026-10-07T18:20:55Z-6e5d91
Generated by Claude Code
mikolalysenko commentedon Oct 7, 2026
[agent] Draft PR: #1073
Generated by Claude Code
Refuse hosted runs from vlt and brace-glob members
Refuse hosted runs from vlt and brace-glob members