aube: sync vendored engine to upstream v1.32.0#519
Conversation
Merges jdx/aube v1.25.1..v1.32.0 (133 commits) into vendor/aube. The merge was done on nubjs/aube's nub-fork branch, which carries real upstream ancestry, after reconciling a broken merge base: vendor/aube on main was at v1.25.1 while nub-fork was still at v1.23.0, because an earlier sync went straight into vendor/aube and bypassed the venue. Recording the v1.25.1 release commit as a parent of the reconciliation commit cut the conflict surface from 72 files / 205 hunks to 23 files / 48 hunks. Nub's PM delta is preserved: GVS and phantom linking, force-materialization, concurrent OSV and trust-policy gating, the defaultTrust floor, the TTY progress line, embedder-profile plumbing, nub-identity config scoping, pnpm-11 namedRegistries, and patch-group range resolution. Upstream capabilities grafted on top include plain unified patch parsing, proxy passthrough to lifecycle scripts, stale patch-hash stripping, hash-only patch decoration, the berry patch-selector superset, and upstream's install cancellation points re-sited into nub's concurrent structure. Two pre-existing defects are fixed because this merge widens them: - decide_with_floor dropped source_key, so the build-approval warning seam disagreed with the runner about file:/git/tarball-backed packages. It now passes source and git-repository keys, matching the runner and upstream. - The pnpm writer required patched_dependencies to contain the source key, but nub's pnpm reader leaves that map empty by design, so every parse and re-emit dropped (patch_hash=...) suffixes and changed dep-path identity. nub-cli now names the features upstream made optional in #1068 and #1069: aube gains rustls and publish; aube-registry gains hickory-dns, which restores the unconditional in-process DNS cache this crate had before v1.32. hickory-dns is deliberately left off the aube crate, since reqwest's feature flips the default resolver for every client in the binary.
The merge base for the next bump was previously inferable only from the version string in vendor/aube/Cargo.toml, which is why the v1.32 bump started against a two-release-stale base. Recording the commit makes it a fact the next bump reads instead of reverse-engineers.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — a plain-in-tree vendor/aube sync from v1.25.1 to v1.32.0 (133 upstream commits). The bulk is upstream jdx/aube code, docs, CI, and benchmarks; the review focused on the nub-side integration: feature-gating, conflict resolutions, and the two flagged fixes.
- Re-base the vendored tree onto the correct upstream commit —
vendor/aube/UPSTREAMnow records the merge base (9f2f5f8255/ v1.32.0), cutting the conflict surface from 72 files/205 hunks against the stale base to 23/48 against the correct one. - Feature-gate the engine deps in
crates/nub-cli/Cargo.toml—aubemoves todefault-features = false, features = ["rustls", "publish"](both now required to compile after jdx/aube#1068/#1069), andaube-registrynameshickory-dnsto preserve its pre-v1.32 unconditional behavior.hickory-dnsis left off theaubecrate deliberately. - Unify the build-approval decision seam (
decide_with_floor) — all five allow-decision sites (run_dep_lifecycle_scripts,should_jail/build_jail,decide_with_floor, theignored_buildsscan, and the graph-hash allow closure) now route throughdecide_package_with_git_repositorywith both the source and git-repository approval keys via the newpackage_build_is_allowedhelper, closing a gap where a bare-nameallowBuildsapproval vouched forfile:/git/tarball-backed bytes the runner refuses. - Fix the pnpm
(patch_hash=…)writer — drops thepatched_dependencies.contains_keyguard (nub's reader leaves that map empty for hash entries), andwith_patch_hashnow strips-then-restamps so a re-emit no longer changes dep-path identity. - Absorb upstream-driven signature changes —
ParseOptions(default-preserving strict integrity), theTrustExcludeRuleexact-version→range generalization under nub'spub(crate)methods, the tokio task-local dependency-chain index withscope_currentre-siting into nub's concurrent lifecycle spawn, and the newInstallControl/Mode::Eventsplumbing (nub defaults toHuman).
The integration is coherent and exceptionally well-documented — every conflict resolution and compiler-forced change is grounded in an inline rationale, and the flagged fixes each carry a test. I verified the five decision seams are consistent, the aube-util cfg partition is exhaustive and mutually exclusive across the rustls/Android/other-target matrix, and the default parse/output paths preserve prior behavior. Two items surfaced only for awareness, neither blocking.
ℹ️ Security-posture change rides in a sync PR
The decide_with_floor fix is a real behavior change to the build-approval path, not a mechanical merge: before this PR the unreviewed-builds warning could disagree with the runner about file:/git/tarball-backed packages, so a bare-name allowBuilds approval effectively vouched for bytes the runner would refuse. The fix is correct and the author already called it out, but it is worth an explicit maintainer read since it changes what a given allowBuilds entry authorizes — landing inside a 219-file sync makes it easy to skim past.
Technical details
# Security-posture change: build-approval seam now honors source / git-repository keys
## Affected sites
- `vendor/aube/crates/aube/src/commands/install/default_trust.rs:256-264` — `decide_with_floor` now calls `decide_package_with_git_repository(name, ver, source_key, git_repository_key)` instead of the old `policy.decide(name, ver)`.
- `vendor/aube/crates/aube/src/commands/install/lifecycle.rs:467-476` (runner), `:174-180` (`should_jail`), `:189-194` (`build_jail`) — same seam.
- `vendor/aube/crates/aube/src/commands/install/mod.rs:215-230` — new `package_build_is_allowed` helper; also consumed by `ignored_builds.rs` and `install/link.rs:348`.
- `vendor/aube/crates/aube-lockfile/src/lib.rs:620-634` — new `git_repository_approval_key` on `LockedPackage`.
## Required outcome
- Confirm the tightened authorization is intended: a bare-name approval no longer authorizes builds for a `file:`/git/tarball-backed instance of that name; an explicit source or `git+<repo>` key is required. This is the desired behavior per the PR body — the note exists so the change gets an explicit sign-off rather than being merged implicitly as part of the sync.
## Open questions for the human
- None blocking. Just confirm the posture change is acceptable to ship in the same PR as the version bump rather than as its own change.ℹ️ Aliased packages still don't inherit patches by real name
The author disclosed that patchedDependencies: is-odd@3.0.1 does not patch odd-alias: npm:is-odd@3.0.1, whereas real pnpm does. This is pre-existing and unchanged by this PR, but the pnpm writer touched here is adjacent code — worth a follow-up issue so the gap is tracked rather than rediscovered next time this file is edited.
Technical details
# Aliased packages do not inherit real-name patch declarations
## Affected sites
- `vendor/aube/crates/aube-lockfile/src/pnpm/write.rs` — the patch-selector resolution keys off the alias name, never the shadowed registry name (the inline comment at the `patch_groups.resolve` call documents this: "an ALIAS resolves under its alias name, never the registry name it shadows").
## Required outcome
- Track as a follow-up; not in scope for this sync. Divergence from real pnpm, which applies the patch to the aliased instance.
## Open questions for the human
- Is matching pnpm here desired, or is nub's alias-scoped patching an intentional divergence? Answer determines whether the follow-up is a bug fix or a documented behavior note.Claude Opus | 𝕏
`patchedDependencies: { "is-odd@3.0.1": ... }` did not patch
`odd-alias: npm:is-odd@3.0.1`. Real pnpm does, because it resolves the patch
against the resolved manifest's name, where the alias does not exist as a
distinct node. aube v1.32.0 has the same gap and additionally records
`(patch_hash=...)` on the alias's dep path, so its lockfile claims a patched
identity the install never produces.
The rule lives in one place: PatchGroups::resolve_package wraps resolve() with
the alias fallback, and both resolve_patched_by_version and the pnpm writer
call it. Output stays keyed by spec_key(), so the linker's Patches map, the
graph hash, and the delta fingerprint inherit it. The no-churn write guard in
io.rs carried a second copy of the matching logic and now routes through the
same entry point.
Precedence is declared-name-first: a key naming the alias wins over one naming
the registry package. pnpm can never match an alias key, so such a key is
nub-only with no pnpm behavior to preserve, and it is the more specific of the
two.
Also fixes a pre-existing reader gap this exposed. pnpm/read.rs baked the
write-time (patch_hash=...) marker into parsed dep paths, so a parsed patched
graph was keyed differently from the fresh graph it is compared against: the
identity hash could never match and the no-churn guard could never fire. The
reader now strips the marker and the writer re-derives it, one owner per
direction. Writer output is unchanged.
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — the one new commit since the prior review (9036306, "pm: apply patches declared against a package's real name to npm aliases"), which closes the aliased-patch gap the first review disclosed as pre-existing.
- Inherit registry-name patches on npm aliases — new
PatchGroups::resolve_package(pkg)tries the resolvedname@versionfirst, then falls back to the aliasedalias_of@version, sopatchedDependencies: is-odd@3.0.1now patchesodd-alias: npm:is-odd@3.0.1(matching pnpm, which has no separate alias node). Group-level precedence with a documented rationale for the alias-name-key-wins tie-break. - Route every consumer through one rule —
resolve_patched_by_versionnow callsresolve_package, so the linker (aube/src/patches.rs), graph-hash fold, drift check, pnpm writer, and the no-op write guard all key patches off the same resolution.io.rs'slockfile_write_is_noopwas rewritten from a hand-rolledPatchGroups::resolvetoresolve_patched_by_version, keyed byspec_key(). - Strip write-time
(patch_hash=…)markers on parse —strip_patch_hash_suffix+PATCH_HASH_MARKERmoved intodep_path.rsas sharedpub(super)items;read.rserases the markers before graph construction so an alias key never reparses asodd-alias@3.0.1(patch_hash=…). - Tests — unit coverage for alias inheritance, the alias-name-key-beats-registry-name-key precedence, and conflict propagation, plus a full pnpm read→write→reparse round-trip asserting the aliased node serializes patched with no undecorated duplicate snapshot.
The change is coherent and correct. I verified the io.rs closure contract holds — resolve_patched_by_version's output is keyed by spec_key() (format!("{name}@{version}")), and graph_identity_hash_with_patches invokes the patch closure with a package's plain name/version (never a dep_path or peer-suffixed string), so the lookup cannot diverge. All five patch consumers now share resolve_package, so the writer's decorated snapshot and the linker's materialized folder agree. The one behavioral shift in io.rs — a per-package conflict now returns false (proceed to the real write) instead of the old None-swallow — is effect-preserving, since both paths defer the branded error to the real write. The prior review's disclosed aliased-patch gap is now resolved; the security-posture note on decide_with_floor still stands for a maintainer read.
Claude Opus | 𝕏
…as key Reverses the precedence added in the previous commit. Patch keys now resolve solely against a package's registry name, matching pnpm, which calls getPatchInfo on the resolved manifest and so can never match a key naming an npm alias. Honoring such a key would have invented nub-only config semantics in a field the pnpm-compat surface is meant to mirror. The alias inheritance itself is unchanged and remains the point of the fix: a patch declared as is-odd@3.0.1 still applies to odd-alias: npm:is-odd@3.0.1. A key that names the alias now matches nothing, which would be silent dead config, so it is reported: WARN_AUBE_PATCH_KEY_ALIAS_NAME names the key and suggests the registry-name spelling, swapping only the name prefix so the user's version selector survives as a drop-in replacement. It fires from record_patches_on_graph beside the existing patch-key conflict gate. Note this is narrower than pnpm, which fails the install outright on any unused patch key (ERR_PNPM_UNUSED_PATCH, allowUnusedPatches defaults to false). Nub silently ignores every dead patch key today, alias-shaped or not; this warning covers only the slice this change could newly create. Full verifyPatches parity is a separate, install-failing change.
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — the one net commit since the prior review (b73a35f6, "pm: resolve patch keys against the registry name only, warn on an alias key"). The merge commit 52134d5 only pulls already-merged origin/main work into the branch and carries no new PR contribution.
- Reverse the alias-name patch precedence to pnpm-faithful resolution —
PatchGroups::resolve_package(pkg)now resolves solely againstregistry_name()(alias_ofwhen set, elsename), dropping the earlier "alias-name key wins its group" rule. This matches pnpm, which callsgetPatchInfoon the resolved manifest and can never match a key naming an npm alias, so honoring one would have invented nub-only semantics in a pnpm-mirrored field. - Keep the inheritance that is the point of the fix — a patch declared as
is-odd@3.0.1still applies toodd-alias: npm:is-odd@3.0.1; the retained test asserts the aliased node inherits the registry-name patch keyed by itsspec_key(). - Report dead alias-name keys instead of silently dropping them — new
alias_only_patch_keysfinds keys that match nothing under the registry-name rule but would have matched the alias's installed name, andwarn_alias_only_patch_keys(called once fromrecord_patches_on_graph) emitsWARN_AUBE_PATCH_KEY_ALIAS_NAMEwith a drop-in registry-name suggestion. - Register the new warning code —
WARN_AUBE_PATCH_KEY_ALIAS_NAMEadded to theaube-codeswarnings const +ALLarray anderror-codes.data.json.
The reversal is coherent and correct. I verified resolve_patched_by_version still keys its output by spec_key() (alias name@version), so the linker, graph-hash fold, drift check, and no-op write guard all agree with the writer's direct resolve_package(pkg) call — and the writer's inline comment was corrected to match. The suggested slice &source_key[alias.len()..] is UTF-8-safe because a group's source key always begins with its classified name, and the dedup on source_key keeps the warning firing once per dead key. The commit message honestly scopes this warning as narrower than pnpm's install-failing ERR_PNPM_UNUSED_PATCH, covering only the alias-shaped slice this change could newly create. The prior review's disclosed aliased-patch gap is resolved; the decide_with_floor security-posture note still stands for a maintainer read.
Claude Opus | 𝕏
…ile name aube v1.32 added a bats test asserting sha1 SRI derived from dist.shasum lands in aube-lock.yaml. Under nub a fresh project writes pnpm-lock.yaml, so the grep fails on a missing file. Same lockfile-name divergence already listed for lockfileIncludeTarballUrl. The derivation itself is present in the merged resolver and covered by aube's unit tests, so only the aube-branded filename assertion is skipped.
A patchedDependencies key that matches no installed package was silently ignored. pnpm fails the install (ERR_PNPM_UNUSED_PATCH, no node_modules, no lockfile written) unless allowUnusedPatches is set, and does so even when the dead key sits beside keys that do apply. Nub now matches, with the message text byte-identical to pnpm's. The gate lives in record_patches_on_graph beside the existing patch-key conflict gate, which is also where pnpm runs it: verifyPatches is inside the resolution step, so a warm install whose resolution is skipped does not re-check. Verified against pnpm 10.15.1 rather than inferred. allowUnusedPatches downgrades it to a warning. The setting surface was probed across all seven candidate spellings against real pnpm 10.15.1: it is a manifest and workspace-YAML setting, and .npmrc is not a home for it at all. Nub therefore reads it from exactly the homes patchedDependencies uses, which means the existing pnpm_aube_objects gating gives the brand boundary for free: a pnpm-incumbent project reads pnpm.allowUnusedPatches, while a nub-identity project reads the neutral top-level allowUnusedPatches and ignores the branded one, the same shape allowBuilds already has. pnpm 10's deprecated allowNonAppliedPatches spelling is accepted, current name winning. Replaces WARN_AUBE_PATCH_KEY_ALIAS_NAME from the previous commit: the alias hint now rides inside this error, so an alias-shaped dead key still gets the drop-in registry-name spelling instead of a generic message. Note this fails installs that previously succeeded: a typo, a removed dependency, or a version bump that outran a patch selector now fails on the next resolving install.
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — the one net commit since the prior review (1796dee, "pm: fail the install on an unused patch key, matching pnpm"), which upgrades the alias-name warning from b73a35f6 to the full pnpm verifyPatches gate the prior review explicitly scoped out as a separate change.
- Generalize dead-key detection —
alias_only_patch_keysbecomesunused_patch_keys, returningVec<UnusedPatchKey>(asource_keyplus an optionalregistry_name_spellingfor the alias sub-case). It now reports every declaredpatchedDependencieskey that matches no installed package, in declaration order, not just the alias-shaped slice. - Fail the install, gated by
allowUnusedPatches—warn_alias_only_patch_keysbecomesverify_patches_used, called fromrecord_patches_on_graphat the resolve gate; an unused key raisesERR_AUBE_UNUSED_PATCHunlessallowUnusedPatchesdowngrades it toWARN_AUBE_UNUSED_PATCH. The message is byte-identical to pnpm'sERR_PNPM_UNUSED_PATCH. - Read the setting from the same brand-scoped homes as
patchedDependencies— newPackageJson::allow_unused_patchesandWorkspaceConfig.allow_unused_patches(workspace yaml wins), each inheriting existing brand gating so a nub-identity project reads only the neutral top-level key. pnpm 10's deprecatedallowNonAppliedPatchesspelling is accepted. - Swap the error codes —
WARN_AUBE_PATCH_KEY_ALIAS_NAMEremoved,ERR_AUBE_UNUSED_PATCH+WARN_AUBE_UNUSED_PATCHadded acrossaube-codesanderror-codes.data.json. Unit + integration tests cover the fail/downgrade split and the deprecated spelling.
The change is coherent and correct. I verified verify_patches_used runs after resolver.resolve at both install call sites (resolve.rs:315, mod.rs:2230), so a warm/frozen install that short-circuits before resolution skips it, matching pnpm and the doc-comment claim. The matched set is built via resolve_package (registry-name rule) and compared against the verbatim declared source keys, so key identity is exact; the alias-replacement slice &source_key[pkg.name.len()..] is UTF-8-safe because a group's source key always begins with its classified name. The removed warning code was introduced earlier in this same PR (b73a35f6) and never shipped, so dropping it is not a stability break, and a grep confirms no dangling references. The prior review's decide_with_floor security-posture note still stands for a maintainer read.
ℹ️ New install-failing gate changes what a previously-passing project does
This is the user-visible blast radius the author flagged: a patchedDependencies key that matches no installed package — a typo, a removed dependency, or a version bump that outran a patch selector — now fails the next resolving install (exit 1, no node_modules, no lockfile) where nub previously ignored it silently. That is the intended pnpm parity, but it means a project that installs cleanly today can start failing after this lands, so it warrants an explicit maintainer sign-off rather than being absorbed implicitly as part of the sync.
Technical details
# Unused-patch-key gate can fail installs that previously succeeded
## Affected sites
- `vendor/aube/crates/aube/src/patches.rs:385,391-449` — `verify_patches_used` raises `ERR_AUBE_UNUSED_PATCH` at the resolve gate for any declared key matching no installed package.
- `vendor/aube/crates/aube-lockfile/src/patch_groups.rs:285-313` — `unused_patch_keys` now reports every dead key, not only alias-shaped ones.
## Required outcome
- Confirm the parity is intended to ship in the sync PR: installs with a stale/typo'd `patchedDependencies` key that pass on `main` today will fail after this lands, unless the user sets `allowUnusedPatches`.
## Open questions for the human
- None blocking. Just confirm the install-failing behavior (vs the prior silent-ignore) is acceptable to land as part of the version bump. `allowUnusedPatches` (and the deprecated `allowNonAppliedPatches`) is the documented escape hatch.Claude Opus | 𝕏
…nder nub
aube v1.32 added a test asserting the overlapped and inline lockfile writes
produce byte-identical output. It cannot run under nub for two independent
reasons: it asserts aube-lock.yaml, and the killswitch it toggles is
unreachable here — nub's profile sets env_prefix = None, so
embedder_env("DISABLE_LOCKFILE_WRITE_OVERLAP") is always None and the overlap
is always on, leaving no inline arm to compare against.
The live overlapped path is verified instead: a cold install writes a
pnpm-lock.yaml byte-identical to real pnpm 10.15.1's, with zero time: blocks,
which is what nub's time-stripping delta feeds into that write path to
produce.

Syncs the vendored engine from aube v1.25.1 to v1.32.0 — 133 upstream commits.
Merge base
vendor/aubewas at v1.25.1 while thenub-forkmerge venue was at v1.23.0: an earlier sync went straight intovendor/aubeand bypassed the venue, so git had been merging against a two-release-stale base.Re-parenting the reconciliation snapshot onto upstream's v1.25.1 release commit:
Cargo.lock, the changelogs, and generated docs auto-merge at the correct base.vendor/aube/UPSTREAMnow records the base commit so the next bump reads it instead of inferring it from a version string.Resolution
Preserved: GVS/phantom linking, force-materialization, concurrent OSV and trust-policy gating, the
defaultTrustfloor, embedder-profile plumbing, nub-identity config scoping, pnpm-11namedRegistries, patch-group range resolution.Grafted from upstream: plain unified patch parsing (jdx/aube#1021),
file:/portal:stale-materialization removal, proxy passthrough to lifecycle scripts (ordered so nub's env overlay still applies last),rustls/hickory-dnsfeature gating with nub's Android carve-out, stale(patch_hash=…)stripping, hash-only patch decoration (jdx/aube#1035), the berry patch-selector superset, and upstream's install cancellation points re-sited into nub's concurrent structure.Seven further defects were found by the compiler, not by conflict markers — a new required
parse_oneparameter, upstream's newaccess.rswritten against a signature nub had changed, aTrustExcludeRulefield rename under a nub-added method, a new progressMode::Eventsvariant, and two new required struct fields.Two pre-existing bugs fixed
Both predate this merge; both are fixed here because the merge widens them.
decide_with_floordroppedsource_key, so the build-approval warning seam disagreed with the runner aboutfile:/git/tarball-backed packages — a bare-nameallowBuildsapproval vouched for bytes the runner refuses. Upstream v1.32 moves all three seams todecide_package_with_git_repository, which would widen the gap to two missing keys.patched_dependenciesto contain the source key, but nub's pnpm reader leaves that map empty by design, so every parse and re-emit dropped(patch_hash=…)suffixes and changed dep-path identity.nub-side
jdx/aube#1068 and #1069 feature-gated the TLS backend and the publish stack, so
crates/nub-cli/Cargo.tomlnow names what it needs:aube→rustls,publish.publishgatescommands::publish, whichpm_engine/publish_family.rscalls directly;rustlsis the crate's only TLS backend. Without them nub-cli does not compile.aube-registry→hickory-dns. Default-preserving: this crate called.hickory_dns(cfg!(not(target_os = "android")))unconditionally before v1.32.hickory-dnsis deliberately left off theaubecrate — reqwest's feature flips the default resolver for every client in the binary.Verification
cargo check --workspace --all-targets,clippy --all-targets --all-features -- -D warnings, 2,590 tests — all green.check -p nub-cli --all-targets,clippy --all-targets --all-features -- -D warnings,fmt --check, and 443 tests across the 15 sync-relevant binaries — all green. The full suite was not run in one pass (pm_two_modealone takes 41 minutes); CI covers it.aube-util's new cfg partition checked under no-default-features,rustls, and the Windows target. The Android branch is exhaustive and mutually exclusive by construction.cargo fmt --checkinsidevendor/aubeare an identical set before and after this merge. nub's root workspace excludesvendor/aube, so they do not gate nub CI.Patch handling brought to pnpm parity
Two further pnpm-compat gaps, both confirmed against real pnpm 10.15.1 rather than inferred.
Patches now apply to npm-aliased installs.
patchedDependencies: { "is-odd@3.0.1": … }did not patchodd-alias: npm:is-odd@3.0.1; pnpm does, because it resolves patches against the resolved manifest's name, where the alias is not a distinct node. The rule lives in one place —PatchGroups::resolve_package, called by both the resolver and the pnpm writer, keyed byspec_key()so the linker'sPatchesmap, the graph hash, and the delta fingerprint all inherit it. The no-churn write guard carried a second copy of the matching logic and now routes through the same entry point.Resolution is by registry name only. A key naming the alias matches nothing, because pnpm can never match one either.
aube v1.32.0 has the same gap, and additionally records
(patch_hash=…)on the alias's dep path — so its lockfile claims a patched identity the install does not produce. Reported upstream as jdx/aube discussion #1082.Fixing this exposed a second, pre-existing reader bug:
pnpm/read.rsbaked the write-time(patch_hash=…)marker into parsed dep paths, so a parsed patched graph could never hash-match a fresh resolve and the no-churn guard could never fire. The reader now strips the marker and the writer re-derives it — one owner per direction, writer output unchanged.An unused patch key now fails the install. A
patchedDependencieskey matching no installed package was silently ignored; pnpm exits 1 with nonode_modulesand no lockfile, even when the dead key sits beside keys that do apply. Nub now matches, with the message text byte-identical to pnpm's, and the gate in the same place pnpm runs it (inside the resolution step, so a warm install whose resolution is skipped does not re-check).allowUnusedPatchesdowngrades it to a warning. All seven candidate spellings were probed against pnpm 10.15.1: this is a manifest and workspace-YAML setting and.npmrcis not a home for it, so nub reads it from exactly the homespatchedDependenciesuses. The existing config gating then gives the brand boundary for free — a pnpm-incumbent project readspnpm.allowUnusedPatches, a nub-identity project reads the neutral top-level key and ignores the branded one, the same shapeallowBuildsalready has. pnpm 10's deprecatedallowNonAppliedPatchesspelling is accepted.This fails installs that previously succeeded. A typo, a removed dependency, or a version bump that outran a patch selector now fails on the next resolving install. That is the intended parity, but it is the user-visible blast radius of this PR.
Flagged for review
decide_with_floorfix is a security-posture change riding in a sync PR.registry dist.shasum with no dist.integrity is derived) is skipped: it grepsaube-lock.yaml, which nub does not write. The derivation it covers is present in the merged resolver and unit-covered.vendor/aube/**(pm: tri-state interactive update picker #512, build: 7-day soak across toolchain and deps, cold_path() hints, pinned tooling #442, fix(lockfile): tolerate non-string pnpm engines values #419) overlap the upstream delta and will conflict; whichever merges second resolves.