Skip to content

aube: sync upstream v2.2.9 and move nub-cli to usage-rs - #879

Merged
colinhacks merged 14 commits into
mainfrom
aube-2
Sep 5, 2026
Merged

aube: sync upstream v2.2.9 and move nub-cli to usage-rs#879
colinhacks merged 14 commits into
mainfrom
aube-2

Conversation

@colinhacks

@colinhacks colinhacks commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Vendors aube v2.2.9 (upstream aubepkg/aube, 237 commits since v1.35.0) and moves nub-cli from clap to usage-rs, the parser upstream switched to in v2.0.1, so the binary links one parser and the engine verbs compose aube's usage_rs::Args types directly.

Vendored engine. vendor/aube/UPSTREAM now records 79906840d9 / v2.2.9. The nub delta (workspace markers, nub.lock naming, .store virtual-store subdir, branded-config gating, EngineContext, env overlay, cold-path settings) is intact. Upstream landed its own copy of EngineContext with a bundled_package_extensions field, kept as ours plus that field. The project registry converges on upstream's JSON registry under the global virtual store: every project registers there on every linker mode, and nub's store prune sweep of the extracted-tree tier now reads that registry instead of the removed store-side one. Upstream's new CAS prune planner stats only unreferenced files (it stat'ed every store file; 98s → on par with the previous 17s on a 900k-file store). Upstream's new PATH composer for dlx, exec and run children keeps the embedder-provisioned node dir as its fallback, which is what lets a fetched bin's #!/usr/bin/env node re-enter nub; without it dlx-fetched bins ran unaugmented.

Kept on nub's side of the merge, for review: hoisted version-preference policy (upstream #1296 not adopted); global install layout (~/.local/bin, <data>/global); the peer-context bound and the latest* widening are refused; EmbedderInstallOverrides.cache_dir outranks NUB_CACHE_DIR; default build trust stays on the defaultTrust floor rather than upstream #1199's seeded allowlist (a built-in Allow answered ahead of the floor and skipped its provenance, advisory and cooling gates and the disclosure line), with pnpm's list folded into the floor's candidates instead, which at this snapshot adds exactly one name to Bun's, @parcel/watcher; patch drift on a pnpm lockfile is the hash-against-hash check (upstream's new path-map check saw every recorded pnpm patch as missing, since nub's reader keeps the content hash and no path), and a hashless { path } entry is judged on its path.

Adopted from upstream: the lockfile as the trust boundary (#1418, #1403): trustPolicy=no-downgrade runs when a version is newly resolved, and an install that reuses an undrifted lockfile trusts the recorded pin without re-fetching its publishing evidence. Nub's re-validation pass, its on-disk cache and the warm_trust_revalidate embedder gate are removed (about 500 lines); the docs paragraph and the two nub-side install tests follow. Also adopted: side-effects marker v2 and the /v1 global-virtual-store layout (warm state re-extracts once), the age-gate blocked-update warning in outdated and update -i, update --ignore-scripts, store prune --dry-run, and the built-in disableGlobalVirtualStoreForPackages list, which now keeps the global virtual store on for nuxt and parcel (upstream #1312) and turns it off for expo, react-native and metro (upstream #1297).

store prune after the /v1 layout. Entries earlier versions wrote at the store root, and the project records beside them, are neither read nor pruned by upstream. Nub's prune now derives that root from the versioned path, keeps an entry a still-existing project links into, holds an unreferenced one for the same 30-day window as the extracted-tree cache (against the root's own .gc-state), and removes the old bookkeeping once every entry is gone. Existing projects keep linking into the old entries until their next install. Behavior change to note on the versioned store itself: with upstream's registry, an entry no registered project links to is removed on the first prune; the 30-day hold now applies to the extracted-tree cache and the previous layout only. The virtual-store docs page is rewritten to match, with its sample output re-captured.

CLI. Every clap surface in nub-cli is on usage-rs 6: the root tree, the shared flag groups, and the engine-verb bridge, which now stamps one static usage_rs::Cli root per verb. User-visible help changes are usage's vocabulary (Flags: for Options:, [FLAGS], , <SUBCOMMAND>), -V advertised beside -v, and clap's tip: line on an unknown-verb error is gone. Usage errors still exit 2. Engine doc hosts (aube.sh, github.com/aubepkg/aube) join the brand rewrite.

Gates: remote clippy + brand lints, cargo test with the compile leg, aube's own cargo test --workspace, the nub-adapted bats suites, the lockfile conformance matrix, and a differential sweep of the CLI surface and install/workspace sequences against the merge-base build. Ad-hoc on a builder: @parcel/watcher builds through the floor with the disclosure line; a fabricated pre-/v1 layout keeps the linked entry, removes the expired orphan and leaves v1 intact, then holds the rest for 30 days; an exact pin that lost publishing trust is still refused at resolve time (ERR_NUB_TRUST_DOWNGRADE), and a frozen install of an undrifted lockfile succeeds without it. Thirteen bats tests new in this range hit documented nub divergences (the nub.lock name, .store, rebranded codes, the branded-config boundary, the hidden GVS toggle flags, nub's install summary) and are on the skip lists with their reasons; four upstream engine tests are adapted to nub policy (a seeded index needs an integrity key; a per-project install still registers the project; a default hoist vetoes the shared store).

Open PRs touching vendor/aube will need a rebase: #758 (likely superseded by upstream #1326/#1369), #727, #726, #718, #583, #577.

Copilot AI lite review requested due to automatic review settings September 4, 2026 20:01
@vercel

vercel Bot commented Sep 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
nub Ready Ready Preview Sep 5, 2026 9:32pm UTC

Request Review

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Important

Frozen installs currently reject valid pnpm lockfiles whenever the project uses patchedDependencies.

Reviewed changes Reviewed the full aube v2.2.9 vendor sync and the nub-cli parser migration at 34355f9.

  • Vendored engine Updated the embedded aube workspace to v2.2.9 while retaining Nub-specific identity, config, lockfile, layout, resolver, and cache policies.
  • CLI parser Replaced clap with usage-rs across native commands, forwarding argv handling, shared flag groups, help/error rendering, and engine-verb roots.
  • Command families Rebuilt the subcommand-bearing publish, store, cache, and config wrappers around upstream argument types and preserved Nub-owned aliases and config routes.
  • Verification surface Reviewed the parser/unit coverage, command-family integration tests, vendored tests, and the differential lockfile round-trip result.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using GPT Sol𝕏

Comment thread vendor/aube/crates/aube/src/commands/install/resolve.rs Outdated

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Important

Hashless/path-only pnpm patch entries are now falsely treated as stale.

Reviewed changes Re-reviewed the four follow-up commits after 34355f9, covering the corrected patch-drift path and the trust, diagnostics, and Windows harness adjustments.

  • Patch drift Replaced the pnpm-only helper with the graph's shared hash-aware drift check across frozen, preferred, lockfile-only, and remove paths.
  • Build trust Removed the seeded upstream lifecycle allowlist so Nub's gated defaultTrust floor remains authoritative.
  • Deprecation hints Preserved the branded deprecations command hint for embedded engines.
  • Compile harness Gave each augmentation fixture a distinct platform-suffixed artifact name to avoid Windows sharing violations.

AI-assisted — Tool: Pullfrog; model: openai/gpt-sol; version: 0.1.68.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using GPT Sol𝕏

Comment thread vendor/aube/crates/aube/src/commands/install/resolve.rs

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes Re-reviewed the path-only pnpm patch-drift fix added after the prior Pullfrog review at 557659a.

  • Patch drift fallback Added selector-level path comparison for accepted hashless pnpm patch entries while preserving hash comparison for normal pnpm and Aube entries across all install modes and remove.
  • Regression coverage Added matching, moved, and removed path-only cases that fail under the previous unconditional hash-only branch.

AI-assisted — Tool: Pullfrog; model: openai/gpt-sol; version: 0.1.68.

Pullfrog  | View workflow run | Using GPT Sol𝕏

Vendors aube at 79906840d9 (v2.2.9, upstream aubepkg/aube), 237 commits past
v1.35.0, and migrates nub-cli from clap to usage-rs, the parser upstream moved
to in v2.0.1, so the binary links one parser and the engine-verb bridge composes
aube's own argument types.

Vendored engine: EngineContext kept as nub's with upstream's
bundled_package_extensions field grafted; every project registers in
upstream's global-virtual-store JSON registry and nub's extracted-tree sweep in
`store prune` reads that registry; the CAS prune planner consults the index
before stat'ing a file; the dlx/exec/run PATH composer keeps the
embedder-provisioned node dir as its fallback; upstream help docs naming aube
paths rewritten structurally. Kept nub's hoisted version-preference policy,
global install layout, lockfile trust-policy re-validation, peer-context bound
and `latest` range handling. Adopted upstream's side-effects marker v2, the
`/v1` global-virtual-store layout, the age-gate blocked-update warning, `update
--ignore-scripts`, `store prune --dry-run`, and the built-in
disableGlobalVirtualStoreForPackages list.

CLI: root tree, shared flag groups and the engine-verb bridge on usage-rs 6,
one static root per verb; help vocabulary follows usage (`Flags:`, `[FLAGS]`),
usage errors still exit 2; the brand rewrite strips aube.sh and
github.com/aubepkg/aube.

Tests: four upstream engine tests adapted to nub policy (integrity-keyed warm
index, per-project installs still register, default hoist vetoes the shared
store); thirteen new upstream bats tests that hit documented nub divergences
added to the skip lists with reasons.
… one

Upstream v2 added `check_patch_drift`, which compares the manifest's
patchedDependencies against the lockfile graph's path map. nub's pnpm
reader stores each recorded patch as its content hash and leaves the
path map empty, so the new check reported every pnpm-recorded patch as
missing and a frozen install of a pnpm-written lockfile with patches
failed with ERR_LOCKFILE_CONFIG_MISMATCH (the drop-in round-trip leg,
patched-deps direction A). The install modes already ran nub's
hash-aware `check_patched_dependencies_drift` right after it; remove the
duplicate at all three call sites and make `check_patch_drift`, still
used by `remove`, delegate to the hash-aware check.
…d allowlist

Upstream #1199 seeds the lifecycle build policy with a bundled snapshot
of pnpm's trusted-dependencies list, so a listed package decides `Allow`
before the `defaultTrust` floor is consulted. Under nub that skipped the
floor's gates (registry provenance, advisory vetting, the cooling
window) and its disclosure line: the native-deps and brand-sweep
harnesses saw esbuild and better-sqlite3 build with no
`WARN defaultTrust` line. The policy now holds only what the project
declared, as before the sync; default trust stays with the floor. The
upstream unit test that pinned the seed now asserts `Unspecified` for a
listed package with nothing declared.
Upstream v2 routed the deprecation summary lines through the install
control and hid the "Run `aube deprecations`" tail unless the embedder
is standalone aube. nub exposes the same verb, and the brand sweep
checks the hint is present and rebranded. Brand the command at the emit
site with `aube_util::cmd` and print the hint unconditionally, as
before the sync.
The harness reused `bin.exe` for every fixture. On the win32-arm64 leg
the rename that publishes one fixture's build failed with a sharing
violation on that name after the previous fixture's artifact had been
run and deleted, so a lingering handle kept the name delete-pending.
A per-fixture name never replaces a file another process may still
hold.
The pnpm branch of the patched-dependency drift check compared hashes
only. The reader keeps a legacy `{ path }` entry, which carries no
hash, in the path map alone, so such an entry read as missing from the
lockfile: frozen installs refused it and preferred installs re-resolved.
A selector with no recorded hash now compares its declared and recorded
paths; hash-bearing entries are unchanged.
Upstream 2.x bundles pnpm's trusted list and allowed those builds ahead
of any gate; this branch removed that seed. The names now feed the
floor instead, where every allow still passes the provenance, advisory
and cooling-window gates and prints the disclosure line. At the
vendored snapshot pnpm's list is Bun's list plus one name,
`@parcel/watcher`, so the merge is that entry, marked in the data file
with both provenances and a re-diff instruction for the next bump.
…w picks

Adopts upstream #1418 (and the #1403 shape it builds on): the lockfile
is the trust boundary. `trustPolicy=no-downgrade` is enforced when a
version is newly resolved; a version already in the active lockfile is
trusted without re-fetching its evidence. Nub's per-install
re-validation block, its cache and tests go; `paranoid` still forces
the full pipeline. The `warm_trust_revalidate` embedder field only
suppressed the old gate and is removed with it, so the warm-path gate
matches upstream. The trust-downgrade docs paragraph states the new
boundary.
The `/v1` namespace left entries written by earlier versions at the
store root, read by nothing and pruned by nothing. The prune planner now
derives that root from the versioned path, marks reachability from the
old project records (a project holding a `v1` record has re-linked and
says nothing about the old root), holds an unreferenced entry for the
same 30-day window as the extracted-tree tier against the root's own
`.gc-state`, and removes the old bookkeeping only once every entry is
gone. A dry run previews it; the JSON plan carries it as its own
section, schema unchanged.
… the tree cannot run

With upstream's project registry, an entry of the versioned store that no
registered project links to is removed on the first prune; the 30-day hold
applies to the extracted-tree cache and the previous layout. The docs page
still described the hold on the store itself, with sample output from the
previous prune summary. Rewritten against the current behavior, samples
re-captured from real runs.

`store.bats` is not among the suites the nub-adapted harness runs, and its
shim does not translate `AUBE_GLOBAL_VIRTUAL_STORE_DIR`, so the bats case
added for the previous-layout sweep could never execute here. The unit
tests beside the planner cover it; the case is removed.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes Re-reviewed the four commits added after a1b66c6, covering the trust-policy and store-pruning follow-ups.

  • Default trust Folded pnpm's additional @parcel/watcher entry into the gated defaultTrust floor while preserving the provenance, advisory, and cooling-window checks.
  • Lockfile trust boundary Removed lockfile-wide publishing-evidence revalidation so trustPolicy=no-downgrade applies to newly resolved versions and reused lockfile pins remain trusted.
  • Legacy store cleanup Added a fail-safe sweep for the pre-v1 global-virtual-store layout, including reachability marking, a 30-day hold, bookkeeping retirement, and CAS accounting.
  • Documentation and coverage Updated the install and virtual-store contracts and added focused checks for the trust-list addition, cold trust rejection, legacy-store safety, and JSON reporting.

Pullfrog  | View workflow run | Using GPT Sol𝕏

@colinhacks

Copy link
Copy Markdown
Contributor Author

Fixed legacy store pruning in 125de28. Relative links, aliased store paths, and custom modules directories now retain live packages. Both defects also reproduce on the merge-base. Added regression coverage; remote workspace tests, Clippy, and end-to-end pruning fixtures pass.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Important

Configured modules directories with nested paths are traversed instead of treated as scan boundaries, so store prune can walk dependency trees and retain expired legacy entries.

Reviewed changes Re-reviewed the legacy global-store pruning fix added after the prior Pullfrog review at 593fb89e.

  • Configured modules directories Added discovery of the effective modules-dir while preserving standard and symlinked node_modules trees.
  • Physical link matching Canonicalized legacy store roots and symlink targets so relative links and aliased store paths mark the same physical entry.
  • Regression coverage Added focused cases for relative links, store aliases, and configured modules-directory spellings.

⚠️ Nested configured modules directories are traversed

With modules-dir=nested/deps, the scan records project/nested/deps but still descends through project/nested and then into deps. This violates the function's stated scan boundary, can make pruning traverse an entire dependency tree, and can keep expired legacy entries that are referenced only by nested dependency node_modules.

Technical details
# Stop at configured modules-directory roots

## Affected sites
- `vendor/aube/crates/aube/src/commands/store.rs:1252-1281` — compares each entry only with `dir.join(modules_dir_name)`, so a multi-component configured path is no longer recognized when its final component is reached.
- `vendor/aube/crates/aube/src/commands/store.rs:1601-1635` — includes `nested/deps` but asserts only that the live link survives, not that the project scan avoids descending into that directory.

## Required outcome
- Every discovered standard or configured modules directory must be returned for marking without being traversed by the project/workspace scan, including multi-component configured paths.
- Coverage must fail if a nested `node_modules` below `nested/deps` is discovered by `find_node_modules_dirs`.

## Suggested approach
- Treat any path already recorded in `found` as a scan boundary before pushing it onto `stack`, rather than comparing only against the current directory's newly computed `configured` path.

Pullfrog  | Fix it ➔View workflow run | Using GPT Sol𝕏

A configured modules directory with a nested name (`nested/deps`) is
recorded from its parent, but the walk then descended into `nested/` and,
with the name recomputed one level down, no longer recognized `deps` as
the modules directory: it walked the whole dependency tree and took every
inner `node_modules` as another root. The marked set was the same, since
the marker already recurses through real directories, so this cost a
second walk of the tree rather than a wrong answer. A recorded directory
is now a boundary the walk does not re-enter; the configured-modules test
asserts the roots the walk returns.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ℹ️ No critical issues — one path-normalization edge remains.

Reviewed changes Re-reviewed the single follow-up commit after the prior Pullfrog review at 125de28b, covering the configured modules-directory traversal fix.

  • Traversal boundary Stopped project discovery from re-entering an already recorded nested modules directory, avoiding a recursive scan through its dependency tree for ordinary nested paths.
  • Regression coverage Added a nested dependency tree and an exact returned-root assertion that fails under the previous implementation.

ℹ️ Raw path spellings bypass the traversal boundary

modulesDir is preserved as a raw path, so a value such as foo/../bar records project/foo/../bar while the walk reaches project/bar. Path::starts_with returns false for those filesystem-equivalent paths, and store prune still descends into the dependency tree.

Technical details
# Normalize configured modules-directory boundaries

## Affected sites
- `vendor/aube/crates/aube/src/commands/store.rs:1285` — compares the walked path against unnormalized recorded paths.
- `vendor/aube/crates/aube-settings/build.rs:257-259` — intentionally leaves path settings raw for call-site normalization.

## Required outcome
- Every configured modules directory that `find_node_modules_dirs` records must remain a scan boundary when its spelling contains `.` or `..` components.
- The exact-root regression assertion must cover an in-project spelling such as `foo/../bar`.

## Suggested approach
- Normalize both operands before the containment check; `aube_linker::normalize_path` already provides lexical normalization without requiring a filesystem lookup.

AI-assisted — Tool: Pullfrog; model: openai/gpt-sol; version: 0.1.68.

@colinhacks

Copy link
Copy Markdown
Contributor Author

Fixed the remaining traversal case in 11d9b6f. Scan boundaries now compare physical paths, including .. after a symlink. The expanded regression test fails before the fix and passes after it; remote workspace tests, Clippy, and pruning fixtures pass.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes Re-reviewed the single follow-up commit after the prior Pullfrog review at 19184b2, covering the configured modules-directory path-normalization fix.

  • Physical traversal boundaries Canonicalized recorded modules-directory boundaries and candidate directories so raw ./.. spellings and symlink-sensitive paths resolve to the same scan boundary.
  • Regression coverage Added exact-root cases for foo/../bar, nested/./deps, and alias/../deps that fail under the previous raw-path comparison.

Pullfrog  | View workflow run | Using GPT Sol𝕏

@colinhacks
colinhacks merged commit bcbe419 into main Sep 5, 2026
56 of 59 checks passed
@colinhacks
colinhacks deleted the aube-2 branch September 5, 2026 21:37
@colinhacks

Copy link
Copy Markdown
Contributor Author

Shipped in v0.9.0: https://github.com/nubjs/nub/releases/tag/v0.9.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants