Repository navigation
Hosted get/scan from a yarn berry workspace member still pins nothing and exits 0 when the root's workspaces glob uses a regex group (packages/(a|b)), a POSIX class ([[:alpha:]]) or a .. segment #1490
Description
[agent] Found by the scheduled Yarn Berry (2+) bug-hunt routine (ledger #305).
Summary
#1097 (fixed by #1480) taught the workspace-member refusal (redirect_workspace_lockfile_elsewhere) to read workspaces globs the way each package manager does, including yarn berry's negation and extglobs. Yarn berry matches workspaces with picomatch (via globby / fast-glob), which also accepts some syntax the matcher in workspace_globs.rs doesn't:
- a plain regex group with alternatives:
packages/(a|b),packages/pkg-(a|b) - POSIX bracket classes:
packages/[[:alpha:]],packages/[[:digit:]]x - a
..segment:packages/a/../b
With any of these, yarn lists the directory as a workspace and installs it from the root yarn.lock. The refusal doesn't match it, though, so get <uuid> --mode hosted run from the member pins nothing, prints a per-package redirect_unconfirmed skip plus a redirect_npm_no_lockfile warning, and exits 0 with status: "success". The next yarn install at the root installs the unpatched package. This is the #884 / #1071 / #1097 failure again, for a different set of glob syntax.
Impact
A user (or CI job) that runs socket-patch from the package directory gets a success exit while nothing is patched. The exit code and status are wrong. VEX wasn't tested in this layout.
Repro
These are real yarn installs, driven by a probe test built on the berry_hosted_project fixture from crates/socket-patch-cli/tests/e2e_redirect_yarn_berry_build.rs. That fixture provides the mock API and grants the patch for left-pad@1.3.0. For each case:
mkdir -p ws/packages/a && cd ws
echo '{"name":"wsroot","private":true,"workspaces":["packages/(a|b)"]}' > package.json
printf 'nodeLinker: node-modules\nenableGlobalCache: false\n' > .yarnrc.yml
echo '{"name":"mem","version":"1.0.0","dependencies":{"left-pad":"1.3.0"}}' > packages/a/package.json
yarn install # root yarn.lock, berry __metadata
yarn workspaces list --json # lists packages/a ("name":"mem")
cd packages/a
socket-patch get <uuid> --mode hosted --json --yes --cwd . \
--api-url <mock> --org org --api-token x
echo "exit=$?" # exit=0, status "success", applied 0The control ["packages/*"] (and packages/@(a|b), packages/[a-c], packages/[!b], packages/./a) is correctly refused, with exit 1 and redirect_workspace_lockfile_elsewhere.
Expected vs actual
CLI_CONTRACT.md (the redirect_workspace_lockfile_elsewhere row) says workspaces patterns are "read the way the root's lock's package manager reads them (#1097)", and the run is "Refused before any takeover or write … exit 1". It also lists the shared grammar (*, ?, **, braces, [a-c] / [!a], extglobs) and leaves out regex groups and POSIX classes, but yarn berry accepts both.
- Expected: exit 1,
redirect_workspace_lockfile_elsewhere, naming the root, as forpackages/*. - Actual: exit 0,
status: "success",applied: 0, eventredirect_unconfirmed("no lockfile entry pinning it could be rewritten"), warningredirect_npm_no_lockfile.
Matrix (Linux, main 285a5e8, node-modules linker)
Root workspaces |
member | yarn 4.0.2 lists it | yarn 4.18.1 lists it | socket-patch (both versions) |
|---|---|---|---|---|
packages/(a|b) |
packages/a | yes | yes | exit 0, nothing pinned |
packages/pkg-(a|b) |
packages/pkg-a | yes | yes | exit 0, nothing pinned |
packages/[[:alpha:]] |
packages/a | yes | yes | exit 0, nothing pinned |
packages/[[:digit:]]x |
packages/1x | yes | yes | exit 0, nothing pinned |
packages/a/../b |
packages/b | yes | yes | exit 0, nothing pinned |
packages/(a) (single group) |
packages/a | no (literal) | no | exit 0 (consistent) |
packages/*, !packages/(b|c) |
packages/b | no (excluded) | no | exit 1 refusal (a false refusal in the other direction; loud) |
packages/*, packages/@(a|b), packages/[a-c], packages/[!b], packages/[^b], packages/./a, packages/**/b, packages/*/, packages/!(z), packages/a?(x) |
— | yes | yes | exit 1 refusal (correct) |
macOS and Windows weren't probed (this routine's probe branches are blocked), but the matcher is OS-independent.
Suspect code
crates/socket-patch-core/src/utils/workspace_globs.rs:156(pattern_matches) and:303(path_glob_matches/char_class): there's no regex-group alternation outside an extglob prefix, no[[:class:]], and a..segment is matched literally.crates/socket-patch-core/src/hosted/governing_root.rs:502: when no dialect matches, the walkcontinues, so the member reads as a standalone lockless project.
Failing closed when a pattern holds syntax the matcher can't read ((, [[:, ..) would avoid the silent success.
Bisect
This isn't a regression: the matcher never read these shapes. The berry dialect arrived in #1480.
Activity
mikolalysenko commentedon Oct 13, 2026
[agent] Claiming this issue (shared root cause: the workspace glob matcher has no reading of yarn berry's picomatch-only syntax — regex groups, POSIX classes and
..segments). Branch: agent/fix-berry-workspace-glob-grammar. Claim-ID: 2026-10-13T16:22:34Z-90959aGenerated by Claude Code
mikolalysenko commentedon Oct 13, 2026
[agent] Draft PR: #1493
Generated by Claude Code
Read yarn berry workspace regex groups (#1490)