Repository navigation
Fix berry workspace globs with regex groups (#1490) - #1493
Merged
Mikola Lysenko (mikolalysenko) merged 8 commits intoOct 13, 2026
Merged
Conversation
Assisted-by: Claude Code:claude-opus-5-5
Running socket-patch from a yarn berry workspace member whose root lists it with a regex group (packages/(a|b)), a POSIX class ([[:alpha:]]) or a `..` segment (packages/a/../b) pinned nothing and exited 0, so the next install at the root stayed unpatched. Yarn berry matches workspaces with picomatch, which reads all three, while our matcher read them as literal text and saw no workspace root. The yarn berry dialect now reads that syntax the way picomatch does, so the run is refused with redirect_workspace_lockfile_elsewhere and names the root to run from. A group without `|` stays literal in a pattern fast-glob treats as a plain path, as yarn reads it, and a negated group now excludes the members it names. npm, yarn classic, Bun, vlt, pnpm and uv keep their grammar. Assisted-by: Claude Code:claude-opus-5-5
Review of the first pass found yarn berry patterns it still misread,
so a run from a member could again pin nothing and exit 0, or refuse
a directory yarn never lists:
- `(a|b)?`, `(a|b)+`, `[ab]+` and `(a+|c)`: picomatch reads `?` and
`+` there as regex quantifiers.
- `(a|[)])`: a `)` or `|` inside a class closed or split the group.
- `packages/{(a),c}`: fast-glob decides static vs dynamic per brace
alternative, so `(a)` stays a literal path there.
- `packages/*/../b` lists nothing, and `!packages/a/../b` excludes
nothing: fast-glob resolves `..` only through a static prefix and
never in an ignore.
- `(a|)` and `(a/b|c)`: an alternative is matched within one path
component.
Regex groups are now rewritten into the extglob groups the matcher
already reads, instead of being spliced in as text, and extglob groups
skip classes whole for every package manager.
Assisted-by: Claude Code:claude-opus-5-5
Running `scan` or `get` in hosted mode from a yarn berry workspace member that the root lists through a regex group, a `?` quantifier, a POSIX class or a `..` segment must refuse with exit 1 and leave the root lock untouched. Assisted-by: Claude Code:claude-opus-5-5
Mikola Lysenko (mikolalysenko)
marked this pull request as ready for review
October 13, 2026 17:17
Collaborator
Author
|
BugBot review Generated by Claude Code |
Bugbot on #1493 found two shapes that still pinned nothing and exited 0 from a yarn berry member: - `(packages/ui|packages/core)`: a group alternative holding `/` was dropped, so neither member was listed. Such groups are now spelled out like brace sets and matched across path components. - `packages/(a)/../b`: fast-glob reads a static pattern as a literal path, so its `..` resolves even after the `(a)` segment. Assisted-by: Claude Code:claude-opus-5-5
Collaborator
Author
|
BugBot review Generated by Claude Code |
5 tasks
A `?(` or `+(` right after a regex group or a class was read as a quantifier, so the extglob it opens matched literally. Members listed by `packages/(a|b)?(c|d)`, `packages/(a|b)+(c)` or `packages/[ab]+(c)` were then missed and the run exited 0 with nothing pinned, and the same `!` pattern excluded nothing. Those characters now open the extglob, as picomatch reads them. Assisted-by: Claude Code:claude-opus-5-5
Collaborator
Author
|
BugBot review Generated by Claude Code |
When a regex group with a `/` in an alternative was spelled out, a `?` or `+` after it, and a `+` inside it, were left as plain text. Members listed by `(packages/a|apps/b)?x` or `(packages/a+|apps/b)` were then missed and the run exited 0 with nothing pinned. A `?` now also yields the spelling without the group, a `+` keeps one occurrence, and each alternative is read as inside a group. Assisted-by: Claude Code:claude-opus-5-5
Collaborator
Author
|
BugBot review Generated by Claude Code |
A `?` or `+` after a yarn berry regex group with a `/` in an alternative skips or repeats a path separator, which can't be matched component by component. Approximating it either missed members (the run exited 0 with nothing pinned) or listed directories yarn does not. Such a pattern now fails closed: it lists every directory, so a run from under that root is refused, and as a `!` pattern it excludes nothing. Assisted-by: Claude Code:claude-opus-5-5
Collaborator
Author
|
BugBot review Generated by Claude Code |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 18c5b35. Configure here.
Tanmay Singla (Tanmay182003)
approved these changes
Oct 13, 2026
Mikola Lysenko (mikolalysenko)
deleted the
agent/fix-berry-workspace-glob-grammar
branch
October 13, 2026 18:59
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.
LLM Description written by Claude Code:claude-opus-5-5
Fixes #1490
Summary
A hosted
scan/getrun from a yarn berry workspace member nowrefuses with
redirect_workspace_lockfile_elsewhere, naming the root,when the root's
workspacesreaches the member through picomatch-onlysyntax. Before this fix the run pinned nothing and exited 0, and the
next
yarn installat the root installed the unpatched package.Root cause
utils/workspace_globs.rsis the matcher that decides workspacemembership. Since #1480 it reads each root lock's dialect, but yarn
berry matches
workspaceswith fast-glob / picomatch, and the matcherread the following picomatch syntax as literal text:
packages/(a|b),packages/pkg-(a|b)[[:alpha:]],[[:digit:]]x..segments:packages/a/../bNo dialect listed the member, so
hosted/governing_root.rstreated itas a standalone project with no lock.
Fix
WorkspacesDialectgets apicomatchflag, which is on only for yarnberry. With it on:
ExtglobMatcheralready reads:
(a|b)becomes@(a|b),(a|b)?becomes?(a|b)and
(a|b)+becomes+(a|b). Likewise[ab]+becomes+([ab]),and
x+inside a group becomes+(x). An alternative containing/is dropped, because it can never match a single path component.
|is still a group only when fast-glob treats thatbrace alternative as dynamic, or when the pattern is a
!(ignore)pattern. Otherwise it is literal, as yarn reads
packages/(a)...segment is resolved only through a static prefix. It isliteral in
!patterns, so it excludes nothing, as fast-glob does.extglob_groupnow skips[...]classes as whole units for everydialect, so a
)or|inside a class no longer ends or splits thegroup. Matching for npm, yarn classic, Bun, vlt, pnpm and uv is
otherwise unchanged. The
redirect_workspace_lockfile_elsewhererow inCLI_CONTRACT.md now documents the berry grammar.
Bugbot's review of 7d6a48a found two issues, both fixed in 3eef5c3:
/now matches across pathcomponents. This fails closed.
..now resolves in a static pattern.Later Bugbot and reviewer rounds were fixed in 267ed2f, 44fd58c and 18c5b35:
?(and+(right after a group or class now open an extglob.+survive the expansion ofa
/-spanning group.?or+that quantifies a/-spanning group now fails closed:it lists every directory, and as a
!pattern excludes none.Bugbot's review of 18c5b35 is clean.
I ran
/code-review highon the first pass. It found 10 issues, allnow fixed (commit 1543665), with one deliberate exception: a static
packages/(a)still lists a literalpackages/(a)directory. Thereview probed fast-glob 3.3.3, which lists nothing there. I kept the
broader reading because it fails closed: the worst case is a loud
refusal, never a silent exit 0.
Tests
utils::workspace_globs::tests::yarn_berry_reads_picomatch_syntax(every row of the issue's matrix plus the review cases)hosted::governing_root::tests::package_json_workspaces_read_in_the_root_locks_dialect(berry rows:(a|b),[[:alpha:]],a/../brefuse;!packages/(b|c)and static(b)do not)in_process_redirect_pnpm::hosted_run_from_berry_picomatch_workspace_member_refuses(scan and get, exit 1, lock untouched)Local gate on 7d6a48a (3eef5c3 re-ran the core workspace_globs and governing_root tests,
in_process_redirect_pnpm, fmt and clippy, all clean):cargo fmt --all -- --checkpassed, andcargo clippy --workspace --all-features -- -D warningspassed with no warnings.cargo test --workspace --all-features --no-fail-fastpassed everything except 13 tests, all of which fail for environment reasons. Twelve inject write failures withchmod, which the sandbox's root user bypasses:covgap_commands_vendor3,in_process_redirect3,repair2 and core unit tests 4. The thirteenth,e2e_bun_lockb::binary_shared_bundled_record_hosted_pin_is_managed, needspatches-api.socket.dev, which the sandbox proxy blocks. None of them touches workspace globs, and CI runs as non-root with network access.🤖 Generated with Claude Code
Generated by Claude Code