Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .claude/skills/aube-bump/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ applies. Reverting was correct. Let the tests arbitrate; don't defend a graft.
Grep after every bump — if one vanished, a resolution was wrong:

```sh
grep -rn "workspace_markers\|lockfile_basename\|EmbedderProfile\|read_branded_pnpm_config\|env_prefix\|cache_namespace\|engine_context\|env_overlay\|path_prepends\|runtime_node" vendor/aube/crates
grep -rn "workspace_markers\|lockfile_basename\|EmbedderProfile\|read_branded_pnpm_config\|env_prefix\|cache_namespace\|engine_context\|env_overlay\|path_prepends\|runtime_node\|cold_path" vendor/aube/crates
```

- **Embedder profile plumbing** — `env_prefix`, `cache_namespace`, `lockfile_basename`,
Expand All @@ -268,6 +268,17 @@ grep -rn "workspace_markers\|lockfile_basename\|EmbedderProfile\|read_branded_pn
it to show as fork delta rather than converge on the next bump.
- **Registry** — `registry_url_for` returns an owned `String` (a `namedRegistries` route lookup borrows
through a lock guard), mTLS/`npmAlwaysAuth`, the Android hickory carve-out.
- **`cold_path()` hints + the 1.95 MSRV** — `core::hint::cold_path()` on the rare arms of the hot
install loops (`aube-linker/materialize.rs` link fallbacks, `aube-resolver/semver_util.rs` cache
misses, `aube-lockfile/pnpm/subset.rs` parser bails, `aube-store/tarball.rs` validation rejects), and
the matching `rust-version = "1.95"` in `vendor/aube/Cargo.toml`. Upstream deliberately holds aube at
1.91 for mise's distro packaging; nub does not (it vendors aube and builds on stable), so this is a
standing fork delta, NOT a convergence. The `cold_path` grep above catches the hints going missing;
the Cargo.toml `rust-version` conflicts on every bump (upstream's 1.91-for-mise comment vs. nub's
divergence note) — resolve OURS every time. Behaviorally inert (a codegen hint), so it never changes
standalone-aube behavior — it just can't upstream (it would break mise's build floor). nub-cli depends
on aube, so `crates/nub-cli/Cargo.toml` sets `rust-version = "1.95"` (the workspace floor stays 1.93);
the CI MSRV `Check` job runs the root leg on 1.95.0 and the aube-free nub-phantom leg on 1.93.0.
- **Runtime / lifecycle-script env** — the embedder runtime seam. `crates/aube-util/src/engine_context.rs`
is a whole nub-only module (`EngineContext` = process-global `OnceLock<RwLock<..>>`); its
`runtime_node_dir` / `runtime_node_bin` / `env_overlay` / `path_prepends` / `lifecycle_user_agent_product`
Expand Down
6 changes: 5 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ jobs:
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
with:
persist-credentials: false
# nub-phantom is an excluded, aube-free workspace and keeps the lower 1.93
# floor, so it verifies on 1.93.0 first. The root workspace (nub-cli) depends
# on the vendored aube, which uses core::hint::cold_path() (stable in 1.95),
# so root + nub-native verify on 1.95.0.
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # 1.93.0 # zizmor: ignore[impostor-commit,ref-version-mismatch] dtolnay force-pushes its version tags, so pinned SHAs go unreachable; commit verified genuine upstream
with:
toolchain: 1.93.0
Expand All @@ -162,11 +166,11 @@ jobs:
with:
shared-key: ci-check
save-if: ${{ github.ref == 'refs/heads/main' }}
- run: cargo check --all-targets
- run: cargo check --manifest-path crates/nub-phantom/Cargo.toml --all-targets
- uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # 1.95.0 # zizmor: ignore[impostor-commit,ref-version-mismatch] dtolnay force-pushes its version tags, so pinned SHAs go unreachable; commit verified genuine upstream
with:
toolchain: 1.95.0
- run: cargo check --all-targets
- run: cd crates/nub-native && cargo check --locked --all-targets --all-features
# Real Windows compile + test coverage is the windows-latest leg of the
# `test` job. The previous fast `x86_64-pc-windows-gnu` cross-check needed
Expand Down
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ split-debuginfo = "unpacked"
[workspace.package]
version = "0.5.0"
edition = "2024"
# Workspace floor stays 1.93; only nub-cli needs 1.95 (it depends on the
# vendored aube, which uses core::hint::cold_path()), so that crate sets its
# own rust-version rather than raising every member — keeps nub-phantom-core /
# nub-phantom-scan buildable on 1.93 for the aube-free nub-phantom eval tool.
rust-version = "1.93"
license = "MIT"
repository = "https://github.com/nubjs/nub"
Expand Down
4 changes: 3 additions & 1 deletion crates/nub-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
name = "nub-cli"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
# 1.95 (above the 1.93 workspace floor) for core::hint::cold_path() in the
# vendored aube engine, a path dep of nub-cli. See vendor/aube/Cargo.toml.
rust-version = "1.95"
license.workspace = true
repository.workspace = true

Expand Down
13 changes: 9 additions & 4 deletions vendor/aube/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,15 @@ panic = "unwind"
[workspace.package]
version = "1.32.0"
edition = "2024"
# Kept at the floor mise can build with (its distro packaging pins the
# toolchain) so mise can embed the library crates. CI enforces this via
# `cargo msrv verify` — bump deliberately, not as a side effect.
rust-version = "1.91"
# DELIBERATE NUB FORK DELTA — do not converge to upstream on a sync.
# Upstream holds this at 1.91 so mise (which embeds aube's library crates and
# is packaged by distros that pin an old toolchain) can build it. nub does not
# ship that way: it vendors aube and compiles every binary on current stable,
# and nub CI does not MSRV-check the vendored tree — so upstream's floor does
# not bind us. We raise it to 1.95 for core::hint::cold_path() on the rare arms
# of the hot install loops (linker, resolver cache, lockfile subset parser,
# tarball validation). Preserve this on every aube bump — see the aube-bump skill.
rust-version = "1.95"
license = "MIT"
repository = "https://github.com/jdx/aube"
homepage = "https://github.com/jdx/aube"
Expand Down
4 changes: 4 additions & 0 deletions vendor/aube/crates/aube-linker/src/materialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,7 @@ impl Linker {
let target = pkg_nm_dir.join(rel_path);

if let Err(e) = self.link_file_fresh(stored, rel_path, &target) {
core::hint::cold_path();
if let Error::MissingStoreFile { .. } = &e {
invalidate_stale_index_for_package(&self.store, pkg, self.index_read_key(pkg));
}
Expand Down Expand Up @@ -862,6 +863,7 @@ impl Linker {
}
};
if let Err(e) = reflink_result {
core::hint::cold_path();
// Source-missing short-circuit avoids the misleading
// "fell back to copy" trace and the redundant copy
// attempt that would just ENOENT for the same reason.
Expand Down Expand Up @@ -917,6 +919,7 @@ impl Linker {
}
LinkStrategy::Hardlink => {
if let Err(e) = std::fs::hard_link(&stored.store_path, dst) {
core::hint::cold_path();
if !stored.store_path.exists() {
return Err(missing_source());
}
Expand Down Expand Up @@ -1219,6 +1222,7 @@ impl Linker {
for (rel_path, stored) in index {
let target = tmp.join(rel_path);
if let Err(e) = self.link_file_fresh(stored, rel_path, &target) {
core::hint::cold_path();
let _ = std::fs::remove_dir_all(&tmp);
if let Error::MissingStoreFile { .. } = &e {
invalidate_stale_index_for_package(&self.store, pkg, self.index_read_key(pkg));
Expand Down
28 changes: 24 additions & 4 deletions vendor/aube/crates/aube-lockfile/src/pnpm/subset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,10 @@ impl<'a> Parser<'a> {
}
// Any top-level key we don't recognize: bail to serde so
// we never silently drop a field a future pnpm adds.
_ => return None,
_ => {
core::hint::cold_path();
return None;
}
}
}

Expand Down Expand Up @@ -304,6 +307,7 @@ impl<'a> Parser<'a> {
break;
}
if line.indent != min_indent {
core::hint::cold_path();
return None;
}
let (k, v) = split_key(line.body)?;
Expand Down Expand Up @@ -350,6 +354,7 @@ impl<'a> Parser<'a> {
break;
}
if line.indent != 2 {
core::hint::cold_path();
return None;
}
// `<importerPath>:` header. pnpm writes an empty importer
Expand Down Expand Up @@ -395,7 +400,10 @@ impl<'a> Parser<'a> {
b"devDependencies" => dev_dependencies = Some(specs),
b"optionalDependencies" => optional_dependencies = Some(specs),
b"skippedOptionalDependencies" => skipped_optional_dependencies = Some(specs),
_ => return None,
_ => {
core::hint::cold_path();
return None;
}
}
}
Some(RawImporter {
Expand All @@ -415,6 +423,7 @@ impl<'a> Parser<'a> {
break;
}
if line.indent != entry_indent {
core::hint::cold_path();
return None;
}
let (name, rest) = split_key(line.body)?;
Expand All @@ -432,6 +441,7 @@ impl<'a> Parser<'a> {
break;
}
if c.indent != child_indent {
core::hint::cold_path();
return None;
}
let (ck, cv) = split_key(c.body)?;
Expand All @@ -440,7 +450,10 @@ impl<'a> Parser<'a> {
match ck {
b"specifier" => specifier = Some(scalar_string(cv)?),
b"version" => version = Some(scalar_string(cv)?),
_ => return None,
_ => {
core::hint::cold_path();
return None;
}
}
}
map.insert(
Expand Down Expand Up @@ -468,6 +481,7 @@ impl<'a> Parser<'a> {
break;
}
if line.indent != 2 {
core::hint::cold_path();
return None;
}
let (key, rest) = split_key(line.body)?;
Expand Down Expand Up @@ -510,6 +524,7 @@ impl<'a> Parser<'a> {
break;
}
if line.indent != 2 {
core::hint::cold_path();
return None;
}
let (key, rest) = split_key(line.body)?;
Expand Down Expand Up @@ -540,6 +555,7 @@ impl<'a> Parser<'a> {
break;
}
if line.indent != indent {
core::hint::cold_path();
return None;
}
let (key, rest) = split_key(line.body)?;
Expand All @@ -560,7 +576,10 @@ impl<'a> Parser<'a> {
b"transitivePeerDependencies" => {
transitive_peer_dependencies = Some(self.parse_seq_value(rest, indent + 2)?);
}
_ => return None,
_ => {
core::hint::cold_path();
return None;
}
}
}
Some(RawSnapshot {
Expand Down Expand Up @@ -637,6 +656,7 @@ fn split_key(body: &[u8]) -> Option<(&[u8], Option<&[u8]>)> {
}
i += 1;
}
core::hint::cold_path();
None
}

Expand Down
2 changes: 2 additions & 0 deletions vendor/aube/crates/aube-resolver/src/semver_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ fn with_cached_range<R>(range_str: &str, f: impl FnOnce(Option<&node_semver::Ran
CACHE.with(|cell| {
let mut map = cell.borrow_mut();
if !map.contains_key(range_str) {
core::hint::cold_path();
let parsed = node_semver::Range::parse(range_str).ok();
map.insert(range_str.to_string(), parsed);
}
Expand All @@ -527,6 +528,7 @@ fn with_cached_version<R>(version: &str, f: impl FnOnce(Option<&node_semver::Ver
CACHE.with(|cell| {
let mut map = cell.borrow_mut();
if !map.contains_key(version) {
core::hint::cold_path();
let parsed = node_semver::Version::parse(version).ok();
map.insert(version.to_string(), parsed);
}
Expand Down
4 changes: 4 additions & 0 deletions vendor/aube/crates/aube-store/src/tarball.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ impl Store {
for entry in archive.entries().map_err(|e| Error::Tar(e.to_string()))? {
entries_seen += 1;
if entries_seen > MAX_TARBALL_ENTRIES {
core::hint::cold_path();
return Err(Error::Tar(format!(
"tarball exceeds entry cap of {MAX_TARBALL_ENTRIES}"
)));
Expand Down Expand Up @@ -381,6 +382,7 @@ impl Store {
entry_type,
tar::EntryType::Regular | tar::EntryType::Continuous
) {
core::hint::cold_path();
return Err(Error::Tar(format!(
"tarball entry type {entry_type:?} is not allowed"
)));
Expand All @@ -395,6 +397,7 @@ impl Store {
.size()
.map_err(|e| Error::Tar(e.to_string()))?;
if declared > MAX_TARBALL_ENTRY_BYTES {
core::hint::cold_path();
return Err(Error::Tar(format!(
"tarball entry exceeds per-entry cap: {declared} bytes > {MAX_TARBALL_ENTRY_BYTES}"
)));
Expand Down Expand Up @@ -425,6 +428,7 @@ impl Store {
// non-empty stream. Synthetic-entry injection: header
// claims empty file, real bytes go to disk.
if declared == 0 && !content.is_empty() {
core::hint::cold_path();
return Err(Error::Tar(format!(
"tarball entry declared 0 bytes but yielded {} bytes",
content.len()
Expand Down
Loading