Skip to content

Fix Bun 1.3+ bun.lockb read as pre-1.3 (#1492) - #1496

Merged
Mikola Lysenko (mikolalysenko) merged 7 commits into
mainfrom
agent/fix-bun-lockb-dialect
Oct 13, 2026
Merged

Mikola Lysenko (mikolalysenko) merged 7 commits into
mainfrom
agent/fix-bun-lockb-dialect

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

LLM Description written by Claude Code:claude-opus-5-5

Fixes #1492

Summary

A Bun 1.3+ project that keeps a binary bun.lockb (saveTextLockfile = false) and excludes a member with "!packages/b" can patch that member again in hosted and vendored mode. Since 5.0.1, socket-patch treated the excluded member as a workspace member. It refused with redirect_workspace_lockfile_elsewhere (hosted) or vendor_lockfile_missing (vendored) and named a root lock Bun never reads for that member.

Root cause

lock_dialect in crates/socket-patch-core/src/hosted/governing_root.rs (#1097, #1480) picks how a root's workspaces globs are read from the lock at that root. Every lone bun.lockb was read with the pre-1.3 Bun rules, which ignore !. Bun 1.3+ applies ! whichever lock format it writes. It marks a bun.lockb with a cNfGvRsN extension that carries configVersion, the binary twin of the text lock's top-level key, which the bun.lock branch already checks.

Fix

Test evidence

  • New hosted::governing_root::tests::bun_lockb_with_a_config_version_tag_honours_negated_workspaces uses real Bun 1.3.14, 1.4.2 workspace-root and 1.4.2 scoped-overrides locks (the last is the new bun-lockb/1.4.2-scoped-overrides fixture, written on Linux) (no refusal, member lock not a stray), plus 1.2.23 and a non-binary lock (still refused).
    • Red before the fix: assertion left == right failed: bun.lockb of 1646 bytes; left: Some("redirect_workspace_lockfile_elsewhere"), right: None.
    • Green after: cargo test -p socket-patch-core --all-features --lib -- governing_root bun_lockb → 67 passed.
  • New vendor::bun_lockb::tests::config_version_is_read_from_tagged_locks_only checks every historical fixture 0.1.1 to 1.4.2, plus workspace "late-dependent" locks.
  • cargo fmt --all -- --check: clean. cargo clippy --workspace --all-features -- -D warnings: clean.
  • cargo test -p socket-patch-core --all-features --lib: 6453 passed and 4 failed. All 4 are permission-denial tests (copy_tree symlinked root, vlt_heal unremovable lock, poetry/requirements write failure) that cannot fail as intended when the sandbox runs as root. They are unrelated to this diff, and CI runs as non-root.
  • I did not run the full cargo test --workspace locally: the sandbox ran out of disk building every CLI test binary. CI runs the full suite.

Per-issue checklist

Notes / not changed

  • Bun 1.3.0 writes no cNfGvRsN tag (fixture bun-lockb/1.3.0), so its binary lock still reads as older Bun. Its text bun.lock without configVersion already does the same today. This fails closed: a refusal, never a wrong pin.
  • /code-review high suggested a different approach: decide membership from the lock's own workspace list (workspace_paths()) instead of the Bun version. I didn't take it. The text-lock branch decides by configVersion too, and the lock's list is stale for a member added since the last install. Moving both branches would be a separate change to the Hosted scan from a yarn classic workspace member still pins nothing and exits 0 when the root's workspaces use a ! pattern (yarn 1 ignores it) or an extglob like packages/@(a|b) #1097 design. Every other finding (full-parse dependence, unknown-extension stop, unbounded read, 1.3.0 doc claim, stale CLI test comment, missing workspace-root fixture) is fixed in 7d05f17. A reviewer's finding on scoped overrides is fixed in 13f02d6 (fixture fitted to the VEX corpus in e509c25).

🤖 Generated with Claude Code


Note

Medium Risk
Changes workspace membership and refusal logic for hosted/vendored npm flows when only bun.lockb is present; mis-detection could refuse or allow runs from the wrong directory, but untagged or invalid locks still fall back to pre-1.3 behavior.

Overview
Fixes #1492: lone binary bun.lockb locks were always interpreted with pre–Bun 1.3 workspace rules (negated ! patterns ignored), so excluded workspace members were wrongly refused with redirect_workspace_lockfile_elsewhere / stray-lock handling when only a tagged bun.lockb existed at the root.

read_config_version in bun_lockb.rs reads Bun’s cNfGvRsN extension (binary twin of text configVersion) via a structural walk, including when sCoPdOvR follows the tag. lock_dialect uses that for standalone bun.lockb (FIFO-safe sync read on the blocking pool) to pick Bun 1.3+ vs pre-1.3 glob semantics, matching the existing bun.lock path.

Docs (CLI_CONTRACT.md), CLI test comments, a 1.4.2-scoped-overrides fixture, VEX golden updates, and unit/integration tests cover tagged vs untagged locks and negated workspaces.

Reviewed by Cursor Bugbot for commit e509c25. Configure here.


Generated by Claude Code

Assisted-by: Claude Code:claude-opus-5-5
Bun 1.3+ writes a cNfGvRsN extension into bun.lockb, the binary
twin of the text lock's configVersion. Expose it from the codec and
add regression tests for #1492: a tagged lone bun.lockb root must
honour "!" workspace patterns, so an excluded member's own lock is
the one patched. WIP: lock_dialect does not read the tag yet.

Assisted-by: Claude Code:claude-opus-5-5
A Bun 1.3+ project that keeps a binary bun.lockb (saveTextLockfile =
false) and excludes a member with "!packages/b" could no longer
patch that member in hosted or vendored mode: socket-patch read the
lone bun.lockb as older Bun, which ignores "!", and refused with
redirect_workspace_lockfile_elsewhere. Read the cNfGvRsN
config-version tag Bun 1.3+ writes into bun.lockb, as configVersion
is read from bun.lock. Fixes #1492.

Assisted-by: Claude Code:claude-opus-5-5
The Bun 1.3+ check parsed the whole bun.lockb, so a tagged lock the
codec rejects, or one with an extension it does not know before the
tag, still read as older Bun and kept refusing an excluded member.
Bun writes the cNfGvRsN extension last, right before the lock's
total length, so read the header and those 16 bytes only. This also
keeps the check cheap on large monorepo locks. Cover a workspace
root's lock, whose tag follows its workspace and catalog extensions,
and say in the docs that Bun 1.3.0 does not write the tag.

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 13, 2026 19:25
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

…ialect

# Conflicts:
#	crates/socket-patch-cli/CLI_CONTRACT.md
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread crates/socket-patch-core/src/vendor/bun_lockb.rs Outdated
Bun writes the sCoPdOvR scoped-overrides extension after cNfGvRsN, so
a lock with nested overrides has no tag at total - 16 and still read as
older Bun, refusing a member excluded with "!". Walk the structure
instead: the header, the six buffer descriptors and the extension tags,
seeking past every array, so package records are never read and the
tag is found wherever Bun puts it. Add a Bun 1.4.2 fixture with scoped
overrides and a "!" workspace pattern.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013g2AhipQxH9Q19uPZT6Aph
Every bun-lockb fixture is swept by the VEX discovery tests, which
rewire minimist@1.2.2 at the root and compare against the golden
corpus. Regenerate the scoped-overrides lock with the corpus's
minimist@1.2.2 / is-number@7.0.0 dependencies and an override nothing
installs (Bun still writes sCoPdOvR after cNfGvRsN), and add its
golden entries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013g2AhipQxH9Q19uPZT6Aph
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

bugbot run


Generated by Claude Code

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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 e509c25. Configure here.

@mikolalysenko Mikola Lysenko (mikolalysenko) added the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 13, 2026
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Ready for review (burn-down agent)


Generated by Claude Code

Merged via the queue into main with commit 394ad24 Oct 13, 2026
54 checks passed
@mikolalysenko
Mikola Lysenko (mikolalysenko) deleted the agent/fix-bun-lockb-dialect branch October 13, 2026 22:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bun 1.3+ bun.lockb root is read as pre-1.3, so a "!"-excluded member can't be patched in hosted or vendored mode (regression in 5.0.1)

3 participants