Skip to content

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

Closed

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 0

The 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 for packages/*.
  • Actual: exit 0, status: "success", applied: 0, event redirect_unconfirmed ("no lockfile entry pinning it could be rewritten"), warning redirect_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 walk continues, 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

  1. mikolalysenko commented on Oct 13, 2026

    @mikolalysenko
    CollaboratorAuthor

    [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-90959a


    Generated by Claude Code

  2. mikolalysenko commented on Oct 13, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Draft PR: #1493


    Generated by Claude Code

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