build: 7-day soak across toolchain and deps, cold_path() hints, pinned tooling - #442
Conversation
Exact stable pin via rust-toolchain.toml so all four workspaces (nub, nub-native, nub-phantom, vendor/aube) build with one toolchain. 1.95 is the floor for core::hint::cold_path().
core::hint::cold_path() (stable since rust 1.95) annotates the rare arm so the optimizer sinks it off the hot path. Sites: the per-file materialize loops' reflink/hardlink fallbacks and link-error returns (project-local and GVS staging), the pnpm-lock byte-cursor subset parser's decline-to-serde bails, the thread-local semver range and version cache misses, and the per-entry tar walk's validation rejects. No behavior change; hints only.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
ℹ️ No critical issues — two minor observations, both non-blocking.
Reviewed changes — pins an exact stable toolchain (rust-toolchain.toml, 1.95.0), bumps every workspace rust-version to 1.95, and adds core::hint::cold_path() codegen hints to rare arms of hot install loops in the vendored aube engine. Hints only, no behavior change.
- Pin the toolchain to 1.95.0 — new repo-root
rust-toolchain.tomlfixeschannel = "1.95.0"plusrustfmt/clippyso all four workspaces build on one compiler. - Bump
rust-versionto 1.95 —Cargo.toml,crates/nub-native/Cargo.toml,crates/nub-phantom/Cargo.toml(1.88 → 1.95) andvendor/aube/Cargo.toml(1.93 → 1.95). 1.95.0 is the floor forcold_path(). - Mark cold branches with
cold_path()— link fallbacks inmaterialize.rs, decline-to-serde/indent-mismatch bails in the pnpm subset parser (subset.rs), semver cache misses insemver_util.rs, and tar-walk validation rejects intarball.rs.
I verified the load-bearing claim: core::hint::cold_path() is stable since 1.95.0 (const fn), and 1.95.0 is a real stable release (16 Apr 2026). The placements are sound — the cache-miss arms in semver_util.rs are documented as trending to a ~1.0 hit rate after the first BFS layer, so marking the miss path cold is justified.
ℹ️ Stale MSRV in the vendored aube docs
Bumping vendor/aube/Cargo.toml from rust-version = "1.93" to "1.95" leaves the vendored aube's own agent docs claiming MSRV 1.93. Those docs aren't in this diff, but this change is what makes them stale — and the aube-parity doctrine keeps the vendored tree self-consistent so it stays conceivably upstreamable, so the drift is worth closing in the same PR.
Technical details
# Stale MSRV in vendored aube docs after rust-version bump
## Affected sites
- `vendor/aube/.rules:6` — `msrv: 1.93` (mirrored by `vendor/aube/AGENTS.md`)
- `vendor/aube/.rules:190` — `edition 2024, latest stable toolchain, msrv 1.93 (...)`
## Required outcome
- The vendored aube MSRV documentation reflects 1.95, matching `vendor/aube/Cargo.toml`'s `rust-version`.
## Open questions for the human
- Whether to update the aube-side docs here or leave them for a dedicated vendor-sync commit. Either is defensible; the risk of leaving them is a future upstream diff carrying a contradictory MSRV.Claude Opus | 𝕏
Adopt the newest nightly at least 7 days old as of 2026-07-11; bump the pin under the same soak rule. rust-version stays 1.95 as the stable msrv floor. Verified on nightly-2026-07-04: clippy --all-targets -D warnings clean across nub and vendor/aube.
There was a problem hiding this comment.
Important
The repo-root pin now propagates a nightly toolchain into the release build, so shipped binaries compile on nightly. Worth an explicit decision before merging.
Reviewed changes — since the prior pullfrog review, the single incremental change swaps the repo-root toolchain pin from stable 1.95.0 to nightly-2026-07-04 (with a 7-day-soak adoption comment). The rust-version bumps to 1.95 and the cold_path() hints are unchanged from the prior review.
- Switch the toolchain pin to nightly —
rust-toolchain.tomlnow pinschannel = "nightly-2026-07-04"instead of stable1.95.0; the comment documents a "newest nightly ≥7 days old" adoption/bump policy.
⚠️ Shipped release binaries now build on nightly
Every CI workflow installs dtolnay/rust-toolchain@... # stable, but the repo-root rust-toolchain.toml overrides that for all cargo invocations. The per-platform release build runs at repo root, so the 8-platform binaries published to npm now compile on nightly-2026-07-04 rather than stable. Nightly carries weaker stability and soundness guarantees than stable; making it the compiler for distributed artifacts is a posture change worth an explicit, conscious decision rather than a side effect of the dev-toolchain pin.
Technical details
# Release binaries compile on the pinned nightly
## Affected sites
- `rust-toolchain.toml:8` — `channel = "nightly-2026-07-04"` at repo root applies to all cargo invocations.
- `.github/workflows/release.yml:466-471` — `cargo build --release -p nub-cli --features embed-runtime --target <t>` / `cross build --release ...` build the shipped binaries at repo root, so the pin applies. `cross` reads `rust-toolchain.toml` and provisions the pinned toolchain in-container, so the container path is not an escape.
- `.github/workflows/release.yml:266` and every other workflow — `dtolnay/rust-toolchain@... # stable` installs stable but the pin supersedes it for cargo.
## Required outcome
- A deliberate decision on whether production release binaries should build on a pinned nightly, and if so, that intent recorded where the release toolchain is chosen (not only in the dev-tooling comment).
## Open questions for the human
- Is nightly intended to apply to the release build, or only to local/CI dev builds? If the latter, the release job could pin an explicit stable toolchain (e.g. a scoped `+stable` / `RUSTUP_TOOLCHAIN`), decoupling ship-toolchain from dev-toolchain.ℹ️ rust-version = 1.95 is no longer verified by any build
Nothing in the tree requires nightly to compile — the stated justification (core::hint::cold_path()) is stable since 1.95.0, and there are no #![feature(...)] gates anywhere. With the toolchain pinned to nightly and rust-version declaring a stable-1.95 floor, no build actually exercises stable 1.95, so a nightly-only construct or a stable-1.95 regression would pass CI undetected. The declared MSRV becomes aspirational rather than enforced.
Technical details
# MSRV floor is decoupled from the build toolchain
## Affected sites
- `Cargo.toml:95`, `crates/nub-native/Cargo.toml:34`, `crates/nub-phantom/Cargo.toml:22`, `vendor/aube/Cargo.toml:23` — `rust-version = "1.95"`.
- `rust-toolchain.toml:8` — build toolchain is `nightly-2026-07-04`, not stable 1.95.
## Required outcome
- Either an MSRV-verification signal (a job that builds on stable 1.95 / `rust-version`), or an explicit acknowledgement that `rust-version` is a documentation-only floor while the build tracks nightly.
## Open questions for the human
- Is a nightly toolchain actually needed, given `cold_path()` is stable on 1.95? If not, pinning stable 1.95 keeps ship-toolchain, dev-toolchain, and the declared MSRV consistent — which was the state at the prior review.Claude Opus | 𝕏
Crate versions published less than 7 days ago are pubtime-incompatible and the resolver skips them unless already in Cargo.lock. Unstable cargo feature, active via the nightly pinned in rust-toolchain.toml; stable cargo ignores the [unstable] table. Unignore the root .cargo/config.toml the same way as the nub-native one.
There was a problem hiding this comment.
ℹ️ No critical issues — the incremental change is correct; one observation ties it to the still-open nightly-posture discussion.
Reviewed changes — since the prior pullfrog review, the single incremental change adds a repo-root .cargo/config.toml enabling cargo's min-publish-age unstable feature and the .gitignore allow-entries needed to track it. The nightly pin, rust-version bumps, and cold_path() hints are unchanged from the prior review.
- Enable a
min-publish-agesupply-chain soak — new.cargo/config.tomlsets[unstable] min-publish-age = trueand[registry] global-min-publish-age = "7 days", so cargo's resolver skips crate versions published in the last 7 days unless already inCargo.lock, mirroring the PM'sminimumReleaseAgediscipline. - Track the root
.cargo/past the broad ignore —.gitignoreadds!.cargo/and!.cargo/config.toml, matching the existingcrates/nub-native/.cargo/allow-entry so the broad.*rule doesn't drop the config.
I verified the config against the Cargo Book's unstable-features page: [unstable] min-publish-age = true is the correct enabling key, global-min-publish-age accepts the "N days" format, and resolver.incompatible-publish-age defaults to "deny" (ignore too-new versions unless already locked) — so the comment's claims are accurate and the file is well-formed.
ℹ️ The soak config makes the nightly pin load-bearing, reinforcing the open nightly-posture question
min-publish-age is a nightly-only cargo feature — the [unstable] table is silently ignored on stable cargo. Before this commit, the prior review noted nothing in the tree actually required nightly (cold_path() is stable on 1.95). This commit changes that: the build config now genuinely depends on nightly, so reverting to stable 1.95 would silently no-op the soak rather than fail. This doesn't introduce a bug — it strengthens the premise of the two still-open findings from the prior review (shipped binaries building on nightly, and rust-version = 1.95 not being verified by any build), which remain human decisions.
Technical details
# min-publish-age couples the supply-chain soak to the nightly pin
## Affected sites
- `.cargo/config.toml:7-8` — `[unstable] min-publish-age = true` is nightly-only; ignored by stable cargo.
- `rust-toolchain.toml:8` — `channel = "nightly-2026-07-04"` is what makes the `[unstable]` key active.
## Required outcome
- No code change required — the config is correct. This is a note that the nightly pin is now load-bearing for the soak, not only a policy convenience for the (stable) `cold_path()` hints.
## Open questions for the human
- The prior review's open questions still stand and are now coupled: if release binaries were to pin stable to decouple ship-toolchain from dev-toolchain, the `min-publish-age` soak would silently stop applying to that build. Worth deciding whether the soak should hold on the release path specifically.Claude Opus | 𝕏
|
On the nightly-posture question: deliberate. The dated-nightly pin (7-day soak on toolchain adoption) is intentionally load-bearing for the -Zmin-publish-age soak; decoupling release builds to stable would silently drop the soak there, so the release path keeps the pinned nightly. rust-version=1.95 stays the declared stable floor. Tracking: rust-lang/cargo#15973 (now rust-lang/cargo#17009) — revisit the pin when min-publish-age stabilizes. |
scripts/soak/constants.mts is the one source for the release-age window; soak.mts parity-checks and fixes every surface (.cargo min-publish-age, pnpm-workspace.yaml minimumReleaseAge in minutes, .npmrc min-release-age in days, taze maturityPeriod via direct import) and enforces lockstep between catalog entries and catalog-shadowed exact pins. Version-pinned soak excludes require a '# published: | removable:' date annotation and expired pins are pruned by --fix. update-deps.mts bumps npm (taze) and cargo through the same window, refusing a non-rustup cargo that would silently skip the [unstable] soak. external-tools.json pins external security tools exact with sha512 SRI; external-tools.mts is the only install path; paths.mts is the single declaration of every location the scripts touch. taze is exact-pinned in package.json (npm cannot parse the catalog: protocol and wpt-worker runs a root npm ci) with the catalog entry as the enforced reference.
…d zizmor docs-links runs soak.mts --check and external-tools.mts --check; the test job installs sfw and its package-manager shims before pnpm install. The SOCKET_SECURITY_KEY repo secret (not yet set) feeds SOCKET_API_KEY + SOCKET_API_TOKEN; until it lands sfw runs its keyless free tier. zizmor.yml lints workflow files on PRs.
The taze config sets write: true, so a dry run must pass --no-write explicitly or it rewrites package.json; also suppress the ni install prompt with --no-install.
A hung release or registry response would stall CI indefinitely; abort after 120s (generous for the largest pinned binary on a slow runner).
- reject flow-style minimumReleaseAgeExclude (invisible to the block parser = unvalidated bypass) and parse entries with trailing comments; catalog parity tolerates blank lines in the block - entrypoint guard uses realpath + pathToFileURL so symlinked or space-containing checkouts don't silently no-op the gate - linkHandle force-removes dangling handles (EEXIST wedge) - extract .zip release assets via bsdtar and verify the binary landed - GITHUB_TOKEN only sent to github.com, never the npm registry - npm-purl installs key completion off the extracted manifest so an interrupted extract self-heals - update-deps runs every requested ecosystem and reports spawn errors - sfw install/shims skip cleanly on windows (POSIX-only mechanism) - Socket credential is SOCKET_SECURITY_KEY everywhere — no SOCKET_API_* aliasing in CI env or the enterprise-tier check
There was a problem hiding this comment.
Important
The socket firewall doesn't actually take effect on the Windows CI leg, so pnpm install there runs unfirewalled without any signal. Worth addressing before merge.
Reviewed changes — since the prior pullfrog review (1012a75), this run covers the two new commits that add the supply-chain soak machinery and route CI installs through a firewall. The nightly pin, rust-version bumps, .cargo/config.toml, and cold_path() hints are unchanged.
- Add the
scripts/soak/cooldown manager — one canonicalSOAK_DAYSinconstants.mts;soak.mtsparity-gates.cargo/config.toml/.npmrc/pnpm-workspace.yaml/taze.config.mtsagainst it, enforces annotated-exclude expiry, and checks catalog↔package.jsonlockstep. - Add pinned external security tooling —
external-tools.jsonpinssfw,zizmor,agentshield,skillspectorto exact versions with per-platform sha512;external-tools.mtsdownloads, SRI-verifies, installs, and writessfwfirewall shims for npm/yarn/pnpm/pip/uv/cargo. - Wire the firewall + gates into CI —
ci.ymlinstalls the sfw shims and prepends the rack bin to$GITHUB_PATHbeforepnpm install, and feedsSOCKET_API_KEY/SOCKET_API_TOKENfrom a repo secret; the docs job runs the soak + tool-pin--checkgates. - Add the
zizmorworkflow-security lint — newzizmor.ymlwithpermissions: {}at workflow scope andpersist-credentials: false. - Add
tazeas a soaked dependency updater — devDep + catalog entry;update-deps.mtsruns taze thenpnpm install, andcargo updateonly via the rustup shim so themin-publish-agesoak applies.
The prior review's inline thread on the # stable CI / nightly / MSRV interaction was addressed by the author's "Deliberate" reply and resolved, so those posture questions are settled and not re-raised here.
ℹ️ Nitpicks
scripts/soak/update-deps.mts:64-73— passing both--npmand--cargomakes!onlyCargoand!onlyNpmboth false, so neither ecosystem updates and the script exits0. Reject the combination or treat the flags as additive selectors..github/workflows/ci.yml:547-549—SOCKET_API_KEY/SOCKET_API_TOKENare set at job scope, so the Socket secret is ambient tocargo build/cargo test(which run third-party build scripts) and the side-Node curl steps too. Scoping to just the shim + install steps would cut the blast radius.scripts/soak/external-tools.mts:154-161,203-210— thetar -xzfextraction has no path-traversal/entry-type guard, unlike this repo's ownvendor/aube/crates/aube-store/src/tarball.rs. Defense-in-depth only (requires a prior SRI/pin compromise), but the asymmetry is worth a note.
Claude Opus | 𝕏
untracked 1.6.4 (catalog-shadowed pin, lockstep-enforced) maintains a generated exclusion section in .dockerignore between markers (pnpm run dockerignore:update), shrinking the docker build context for the from-source images; the package.json whitelist rescues the load-bearing .cargo configs (crates/nub-native/.cargo routes in-container build output to the shared target dir). external-tools --check gains a docker-prebake parity gate (null surface here — nub's docker images are product artifacts, not dev-tool prebakes).
There was a problem hiding this comment.
ℹ️ No new issues in the fix commits. Two prior findings remain open (both latent).
Reviewed changes — this run covers the three fix commits pushed in response to the prior review, which harden the soak gates and the external-tool installer. The delta introduces no new issues.
- Skip the sfw firewall cleanly on Windows —
installToolandwriteShimsshort-circuit onwin32with a log line, so the POSIX-only shim mechanism is an explicit, visible gap rather than a silent fail-open on the Windows CI leg. - Scope the
GITHUB_TOKENto GitHub hosts —download()attaches theauthorizationheader only when the URL host isgithub.com, so the token is no longer sent to the npm registry on the purl path. - Make
update-depsflags additive and the dry run actually dry — passing both--npmand--cargonow runs both, and--dry-runoverrides the taze config'swrite: truewith--no-write --no-install. - Harden the installer — download timeout (
AbortSignal.timeout),.zipextraction plus a missing-binary check, dangling-symlink handling, and a manifest-based completion marker so an interrupted npm extract self-heals. - Tighten the soak parser — reject flow-style
minimumReleaseAgeExclude: [...](invisible to the block parser), tolerate trailing comments on exclude entries, handle blank lines inside thecatalog:block, and makeisMaindetection userealpath+pathToFileURL.
The two ⚠️/ℹ️ threads the fix commits addressed (Windows firewall skip, GITHUB_TOKEN host-gating) are resolved. Two informational threads remain open — addDaysIso throwing on a shape-valid-but-invalid date, and checkPins not flagging an expired soakBypass. Both are latent (no current data exercises either), so they don't block merge, but they're left open since the code doesn't yet address them.
Claude Opus | 𝕏
267 findings to zero at the default persona: persist-credentials:
false on every checkout that doesn't push (the homebrew-tap bump
keeps its credentialed checkout, inline-ignored with the reason);
workflow-level permissions: {} with per-job scopes matched to what
each job's steps actually use; all template-injection sites moved to
env-var indirection (28 in release.yml alone, zero behavior change);
adhoc package installs documented as already env-pinned; deliberate
residuals carry inline zizmor ignores with per-site verified reasons
(cache-poisoning on gate-only jobs, secrets-inherit where docker.yml
declares no workflow_call secrets).
Same mechanism as the sibling repos: SRI-pinned pnpm/npm in external-tools.json (npm 12.0.0 carries a dated soakBypass annotation), CI installs both before the firewall shims, shims wrap the pinned binaries directly, and npm-packaged external tools materialize runtime deps through nub itself first (pnpm/npm fallbacks), prod-only with scripts skipped. Also hardens two skill docs flagged by static skill scanning: the vendor-sync rsync --delete now guards against an empty staging tree, and the bench cache-wipe recipe fails loudly on an empty cache-path substitution.
There was a problem hiding this comment.
Important
The new pnpm/npm firewall-shim wiring overwrites the pinned pnpm/npm wrapper it depends on, so pnpm/npm cannot resolve through the shims on the Linux and macOS test legs. One inline finding; worth fixing before merge.
Reviewed changes — since the prior pullfrog review (7e422e3), this run covers the two new commits: workflow-security hardening across all workflows, and pinning pnpm/npm into the tool rack so CI installs run through the pinned binaries + the socket firewall.
- Harden every workflow against zizmor findings — workflow-level
permissions: {}, per-job least-privilege scopes,persist-credentials: falseon all checkouts, SHA-pinned actions, and env-var indirection for every${{ github.* }}/${{ steps.* }}interpolation inrun:steps (values now read from a stepenv:block), with justified inlinezizmor: ignoreon the gate-only cache and vendored-template cases. - Pin pnpm 11.8 + npm 12 via the tool rack —
external-tools.jsonadds a per-platform pnpm SEA (plus a darwin-x64 registry.tgzfallback) and a single npm registry tarball with asoakBypass;external-tools.mtsfactors the registry-tarball install into a sharedinstallNpmTarballand materializes tool deps throughPM_DEP_INSTALLERS(dogfoodingnub install --prodfirst, then pnpm/npm). - Route CI through the pinned PMs — the
testjob installspnpm/npm/sfwand writes the firewall shims in one call, then assertspnpm --version/npm --versionresolve.
The zizmor hygiene commit is mechanical and correct — the env-var-indirection rewrites preserve the original interpolated values and the inline ignores are individually justified. The prior two latent threads (addDaysIso on an invalid date, checkPins not flagging an expired soakBypass) are unchanged by these commits and remain open.
Claude Opus | 𝕏
…unit tests writeShims wrote THROUGH the rack symlink handle, overwriting the pinned binary with the shim body — which then exec'd itself forever. Remove the handle before writing. Windows .zip archives resolve <bin>.exe before failing, and a failed extract cleans its dir so retries re-extract. Annotation and soakBypass dates must be real calendar dates (finding, not a RangeError crash), and an expired soakBypass is flagged like any other soaked pin. node:test unit tests cover the checkers, fixers, and parsers (pnpm run test:scripts; also in the CI gate).
There was a problem hiding this comment.
✅ No new issues found in the incremental change. One prior non-blocking thread remains open.
Reviewed changes — the single new commit 49610a3 adds a dev-machine remote-map guard: a standalone script declaring where this repo's PRs open versus where branches push, plus unit tests. It is not wired into any CI gate or soak surface.
- Declare the two GitHub homes as law — new
scripts/soak/remotes.mtsfixesPR_HOME(nubjs/nub, theoriginPRs open on) andPUSH_HOME(jdalton/nub, theforkbranches push to).--checkverifies a clone's remotes match,--fixadds or repoints only theforkremote (originis report-only, never auto-rewritten), and--printemits the map as JSON. - Cover the pure core — new
scripts/soak/remotes.test.mtsexercises both arms ofnormalizeGitHubRepo(https / scp-ssh /ssh://, with and without.git, non-github rejected),parseRemotes, andcheckRemotesdrift classification;remotes:check/remotes:fixpackage.jsonscripts drive the CLI.
I verified the load-bearing pieces. The normalizeGitHubRepo regex requires an exact owner/repo pair, so a truncated scp URL (git@github.com:aube.git) correctly returns null, and checkRemotes compares case-insensitively while ignoring undeclared extra remotes. The --check path self-skips under CI (unless --fix), the --fix path touches only the fork remote and writes an SSH URL, and the test suite is hermetic — it imports only node: builtins plus the local module and exercises the pure functions with no git spawn, so the test:scripts glob picks it up safely on the CI docs job.
One prior open thread — the rack.test.mts win32 skip-guard nit — is untouched by this commit and left open. It's a local-Windows-dev-only test failure (CI runs test:scripts on ubuntu-latest), so it doesn't block merge.
Claude Opus | 𝕏
…e, parity-checked
There was a problem hiding this comment.
ℹ️ No new issues in the incremental change. One prior non-blocking thread remains open.
Reviewed changes — since the prior pullfrog review (49610a3, whose own commit was already reviewed), the one new commit ff97dbf closes the last dependency path the local soak surfaces couldn't reach: Renovate updates.
- Gate
renovate.jsonagainst the soak window — newcheckRenovateConfig/fixRenovateConfiginscripts/soak/soak.mtsdemand an explicit top-levelminimumReleaseAgeof"7 days"(anextends:preset value doesn't count, since a preset can drift without a commit here); registered as a fixer-backed surface and exported alongside the other checks. - Carry the window in
renovate.json— the config gains top-level"minimumReleaseAge": "7 days"plus"internalChecksFilter": "strict", so Renovate actually holds a too-fresh PR back rather than only annotating it. - Register the surface and document it —
paths.mtsaddsSURFACES.renovateJson, and thesoakskill's description + surface table listrenovate.json. - Cover both functions — two hermetic tests assert preset-inheritance-is-drift and that
--fixsets the window, preserves other keys, and is idempotent.
The rationale is sound: Renovate bumps manifests and lockfiles server-side, and cargo's min-publish-age skips already-locked versions, so a Renovate PR would otherwise sidestep every local soak surface. Top-level minimumReleaseAge cascades to all enabledManagers per the Renovate schema, and internalChecksFilter: "strict" is the load-bearing half that makes the age check block rather than advise. Verified locally: node scripts/soak/soak.mts --check exits 0 with the renovate surface included, and all 22 soak.test.mts cases pass, including the two new renovate tests and main --check against the tracked repo state.
The one prior thread — the rack.test.mts:41 win32 skip-guard nit — is untouched by this commit and remains open. It's a local-Windows-dev-only test failure (CI runs test:scripts on ubuntu-latest), so it doesn't block merge.
Claude Opus | 𝕏
There was a problem hiding this comment.
ℹ️ No new issues in the incremental change. One prior non-blocking thread remains open.
Reviewed changes — since the prior pullfrog review (ff97dbf), the two new commits fix a portability bug in the commit-msg hook and pin its behavior with tests.
- Fix the BSD-awk breakage in the
commit-msghook —.githooks/commit-msgdrops theEND { /* ... */ }block whose C-style comment broke BSDawk, silently skipping the trailing-blank collapse. The rewrite relies on trailing blanks never being flushed at EOF, so the collapse-runs-to-one and drop-trailing semantics are preserved without anENDblock. - Pin the hook's behavior — new hermetic
scripts/soak/commit-msg-hook.test.mtsasserts exit0and empty stderr across five cases (agent-trailer strip, human co-author preserved, blank-run collapse plus trailing-drop, subject-only leaves no trailing blank, hook present at thecore.hooksPathlocation). Every case asserts an empty stderr, so a tool erroring mid-pipeline is a failure even when the message still comes out right — which is the incident this pins.
The awk rewrite is behavior-preserving: blank lines accumulate a counter and a run is emitted as at most one blank only before the next non-blank line, so EOF-trailing blanks are dropped as intended (traced against the feat: subject\n\n\n\nbody line\n\n\n → feat: subject\n\nbody line\n case). The tests are contract-named and comprehensive without bloat.
The one prior open thread — the rack.test.mts:41 win32 skip-guard nit — is untouched by these commits and remains open. It's a local-Windows-dev-only test failure (CI runs test:scripts on ubuntu-latest), so it doesn't block merge.
Claude Opus | 𝕏
The post-extract path resolves <bin>.exe for windows archives, but the pre-download idempotency guard checked only the suffix-less destBin, so a second --install on windows re-downloaded and re-extracted (and wedges on a restricted/offline runner). Mirror the same .exe resolution in the early-exit. Mirrors jdx/aube#1028.
There was a problem hiding this comment.
ℹ️ No new issues in the incremental change. One prior non-blocking thread remains open.
Reviewed changes — the single new commit 003447a makes a repeat Windows tool install a no-op instead of forcing a re-download, closing the last idempotency gap in the pinned-tool installer.
- Resolve
<bin>.exein the pre-download early-exit —installAssetToolnow checks for a<bin>.exevariant before deciding to download, mirroring the identical resolution the post-extract path already does. On Windows a tool installed as<bin>.exewas invisible to the "already installed" check, so a second--installre-downloaded — which wedges on a restricted/offline runner.
The change is sound and behavior-preserving off Windows. The new block is logically identical to the post-extract block from d947ec8, and passing the .exe-suffixed binName into linkHandle is safe: on win32 linkHandle strips the trailing .exe from the handle base and sets viaExe = true, so the .cmd/bash forwarders dispatch to the .exe directly — exactly matching a fresh install. On non-Windows platforms no <bin>.exe exists, so the block is a no-op. Only fully provable on the windows-latest test leg.
The one prior open thread — the rack.test.mts:41 win32 skip-guard nit — is untouched by this commit and remains open. It's a local-Windows-dev-only test failure (CI runs test:scripts on ubuntu-latest), so it doesn't block merge.
Claude Opus | 𝕏
# Conflicts: # .claude/skills/aube-sync/SKILL.md # .github/workflows/ci.yml # Cargo.toml # crates/nub-phantom/Cargo.toml # vendor/aube/Cargo.toml
A repo-root rust-toolchain.toml outranks `rustup default` in rustup's toolchain precedence, and dtolnay/rust-toolchain only sets the default. Pinning nightly there therefore redirected every version-pinned CI job -- including the MSRV Check legs that compile on 1.93.0 and 1.95.0 -- and built the released binaries on nightly rather than stable. min-publish-age is the only thing that needed nightly, and it only matters where versions are chosen. Request it per invocation there: update-deps.mts now runs `cargo +nightly update`. Every CI job and every shipped binary builds on stable again, and the MSRV legs verify their declared floors. Drops the now-unreachable toolchain-soak checker and its tests; the remaining surfaces are unchanged.
There was a problem hiding this comment.
ℹ️ No new issues in the incremental change. One prior non-blocking thread remains open.
Reviewed changes — the latest commit drops the repo-root nightly toolchain pin and, together with two merges from main, this leaves the PR carrying no Rust changes at all. The remaining supply-chain-soak, workflow-security, and tooling work is unchanged and internally consistent.
- Drop the repo-root nightly pin —
rust-toolchain.tomlis deleted; the cargomin-publish-agesoak is now requested per-invocation viacargo +nightly updateinupdate-deps.mts(the only step that picks versions), because a repo-root toolchain file would outrankrustup defaultand silently redirect the version-pinned CI/MSRV legs and the release build. - Remove the toolchain soak gate —
checkToolchainSoakand its two tests are removed fromsoak.mts/soak.test.mts, andrust-toolchain.tomlis dropped from the surfaces array; no dangling references remain. - Pin the primary Rust checks job to 1.93.0 — the
Rust checksjob now installstoolchain: 1.93.0, consistent with the revertedrust-version = "1.93"; other Rust jobs stay on# stable. - Retire the expired npm
soakBypass— theexternal-tools.jsonnpm entry drops itssoakBypassnow that12.0.0has cleared the 7-day window. - Reword the soak docs + guard a benchmark cache wipe —
.cargo/config.tomland thesoakskill explain the no-nightly-pin posture; the benchmarking skill guards its cache-dirrm -rfagainst an empty substitution.
Verified locally on Node 24: soak --check (all surfaces match SOAK_DAYS=7), tools:check (7 pins valid), and test:scripts (61 pass / 1 win32-skip / 0 fail) are all green — the three gates CI runs. The 1.93.0 pin is consistent with the reverted MSRV and no cold_path() remains in the tree, so there is no 1.95 floor to break.
ℹ️ PR title and description no longer match the diff
The title and description still headline cold_path() hints, the pinned nightly toolchain, and the rust-version bump to 1.95 — but the final diff (53 files) contains no .rs files, no Cargo.toml changes, and no rust-toolchain.toml. Those Rust changes were reconciled out by the merges from main (main's vendored aube tree evolved past the cold_path() patch) and the nightly-pin drop. The PR is coherent and mergeable as-is; this is only a note that the metadata now over-promises relative to what ships.
Technical details
# PR metadata advertises Rust work the diff no longer contains
## Affected sites
- PR #442 title: "build: 7-day soak across toolchain and deps, cold_path() hints, pinned tooling"
- PR #442 body: the "Toolchain + cold-path hints" section describes `nightly-2026-07-04`, `rust-version` 1.88 → 1.95, and `cold_path()` in `materialize.rs`/`subset.rs`/`semver_util.rs`/`tarball.rs`.
- Final diff (`426042a`): 0 `.rs` files, 0 `Cargo.toml`, 0 `rust-toolchain.toml`. Working tree `rust-version` = 1.93 (root) / 1.91 (vendor/aube); grep of `vendor/aube` = 0 `cold_path`.
## Required outcome
- A conscious decision on whether the dropped Rust work (nightly pin, 1.95 bump, `cold_path()` hints) is out of scope for this PR — and if so, the title and description updated to describe only the soak/security/tooling changes that actually ship.
## Open questions for the human
- Are the `cold_path()` hints intended to return in a follow-up (against main's current aube tree), or are they abandoned? If returning, the 1.95 floor decision comes back with them.Claude Opus | 𝕏
- Correct the actions/checkout@93cb6efe pin comments from the moving major tag "v5" to the exact immutable tag v5.0.1 the SHA resolves to (comment only; no SHA changed). Clears 53 ref-version-mismatch findings. - Suppress impostor-commit and ref-version-mismatch on the dtolnay/rust-toolchain pins; the repo force-pushes its version tags, so pinned SHAs go unreachable and cannot be pinned to a stable tag. Both SHAs verified as genuine upstream commits. - Add persist-credentials: false to the two verify-install.yml checkout steps flagged by artipacked; neither job pushes to the repo. - Suppress dangerous-triggers on trunk-red.yml; it is a report-only workflow_run notifier that never checks out or runs PR-controlled code.
|
Thanks, love this! Merged. Rebased onto
CI green. The soak manager, hardening, and pinned tools are as you built them. |
) * perf(pm): restore cold_path() hints in the vendored aube hot loops nub vendors aube and builds every binary on current stable, so aube's upstream MSRV floor (1.91, held there so mise's distro packaging can embed the crates) does not bind nub. Re-add core::hint::cold_path() on the rare arms of the hot install loops: linker link-fallbacks, resolver semver cache-misses, lockfile subset-parser bails, tarball validation rejects. Behaviorally inert — a codegen hint only. Raise the vendored aube rust-version to 1.95 (documented in Cargo.toml as a deliberate fork delta, preserved on every aube bump) and, because nub-cli depends on aube, the nub root rust-version to 1.95 as well. nub-native already required 1.95, so this only aligns the root floor with what the full build needs. The CI MSRV Check job runs the root + native legs on 1.95.0; nub-phantom (aube-free) keeps its 1.93.0 leg. Refs #442 * fix(msrv): scope the 1.95 floor to nub-cli, keep the workspace at 1.93 Raising the workspace rust-version to 1.95 propagated to nub-phantom-core and nub-phantom-scan (root members), which the aube-free nub-phantom eval tool depends on — so the MSRV Check job's nub-phantom-on-1.93 leg failed with "requires rustc 1.95". Only nub-cli actually pulls in the vendored aube (the sole user of core::hint::cold_path), so set rust-version = "1.95" on nub-cli alone and restore the workspace floor to 1.93. Every other crate keeps its 1.93 floor.
|
Kept stable-only (no nightly): Nothing else here was re-touched — the soak, zizmor hardening, and pinned tooling all stand as merged. |
|
Shipped in v0.6.0: https://github.com/nubjs/nub/releases/tag/v0.6.0 |
…, agent/skills scanners, taze) (#6912) * build: 7-day supply-chain soak + pinned security tooling Ports the nub soak/security stack (nubjs/nub#442) with the wheelhouse shim workarounds baked in: - scripts/soak/: soak window parity gate + fixer (SOAK_DAYS=7 in constants.mts is the single source), soaked dependency updater (taze for npm, rustup cargo for crates), and the external-tools installer (SRI-verified rack + PATH handles + sfw firewall shims). - Soak surfaces: .npmrc min-release-age, tools/pnpm-workspace.yaml minimumReleaseAge (pnpm domain isolated in tools/ so the root stays npm-only), tools/taze.config.mts (imports SOAK_DAYS), .cargo/config.toml min-publish-age (nightly-only; inert on stable), .github/dependabot.yml cooldown per update block (the renovate-check equivalent, reworked for dependabot). - external-tools.json: exact pins + sha512 SRI for pnpm 11.8.0, npm 12, sfw-free/-enterprise 1.13.1, zizmor 1.26.1, agentshield 1.4.0, skillspector @2eb84478. - sfw shims carry the known workarounds: per-command recursion sentinel (not PATH-strip), fail-open when sfw is absent, symlink clobber guard, rack-pinned pnpm/npm resolution. - zizmor workflow (hash-pinned action, gate at high) with a documented starting config: official actions ref-pin, six existing third-party actions grandfathered pending a digest-pin sweep, cache-poisoning (49 Low-confidence findings) disabled, release-packages.yml excessive-permissions scoped-ignored pending a job-level split. - security-audit.yml gains always-run soak-gate, agent-scan (AgentShield over .claude/), and skills-scan (NVIDIA SkillSpector over .claude/skills/, static --no-llm path) jobs. - .claude/skills/soak/SKILL.md documents the workflow. * docs: changelog fragment for #6912 * ci(zizmor): run the SRI-pinned binary instead of the marketplace action The zizmorcore/zizmor-action run hit startup_failure (repo Actions allowlist), and the rack binary is the better shape anyway: one pin source (external-tools.json), and local tools:install audits with the exact bits CI uses. Token-only-when-nonempty works around zizmor treating an empty --gh-token as real and then fatally erroring. * deps(sfw): bump firewall pins to 1.14.0 via dated soakBypass 1.14.0 (published 2026-07-23) fixes two things the shims care about: sfw's own diagnostics now go to stderr (stdout stays transparent for callers capturing `pnpm --version` through a shim), and the child env gains a NO_PROXY loopback exemption (localhost,127.0.0.1,::1) so locally-mocked registries are never proxied. Inside the 7-day window until 2026-07-30, so both pins carry the dated soakBypass annotation; tools:check will demand its removal once the window clears — prune the two annotations then. * deps(tools): bump zizmor 1.28.0, pnpm 11.15.1, npm 12.0.1 — newest soaked releases All three cleared the 7-day window (zizmor 1.28.0 published 07-21, pnpm 11.15.1 07-19, npm 12.0.1 07-10), so no bypass annotations. pnpm 11.16/11.17 and taze 19.16.0 are still soaking; skillspector upstream (2.4/2.5) is entirely inside the window — follow-up bumps once cleared. Gate re-verified: zizmor 1.28.0 with the shipped config reports 0 findings at high across .github/. * docs: fragment says rack-pinned zizmor, not marketplace action * feat(soak): auto-prune expired bypass annotations — fixer + scheduled bot PR The soak gates fail closed by design: the day a soakBypass window clears, tools:check goes red until the two annotation lines come off. Failing closed is right; making a human notice is not. So: - external-tools.mts gains --fix (npm run tools:fix): prunes soakBypass annotations whose removable date has passed, then re-runs the checks. Valid-but-expired only — malformed dates stay findings for a human. - soak-autofix.yml (daily cron + dispatch) runs soak:fix + tools:fix, and when anything changed commits to bot/soak-autofix and opens (or force-updates) a PR. Note in-workflow: PRs opened with the default github.token don't trigger CI; set the optional SOAK_AUTOFIX_TOKEN secret to make the bot PRs run checks like any other. Verified end-to-end with a planted expired annotation: --fix prunes it, check returns green, and the fixer is idempotent (unit-tested). * ci(soak-autofix): bind the artipacked ignore to the checkout line * ci: pin actions/* to latest release-tag SHAs in the new security workflows checkout v7.0.1 (3d3c42e5) + setup-node v7.0.0 (82076278) across soak-autofix / zizmor / security-audit — both releases cleared the 7-day window (07-20 / 07-14). Also splits the PATH export in agent-scan (SC2155). The legacy workflow fleet stays on ref pins per the documented digest-pin sweep in .github/zizmor.yml. * fix(soak): expired annotations warn instead of failing — stale is not unsafe An EXPIRED soakBypass / exclude pin means the version has fully soaked: the bypass no longer bypasses anything and the pin stays SRI-verified. Failing closed on that turned a no-risk cosmetic state into a red required check that flips overnight with zero code change — the exact noise that trains people to admin-bypass (and the model wheelhouse deliberately avoids: informational + auto-drop). Now: expired-but-VALID annotations are warnings (exit 0), surfaced by staleBypasses / staleExcludes and pruned by --fix + the daily soak-autofix workflow. Missing, malformed, or wrong-arithmetic annotations stay hard failures — unauditable IS unsafe. This also defuses the 2026-07-30 expiry of this PR's own sfw annotations. * fix(soak): address review-bot findings across the port - platformKey(): detect musl via the loader heuristic — the -musl pnpm pins were dead keys and a musl host silently installed glibc bits; tools with no -musl pin now fail loud instead. - RUSTUP_CARGO honors CARGO_HOME (custom cargo homes reported the rustup shim as missing). - parseExcludeEntries: tolerate a trailing comment on the minimumReleaseAgeExclude key line — previously the block never opened and every entry beneath escaped validation. - checkCatalogParity: malformed package.json is a Finding, not a crash. - soak-autofix workflow: main-ref guard (dispatch on a topic branch can't force-push the bot branch), concurrency group, and fixer exit status captured + re-raised AFTER the mechanical commit instead of '|| true' masking runtime failures. - sfw shims: fail-open is no longer silent-open — one stderr line when sfw is missing (never on the sentinel re-entry path). - GITHUB_TOKEN on the CI install steps (github.com release fetches). - schematic YYYY-MM-DD example dates in the yaml + skill (the concrete examples were expired copy-paste bait); em-dashes restored in external-tools.json (ensure_ascii artifact). * fix(sfw): export SFW_UNKNOWN_HOST_ACTION=ignore in the shims Wheelhouse lesson: enterprise sfw defaults to BLOCK for non-registry hosts, which breaks ordinary dev flows (API calls, git clones) the day a SOCKET_SECURITY_KEY lands. Free tier hardcodes ignore and disregards the var, so setting it unconditionally is always safe.
…ass and panic-dedup gaps hit compiling a real npm CLI (#7021) * build: 7-day supply-chain soak + pinned security tooling Ports the nub soak/security stack (nubjs/nub#442) with the wheelhouse shim workarounds baked in: - scripts/soak/: soak window parity gate + fixer (SOAK_DAYS=7 in constants.mts is the single source), soaked dependency updater (taze for npm, rustup cargo for crates), and the external-tools installer (SRI-verified rack + PATH handles + sfw firewall shims). - Soak surfaces: .npmrc min-release-age, tools/pnpm-workspace.yaml minimumReleaseAge (pnpm domain isolated in tools/ so the root stays npm-only), tools/taze.config.mts (imports SOAK_DAYS), .cargo/config.toml min-publish-age (nightly-only; inert on stable), .github/dependabot.yml cooldown per update block (the renovate-check equivalent, reworked for dependabot). - external-tools.json: exact pins + sha512 SRI for pnpm 11.8.0, npm 12, sfw-free/-enterprise 1.13.1, zizmor 1.26.1, agentshield 1.4.0, skillspector @2eb84478. - sfw shims carry the known workarounds: per-command recursion sentinel (not PATH-strip), fail-open when sfw is absent, symlink clobber guard, rack-pinned pnpm/npm resolution. - zizmor workflow (hash-pinned action, gate at high) with a documented starting config: official actions ref-pin, six existing third-party actions grandfathered pending a digest-pin sweep, cache-poisoning (49 Low-confidence findings) disabled, release-packages.yml excessive-permissions scoped-ignored pending a job-level split. - security-audit.yml gains always-run soak-gate, agent-scan (AgentShield over .claude/), and skills-scan (NVIDIA SkillSpector over .claude/skills/, static --no-llm path) jobs. - .claude/skills/soak/SKILL.md documents the workflow. * docs: changelog fragment for #6912 * ci(zizmor): run the SRI-pinned binary instead of the marketplace action The zizmorcore/zizmor-action run hit startup_failure (repo Actions allowlist), and the rack binary is the better shape anyway: one pin source (external-tools.json), and local tools:install audits with the exact bits CI uses. Token-only-when-nonempty works around zizmor treating an empty --gh-token as real and then fatally erroring. * deps(sfw): bump firewall pins to 1.14.0 via dated soakBypass 1.14.0 (published 2026-07-23) fixes two things the shims care about: sfw's own diagnostics now go to stderr (stdout stays transparent for callers capturing `pnpm --version` through a shim), and the child env gains a NO_PROXY loopback exemption (localhost,127.0.0.1,::1) so locally-mocked registries are never proxied. Inside the 7-day window until 2026-07-30, so both pins carry the dated soakBypass annotation; tools:check will demand its removal once the window clears — prune the two annotations then. * deps(tools): bump zizmor 1.28.0, pnpm 11.15.1, npm 12.0.1 — newest soaked releases All three cleared the 7-day window (zizmor 1.28.0 published 07-21, pnpm 11.15.1 07-19, npm 12.0.1 07-10), so no bypass annotations. pnpm 11.16/11.17 and taze 19.16.0 are still soaking; skillspector upstream (2.4/2.5) is entirely inside the window — follow-up bumps once cleared. Gate re-verified: zizmor 1.28.0 with the shipped config reports 0 findings at high across .github/. * docs: fragment says rack-pinned zizmor, not marketplace action * feat(soak): auto-prune expired bypass annotations — fixer + scheduled bot PR The soak gates fail closed by design: the day a soakBypass window clears, tools:check goes red until the two annotation lines come off. Failing closed is right; making a human notice is not. So: - external-tools.mts gains --fix (npm run tools:fix): prunes soakBypass annotations whose removable date has passed, then re-runs the checks. Valid-but-expired only — malformed dates stay findings for a human. - soak-autofix.yml (daily cron + dispatch) runs soak:fix + tools:fix, and when anything changed commits to bot/soak-autofix and opens (or force-updates) a PR. Note in-workflow: PRs opened with the default github.token don't trigger CI; set the optional SOAK_AUTOFIX_TOKEN secret to make the bot PRs run checks like any other. Verified end-to-end with a planted expired annotation: --fix prunes it, check returns green, and the fixer is idempotent (unit-tested). * ci(soak-autofix): bind the artipacked ignore to the checkout line * ci: pin actions/* to latest release-tag SHAs in the new security workflows checkout v7.0.1 (3d3c42e5) + setup-node v7.0.0 (82076278) across soak-autofix / zizmor / security-audit — both releases cleared the 7-day window (07-20 / 07-14). Also splits the PATH export in agent-scan (SC2155). The legacy workflow fleet stays on ref pins per the documented digest-pin sweep in .github/zizmor.yml. * fix(soak): expired annotations warn instead of failing — stale is not unsafe An EXPIRED soakBypass / exclude pin means the version has fully soaked: the bypass no longer bypasses anything and the pin stays SRI-verified. Failing closed on that turned a no-risk cosmetic state into a red required check that flips overnight with zero code change — the exact noise that trains people to admin-bypass (and the model wheelhouse deliberately avoids: informational + auto-drop). Now: expired-but-VALID annotations are warnings (exit 0), surfaced by staleBypasses / staleExcludes and pruned by --fix + the daily soak-autofix workflow. Missing, malformed, or wrong-arithmetic annotations stay hard failures — unauditable IS unsafe. This also defuses the 2026-07-30 expiry of this PR's own sfw annotations. * fix(soak): address review-bot findings across the port - platformKey(): detect musl via the loader heuristic — the -musl pnpm pins were dead keys and a musl host silently installed glibc bits; tools with no -musl pin now fail loud instead. - RUSTUP_CARGO honors CARGO_HOME (custom cargo homes reported the rustup shim as missing). - parseExcludeEntries: tolerate a trailing comment on the minimumReleaseAgeExclude key line — previously the block never opened and every entry beneath escaped validation. - checkCatalogParity: malformed package.json is a Finding, not a crash. - soak-autofix workflow: main-ref guard (dispatch on a topic branch can't force-push the bot branch), concurrency group, and fixer exit status captured + re-raised AFTER the mechanical commit instead of '|| true' masking runtime failures. - sfw shims: fail-open is no longer silent-open — one stderr line when sfw is missing (never on the sentinel re-entry path). - GITHUB_TOKEN on the CI install steps (github.com release fetches). - schematic YYYY-MM-DD example dates in the yaml + skill (the concrete examples were expired copy-paste bait); em-dashes restored in external-tools.json (ensure_ascii artifact). * fix(sfw): export SFW_UNKNOWN_HOST_ACTION=ignore in the shims Wheelhouse lesson: enterprise sfw defaults to BLOCK for non-registry hosts, which breaks ordinary dev flows (API calls, git clones) the day a SOCKET_SECURITY_KEY lands. Free tier hardcodes ignore and disregards the var, so setting it unconditionally is always safe. * fix(soak): take review fixes surfaced on the aube twin - checkDockerPrebake: parse the rustup install line's argument list instead of substring-matching the msrv (a multi-toolchain install line false-failed the check). - RUSTUP_CARGO resolves cargo.exe on win32. - soak-autofix: lease-checked force push (fetch the bot branch, then --force-with-lease) so a concurrent actor's commits are never clobbered. * docs(soak): align prose with warn-not-fail; source-cite the unknown-host comment Same drift pullfrog flagged on the nub twin: the skill still said the gates "fail closed when a bypass window clears" — expired-but-valid annotations warn and get pruned by soak:fix / the soak-autofix workflow; invalid annotations are what fail. The shim comment now claims only what the source shows about SFW_UNKNOWN_HOST_ACTION (the enterprise config parses it; inert for free). * fix(soak): never prune a wrong-arithmetic annotation as "cleared" Greptile P1 on the aube twin: the pruners and stale lists accepted any valid-ISO annotation whose removable date had passed — including one whose removable was WRONG (earlier than published + SOAK_DAYS). Such an annotation must surface as the hard check failure it is; treating it as soaked would silently delete a bypass whose real window may still be open. All four surfaces (staleExcludes, fixWorkspaceYaml, staleBypasses, pruneExpiredSoakBypasses) now require the arithmetic to hold before an annotation counts as stale or prunable; regression tests cover the wrong-math-expired case. * fix(soak): downloads fall back to unauthenticated and retry once on 5xx The nub node-18 compat leg died on `download failed 500` — the first authed fetch of a PUBLIC sfw release asset after GITHUB_TOKEN was added to the step env. Whether that 500 was token-induced (an Actions token against a cross-org public asset endpoint) or a transient GitHub blip, one attempt was too brittle: download() now retries without auth when an authed fetch fails (public assets need no credential), and once more after 2s on a 5xx. Regression test pins the fallback dropping the Authorization header. * fix(soak): stop the fixers reformatting files they do not own Adversarial self-review of the renovate/npmrc/yaml fixers, prompted by the 20-line diff my own soak:fix produced on aube's renovate.json: - fixRenovateConfig rewrote the WHOLE file via JSON.parse + re-stringify, collapsing hand-written single-line arrays and reformatting unrelated packageRules (aube's decmpfs musl hold among them). It is now a targeted text edit: only the minimumReleaseAge line changes, every other byte is preserved. A regression test asserts exactly one changed line and that the decmpfs rule survives verbatim. - The insert path produced INVALID JSON for a minimal `{}` config (`{,\n ...}`); guarded and covered by a test. - fixNpmrc / fixWorkspaceYaml matched trailing `\s*$` under /m — `\s` matches newlines, so the replacement swallowed blank lines after the key. Now `[ \t]*$`; verified soak:fix is a no-op on a clean tree. - checkRenovateConfig now also requires `internalChecksFilter: strict`. Without it renovate's default flexible mode raises updates that have NOT cleared minimumReleaseAge — the window silently stops biting. - The no-pinned-asset error names the musl case and lists the pinned platforms: sfw ships no musl asset, so an alpine runner hits this, and the old message gave nothing to act on. Verified alongside: decmpfs stays at 0.1.0 under `cargo update` (the `=0.1.0` requirement holds, so the soak updater cannot smuggle in the musl-breaking 0.1.2), and `--force-with-lease` correctly rejects a concurrent update even when the preceding fetch fails, and still creates the branch on a first run. * fix(soak): stop the fixers reformatting files they do not own Adversarial self-review of the renovate/npmrc/yaml fixers, prompted by the 20-line diff my own soak:fix produced on aube's renovate.json: - fixRenovateConfig rewrote the WHOLE file via JSON.parse + re-stringify, collapsing hand-written single-line arrays and reformatting unrelated packageRules (aube's decmpfs musl hold among them). It is now a targeted text edit: only the minimumReleaseAge line changes, every other byte is preserved. A regression test asserts exactly one changed line and that the decmpfs rule survives verbatim. - The insert path produced INVALID JSON for a minimal `{}` config (`{,\n ...}`); guarded and covered by a test. - fixNpmrc / fixWorkspaceYaml matched trailing `\s*$` under /m — `\s` matches newlines, so the replacement swallowed blank lines after the key. Now `[ \t]*$`; verified soak:fix is a no-op on a clean tree. - checkRenovateConfig now also requires `internalChecksFilter: strict`. Without it renovate's default flexible mode raises updates that have NOT cleared minimumReleaseAge — the window silently stops biting. - The no-pinned-asset error names the musl case and lists the pinned platforms: sfw ships no musl asset, so an alpine runner hits this, and the old message gave nothing to act on. Verified alongside: decmpfs stays at 0.1.0 under `cargo update` (the `=0.1.0` requirement holds, so the soak updater cannot smuggle in the musl-breaking 0.1.2), and `--force-with-lease` correctly rejects a concurrent update even when the preceding fetch fails, and still creates the branch on a first run. * feat(soak): gate npm's min-release-age-exclude entries too Auditing a sibling fleet repo (abitious) for compatibility surfaced an unguarded bypass: npm >= 11.17 has its OWN exclude surface, `min-release-age-exclude[]=<spec>`, parallel to pnpm's `minimumReleaseAgeExclude` block — and the gate validated only the pnpm side. `min-release-age-exclude[]=lodash@1.2.3` was therefore an unvalidated, never-expiring hole in exactly the rule the yaml side enforces. checkNpmrc now applies the same law to .npmrc: bare names and `@scope/*` globs are standing trust (the shape real repos use for trusted scopes, so this is not a churn tax), while a VERSION-PINNED entry needs the `# published: | removable:` annotation with correct arithmetic and real calendar dates. Tests cover trusted-glob, unannotated, correct, wrong-arithmetic, and impossible-date cases. * fix(soak): fail loudly when cargo silently ignores min-publish-age Verified rather than assumed, and the assumption was wrong: cargo treats an [unstable] key it does not implement as a WARNING ("unused config key `unstable.min-publish-age`") and exits 0. Measured on nightly 2026-03-21, which has no such -Z — so `cargo +nightly update` on a merely-OLD nightly resolved every crate with NO window at all while the run reported success. The tooling was claiming a protection it had not applied. updateCargo now captures stderr and treats that warning as a hard failure: the lockfile changes are unsoaked, so say so and exit nonzero with the fix (`rustup update nightly`). The detector is an exported, unit-tested predicate pinning cargo's exact wording. perry rides stable, where the key is expected to be inert, so there the same detection downgrades to an explicit note naming dependabot cooldown as the enforcing surface for cargo deps — no silent no-op either way. * feat(soak): explain a window-blocked cargo re-resolution, refuse the env bypass Re-measured on a current nightly (2026-07-27, cargo 1.99.0-nightly): the `-Z min-publish-age` feature IS implemented there and the window visibly bites — it holds a too-fresh release back ("available: v0.2.189, published 7 days ago"). Both measurements are now recorded in the comment and the skill, since the OLD nightly (2026-03-21) is the evidence that a stale toolchain skips the window silently. Running the real updater surfaced the other half of the contract: the window can make re-resolution IMPOSSIBLE, not just conservative. When a requirement's only candidate is inside the window (aube today: `clap_usage = "^4"`, whose 4.0.0 shipped 3 days ago) cargo fails the whole update — correct behavior, but its own help line advertises `CARGO_RESOLVER_INCOMPATIBLE_PUBLISH_AGE=allow`, a blanket env-var bypass this design deliberately does not have. The updater now detects that failure and prints ordered options (wait it out, repin so a soaked version satisfies the requirement, or adopt the fresh release as a reviewable commit) with an explicit warning against the env bypass. Predicate is exported and unit-tested against cargo's real wording. * fix(soak): take the adversarial-review findings An independent hostile review of the three sibling PRs found real defects, including one where my own test had verified only the safe half of the case: - soak-autofix no longer force-pushes at all. `git fetch origin $BRANCH` UPDATES the remote-tracking ref (actions/checkout leaves the default wildcard refspec), so the following --force-with-lease took its lease against whatever another actor had just pushed and overwrote it — the classic fetch-before-lease anti-pattern, and the inline comment asserting "a concurrent actor's commits are never clobbered" was false. Demonstrated: a human commit onto the open autofix PR was discarded by the next scheduled run. My earlier test only covered the fetch-FAILS path (which is genuinely safe, rejecting with "stale info"). The step now stashes the fixes, bases the work on the existing bot branch when there is one, and plain-pushes: human commits survive by construction, an empty re-run exits 0 instead of pushing a no-op commit, and a genuine conflict fails loudly instead of being resolved by deletion. - fixWorkspaceYaml's prune set must EQUAL staleExcludes' warn set: it was missing the VERSION_PIN_RE guard, so a bare-name / `@scope/*` standing-trust entry sitting under an expired annotation line was deleted by --fix, silently re-arming the soak for a whole scope inside a bot commit advertised as touching only annotation lines. - download() retry semantics split by meaning: 401/403/404 with a token means the credential is the problem (retry unauthenticated), >=500 is transient (retry with the SAME auth). Dropping auth on 5xx made a private asset 404 on the retry, report a bogus "download failed 404", and never be able to succeed. The SRI is verified either way, so no retry can substitute a different artifact. * fix(soak): take the review findings — one is a regression I introduced - fixRenovateConfig still matched a trailing `\s*$`, which under /m eats the NEWLINES after the value: replacing through it silently deleted the blank line that followed. That is the exact defect the same commit fixed in fixNpmrc and fixWorkspaceYaml, kept in the third fixer. Verified with a config carrying a blank line after the key: it disappeared before, survives now. - checkPins now rejects a soakBypass whose `version` is not the version actually pinned. Bump a pin and leave the annotation behind and the ledger vouches for a release that is no longer installed — "1.13.1 was adopted early" while 1.14.0 ships unreviewed. A mismatch is unauditable, so it is a hard finding, not a stale-annotation warning. - soak-autofix.yml's header still described the gates as failing closed on a cleared window; the fourth and last sibling of that stale premise. Expired is a warning, invalid still fails, and the workflow's job is convergence rather than rescue. - Two paths were changed without a test covering them, both added: the multi-arg `rustup toolchain install 1.91.0 1.93.0` case that motivated replacing the substring msrv match (only the negative case was covered), and the `>= 500` retry branch that the retry commit is named for (the existing test exercises only the auth fallback). * fix(compile): survive binary/workspace skew and complete the surfaces a real npm CLI needs Compiling Socket Firewall (sfw — a TLS-MITM proxy CLI with undici, node-forge, iovalkey, zod, … in its graph) end-to-end surfaced four independent blockers. Fixed here: 1. auto-optimize feature skew (driver.rs / freshness.rs): the perry binary's baked-in cross-feature list tracks the branch it was BUILT from, but the auto-optimize cargo build resolves against the checkout on disk. One unknown `perry-runtime/<feat>` failed the whole resolve, and the silent prebuilt fallback linked without the routed ext-pump entrypoints — undefined-js_* errors two stages from the cause. New retain_workspace_declared_features() drops names the checkout's perry-runtime / perry-stdlib don't declare (features table + optional deps, fail-open on unreadable manifests) before the build stamp is computed, and the cargo-failure fallback now says what the consequence and remedy are. 2. perry-ext-zlib zstd surface: undici's web-fetch content decoding references js_zlib_create_zstd_decompress unconditionally, but only perry-stdlib's `compression` module carried the zstd codecs — and routing node:zlib to the ext archive strips that feature. Port the full surface (create factories, sync/async one-shots, streaming write-codec via zstd::stream::write) so the routed archive is self-sufficient. 3. class X extends DOMException (codegen + runtime): undici probes DOMException inheritability at module load (websocketerror.js), and the name was neither in the builtin-parent list nor backed by a subclass initializer — the compiled binary died at startup with 'DOMException is not a function'. Add js_dom_exception_subclass_init (stamps message/name/code onto the subclass instance) wired through both the explicit super() lowering and the implicit-ctor NativeInstanceBase chain walk. 4. panic-runtime dedup for prebuilt (panic=unwind) wrappers co-linked with a panic=abort auto-optimized stdlib (strip_dedup.rs): the name-containment rule never nominated the wrapper's panic_unwind member (stdlib bundles panic_abort under a different name), and the localize pass severed the std-cgu → panic_unwind __rust_drop_panic edge that abort stdlibs cannot re-provide. Nominate panic_unwind in the nosharedeps fixed-point (protected exactly when the stdlib can't cover it), and skip localizing panic symbols a sibling member still references. Allocator shims stay always-localized: leaving the wrapper's system-malloc shim global beats the runtime's mimalloc at link and breaks pointer classification (silent console loss). With these, sfw and sfw-free compile, link, and run as native arm64 binaries straight from their TypeScript entrypoints. * docs: changelog fragment for #7021 * fix(runtime): make the rebound RegExp global constructible via its call form ECMA-262 22.2.4: `RegExp(pattern, flags)` without `new` constructs exactly like `new RegExp`, with the identity shortcut `RegExp(re)` → `re`. The globalThis sentinel fell through to the noop thunk and returned undefined — which is how lodash's module init died: runInContext rebinds the global (`var RegExp = context.RegExp`) and builds `reIsNative` through the call form, so the immediately following `reIsNative.test(...)` threw 'Cannot read properties of undefined'. New regexp_constructor_call_thunk (arity 2) mirrors the dynamic-new RegExp arm in class_registry/construct.rs; without the regex-engine feature it keeps the old noop behavior. Unblocks sfw-registry (lodash via registry/proxy-request.ts). * docs: extend #7021 changelog fragment with the RegExp call-form fix * fix: address stacked stdlib review * fix: address compile stack review * docs: correct compile-gap blocker count --------- Co-authored-by: Ralph Küpper <ralph@skelpo.com>
…s") support (#7028) * build: 7-day supply-chain soak + pinned security tooling Ports the nub soak/security stack (nubjs/nub#442) with the wheelhouse shim workarounds baked in: - scripts/soak/: soak window parity gate + fixer (SOAK_DAYS=7 in constants.mts is the single source), soaked dependency updater (taze for npm, rustup cargo for crates), and the external-tools installer (SRI-verified rack + PATH handles + sfw firewall shims). - Soak surfaces: .npmrc min-release-age, tools/pnpm-workspace.yaml minimumReleaseAge (pnpm domain isolated in tools/ so the root stays npm-only), tools/taze.config.mts (imports SOAK_DAYS), .cargo/config.toml min-publish-age (nightly-only; inert on stable), .github/dependabot.yml cooldown per update block (the renovate-check equivalent, reworked for dependabot). - external-tools.json: exact pins + sha512 SRI for pnpm 11.8.0, npm 12, sfw-free/-enterprise 1.13.1, zizmor 1.26.1, agentshield 1.4.0, skillspector @2eb84478. - sfw shims carry the known workarounds: per-command recursion sentinel (not PATH-strip), fail-open when sfw is absent, symlink clobber guard, rack-pinned pnpm/npm resolution. - zizmor workflow (hash-pinned action, gate at high) with a documented starting config: official actions ref-pin, six existing third-party actions grandfathered pending a digest-pin sweep, cache-poisoning (49 Low-confidence findings) disabled, release-packages.yml excessive-permissions scoped-ignored pending a job-level split. - security-audit.yml gains always-run soak-gate, agent-scan (AgentShield over .claude/), and skills-scan (NVIDIA SkillSpector over .claude/skills/, static --no-llm path) jobs. - .claude/skills/soak/SKILL.md documents the workflow. * docs: changelog fragment for #6912 * ci(zizmor): run the SRI-pinned binary instead of the marketplace action The zizmorcore/zizmor-action run hit startup_failure (repo Actions allowlist), and the rack binary is the better shape anyway: one pin source (external-tools.json), and local tools:install audits with the exact bits CI uses. Token-only-when-nonempty works around zizmor treating an empty --gh-token as real and then fatally erroring. * deps(sfw): bump firewall pins to 1.14.0 via dated soakBypass 1.14.0 (published 2026-07-23) fixes two things the shims care about: sfw's own diagnostics now go to stderr (stdout stays transparent for callers capturing `pnpm --version` through a shim), and the child env gains a NO_PROXY loopback exemption (localhost,127.0.0.1,::1) so locally-mocked registries are never proxied. Inside the 7-day window until 2026-07-30, so both pins carry the dated soakBypass annotation; tools:check will demand its removal once the window clears — prune the two annotations then. * deps(tools): bump zizmor 1.28.0, pnpm 11.15.1, npm 12.0.1 — newest soaked releases All three cleared the 7-day window (zizmor 1.28.0 published 07-21, pnpm 11.15.1 07-19, npm 12.0.1 07-10), so no bypass annotations. pnpm 11.16/11.17 and taze 19.16.0 are still soaking; skillspector upstream (2.4/2.5) is entirely inside the window — follow-up bumps once cleared. Gate re-verified: zizmor 1.28.0 with the shipped config reports 0 findings at high across .github/. * docs: fragment says rack-pinned zizmor, not marketplace action * feat(soak): auto-prune expired bypass annotations — fixer + scheduled bot PR The soak gates fail closed by design: the day a soakBypass window clears, tools:check goes red until the two annotation lines come off. Failing closed is right; making a human notice is not. So: - external-tools.mts gains --fix (npm run tools:fix): prunes soakBypass annotations whose removable date has passed, then re-runs the checks. Valid-but-expired only — malformed dates stay findings for a human. - soak-autofix.yml (daily cron + dispatch) runs soak:fix + tools:fix, and when anything changed commits to bot/soak-autofix and opens (or force-updates) a PR. Note in-workflow: PRs opened with the default github.token don't trigger CI; set the optional SOAK_AUTOFIX_TOKEN secret to make the bot PRs run checks like any other. Verified end-to-end with a planted expired annotation: --fix prunes it, check returns green, and the fixer is idempotent (unit-tested). * ci(soak-autofix): bind the artipacked ignore to the checkout line * ci: pin actions/* to latest release-tag SHAs in the new security workflows checkout v7.0.1 (3d3c42e5) + setup-node v7.0.0 (82076278) across soak-autofix / zizmor / security-audit — both releases cleared the 7-day window (07-20 / 07-14). Also splits the PATH export in agent-scan (SC2155). The legacy workflow fleet stays on ref pins per the documented digest-pin sweep in .github/zizmor.yml. * fix(soak): expired annotations warn instead of failing — stale is not unsafe An EXPIRED soakBypass / exclude pin means the version has fully soaked: the bypass no longer bypasses anything and the pin stays SRI-verified. Failing closed on that turned a no-risk cosmetic state into a red required check that flips overnight with zero code change — the exact noise that trains people to admin-bypass (and the model wheelhouse deliberately avoids: informational + auto-drop). Now: expired-but-VALID annotations are warnings (exit 0), surfaced by staleBypasses / staleExcludes and pruned by --fix + the daily soak-autofix workflow. Missing, malformed, or wrong-arithmetic annotations stay hard failures — unauditable IS unsafe. This also defuses the 2026-07-30 expiry of this PR's own sfw annotations. * fix(soak): address review-bot findings across the port - platformKey(): detect musl via the loader heuristic — the -musl pnpm pins were dead keys and a musl host silently installed glibc bits; tools with no -musl pin now fail loud instead. - RUSTUP_CARGO honors CARGO_HOME (custom cargo homes reported the rustup shim as missing). - parseExcludeEntries: tolerate a trailing comment on the minimumReleaseAgeExclude key line — previously the block never opened and every entry beneath escaped validation. - checkCatalogParity: malformed package.json is a Finding, not a crash. - soak-autofix workflow: main-ref guard (dispatch on a topic branch can't force-push the bot branch), concurrency group, and fixer exit status captured + re-raised AFTER the mechanical commit instead of '|| true' masking runtime failures. - sfw shims: fail-open is no longer silent-open — one stderr line when sfw is missing (never on the sentinel re-entry path). - GITHUB_TOKEN on the CI install steps (github.com release fetches). - schematic YYYY-MM-DD example dates in the yaml + skill (the concrete examples were expired copy-paste bait); em-dashes restored in external-tools.json (ensure_ascii artifact). * fix(sfw): export SFW_UNKNOWN_HOST_ACTION=ignore in the shims Wheelhouse lesson: enterprise sfw defaults to BLOCK for non-registry hosts, which breaks ordinary dev flows (API calls, git clones) the day a SOCKET_SECURITY_KEY lands. Free tier hardcodes ignore and disregards the var, so setting it unconditionally is always safe. * fix(soak): take review fixes surfaced on the aube twin - checkDockerPrebake: parse the rustup install line's argument list instead of substring-matching the msrv (a multi-toolchain install line false-failed the check). - RUSTUP_CARGO resolves cargo.exe on win32. - soak-autofix: lease-checked force push (fetch the bot branch, then --force-with-lease) so a concurrent actor's commits are never clobbered. * docs(soak): align prose with warn-not-fail; source-cite the unknown-host comment Same drift pullfrog flagged on the nub twin: the skill still said the gates "fail closed when a bypass window clears" — expired-but-valid annotations warn and get pruned by soak:fix / the soak-autofix workflow; invalid annotations are what fail. The shim comment now claims only what the source shows about SFW_UNKNOWN_HOST_ACTION (the enterprise config parses it; inert for free). * fix(soak): never prune a wrong-arithmetic annotation as "cleared" Greptile P1 on the aube twin: the pruners and stale lists accepted any valid-ISO annotation whose removable date had passed — including one whose removable was WRONG (earlier than published + SOAK_DAYS). Such an annotation must surface as the hard check failure it is; treating it as soaked would silently delete a bypass whose real window may still be open. All four surfaces (staleExcludes, fixWorkspaceYaml, staleBypasses, pruneExpiredSoakBypasses) now require the arithmetic to hold before an annotation counts as stale or prunable; regression tests cover the wrong-math-expired case. * fix(soak): downloads fall back to unauthenticated and retry once on 5xx The nub node-18 compat leg died on `download failed 500` — the first authed fetch of a PUBLIC sfw release asset after GITHUB_TOKEN was added to the step env. Whether that 500 was token-induced (an Actions token against a cross-org public asset endpoint) or a transient GitHub blip, one attempt was too brittle: download() now retries without auth when an authed fetch fails (public assets need no credential), and once more after 2s on a 5xx. Regression test pins the fallback dropping the Authorization header. * fix(soak): stop the fixers reformatting files they do not own Adversarial self-review of the renovate/npmrc/yaml fixers, prompted by the 20-line diff my own soak:fix produced on aube's renovate.json: - fixRenovateConfig rewrote the WHOLE file via JSON.parse + re-stringify, collapsing hand-written single-line arrays and reformatting unrelated packageRules (aube's decmpfs musl hold among them). It is now a targeted text edit: only the minimumReleaseAge line changes, every other byte is preserved. A regression test asserts exactly one changed line and that the decmpfs rule survives verbatim. - The insert path produced INVALID JSON for a minimal `{}` config (`{,\n ...}`); guarded and covered by a test. - fixNpmrc / fixWorkspaceYaml matched trailing `\s*$` under /m — `\s` matches newlines, so the replacement swallowed blank lines after the key. Now `[ \t]*$`; verified soak:fix is a no-op on a clean tree. - checkRenovateConfig now also requires `internalChecksFilter: strict`. Without it renovate's default flexible mode raises updates that have NOT cleared minimumReleaseAge — the window silently stops biting. - The no-pinned-asset error names the musl case and lists the pinned platforms: sfw ships no musl asset, so an alpine runner hits this, and the old message gave nothing to act on. Verified alongside: decmpfs stays at 0.1.0 under `cargo update` (the `=0.1.0` requirement holds, so the soak updater cannot smuggle in the musl-breaking 0.1.2), and `--force-with-lease` correctly rejects a concurrent update even when the preceding fetch fails, and still creates the branch on a first run. * fix(soak): stop the fixers reformatting files they do not own Adversarial self-review of the renovate/npmrc/yaml fixers, prompted by the 20-line diff my own soak:fix produced on aube's renovate.json: - fixRenovateConfig rewrote the WHOLE file via JSON.parse + re-stringify, collapsing hand-written single-line arrays and reformatting unrelated packageRules (aube's decmpfs musl hold among them). It is now a targeted text edit: only the minimumReleaseAge line changes, every other byte is preserved. A regression test asserts exactly one changed line and that the decmpfs rule survives verbatim. - The insert path produced INVALID JSON for a minimal `{}` config (`{,\n ...}`); guarded and covered by a test. - fixNpmrc / fixWorkspaceYaml matched trailing `\s*$` under /m — `\s` matches newlines, so the replacement swallowed blank lines after the key. Now `[ \t]*$`; verified soak:fix is a no-op on a clean tree. - checkRenovateConfig now also requires `internalChecksFilter: strict`. Without it renovate's default flexible mode raises updates that have NOT cleared minimumReleaseAge — the window silently stops biting. - The no-pinned-asset error names the musl case and lists the pinned platforms: sfw ships no musl asset, so an alpine runner hits this, and the old message gave nothing to act on. Verified alongside: decmpfs stays at 0.1.0 under `cargo update` (the `=0.1.0` requirement holds, so the soak updater cannot smuggle in the musl-breaking 0.1.2), and `--force-with-lease` correctly rejects a concurrent update even when the preceding fetch fails, and still creates the branch on a first run. * feat(soak): gate npm's min-release-age-exclude entries too Auditing a sibling fleet repo (abitious) for compatibility surfaced an unguarded bypass: npm >= 11.17 has its OWN exclude surface, `min-release-age-exclude[]=<spec>`, parallel to pnpm's `minimumReleaseAgeExclude` block — and the gate validated only the pnpm side. `min-release-age-exclude[]=lodash@1.2.3` was therefore an unvalidated, never-expiring hole in exactly the rule the yaml side enforces. checkNpmrc now applies the same law to .npmrc: bare names and `@scope/*` globs are standing trust (the shape real repos use for trusted scopes, so this is not a churn tax), while a VERSION-PINNED entry needs the `# published: | removable:` annotation with correct arithmetic and real calendar dates. Tests cover trusted-glob, unannotated, correct, wrong-arithmetic, and impossible-date cases. * fix(soak): fail loudly when cargo silently ignores min-publish-age Verified rather than assumed, and the assumption was wrong: cargo treats an [unstable] key it does not implement as a WARNING ("unused config key `unstable.min-publish-age`") and exits 0. Measured on nightly 2026-03-21, which has no such -Z — so `cargo +nightly update` on a merely-OLD nightly resolved every crate with NO window at all while the run reported success. The tooling was claiming a protection it had not applied. updateCargo now captures stderr and treats that warning as a hard failure: the lockfile changes are unsoaked, so say so and exit nonzero with the fix (`rustup update nightly`). The detector is an exported, unit-tested predicate pinning cargo's exact wording. perry rides stable, where the key is expected to be inert, so there the same detection downgrades to an explicit note naming dependabot cooldown as the enforcing surface for cargo deps — no silent no-op either way. * feat(soak): explain a window-blocked cargo re-resolution, refuse the env bypass Re-measured on a current nightly (2026-07-27, cargo 1.99.0-nightly): the `-Z min-publish-age` feature IS implemented there and the window visibly bites — it holds a too-fresh release back ("available: v0.2.189, published 7 days ago"). Both measurements are now recorded in the comment and the skill, since the OLD nightly (2026-03-21) is the evidence that a stale toolchain skips the window silently. Running the real updater surfaced the other half of the contract: the window can make re-resolution IMPOSSIBLE, not just conservative. When a requirement's only candidate is inside the window (aube today: `clap_usage = "^4"`, whose 4.0.0 shipped 3 days ago) cargo fails the whole update — correct behavior, but its own help line advertises `CARGO_RESOLVER_INCOMPATIBLE_PUBLISH_AGE=allow`, a blanket env-var bypass this design deliberately does not have. The updater now detects that failure and prints ordered options (wait it out, repin so a soaked version satisfies the requirement, or adopt the fresh release as a reviewable commit) with an explicit warning against the env bypass. Predicate is exported and unit-tested against cargo's real wording. * fix(soak): take the adversarial-review findings An independent hostile review of the three sibling PRs found real defects, including one where my own test had verified only the safe half of the case: - soak-autofix no longer force-pushes at all. `git fetch origin $BRANCH` UPDATES the remote-tracking ref (actions/checkout leaves the default wildcard refspec), so the following --force-with-lease took its lease against whatever another actor had just pushed and overwrote it — the classic fetch-before-lease anti-pattern, and the inline comment asserting "a concurrent actor's commits are never clobbered" was false. Demonstrated: a human commit onto the open autofix PR was discarded by the next scheduled run. My earlier test only covered the fetch-FAILS path (which is genuinely safe, rejecting with "stale info"). The step now stashes the fixes, bases the work on the existing bot branch when there is one, and plain-pushes: human commits survive by construction, an empty re-run exits 0 instead of pushing a no-op commit, and a genuine conflict fails loudly instead of being resolved by deletion. - fixWorkspaceYaml's prune set must EQUAL staleExcludes' warn set: it was missing the VERSION_PIN_RE guard, so a bare-name / `@scope/*` standing-trust entry sitting under an expired annotation line was deleted by --fix, silently re-arming the soak for a whole scope inside a bot commit advertised as touching only annotation lines. - download() retry semantics split by meaning: 401/403/404 with a token means the credential is the problem (retry unauthenticated), >=500 is transient (retry with the SAME auth). Dropping auth on 5xx made a private asset 404 on the retry, report a bogus "download failed 404", and never be able to succeed. The SRI is verified either way, so no retry can substitute a different artifact. * fix(soak): take the review findings — one is a regression I introduced - fixRenovateConfig still matched a trailing `\s*$`, which under /m eats the NEWLINES after the value: replacing through it silently deleted the blank line that followed. That is the exact defect the same commit fixed in fixNpmrc and fixWorkspaceYaml, kept in the third fixer. Verified with a config carrying a blank line after the key: it disappeared before, survives now. - checkPins now rejects a soakBypass whose `version` is not the version actually pinned. Bump a pin and leave the annotation behind and the ledger vouches for a release that is no longer installed — "1.13.1 was adopted early" while 1.14.0 ships unreviewed. A mismatch is unauditable, so it is a hard finding, not a stale-annotation warning. - soak-autofix.yml's header still described the gates as failing closed on a cleared window; the fourth and last sibling of that stale premise. Expired is a warning, invalid still fails, and the workflow's job is convergence rather than rescue. - Two paths were changed without a test covering them, both added: the multi-arg `rustup toolchain install 1.91.0 1.93.0` case that motivated replacing the substring msrv match (only the negative case was covered), and the `>= 500` retry branch that the retry commit is named for (the existing test exercises only the auth fallback). * fix(compile): survive binary/workspace skew and complete the surfaces a real npm CLI needs Compiling Socket Firewall (sfw — a TLS-MITM proxy CLI with undici, node-forge, iovalkey, zod, … in its graph) end-to-end surfaced four independent blockers. Fixed here: 1. auto-optimize feature skew (driver.rs / freshness.rs): the perry binary's baked-in cross-feature list tracks the branch it was BUILT from, but the auto-optimize cargo build resolves against the checkout on disk. One unknown `perry-runtime/<feat>` failed the whole resolve, and the silent prebuilt fallback linked without the routed ext-pump entrypoints — undefined-js_* errors two stages from the cause. New retain_workspace_declared_features() drops names the checkout's perry-runtime / perry-stdlib don't declare (features table + optional deps, fail-open on unreadable manifests) before the build stamp is computed, and the cargo-failure fallback now says what the consequence and remedy are. 2. perry-ext-zlib zstd surface: undici's web-fetch content decoding references js_zlib_create_zstd_decompress unconditionally, but only perry-stdlib's `compression` module carried the zstd codecs — and routing node:zlib to the ext archive strips that feature. Port the full surface (create factories, sync/async one-shots, streaming write-codec via zstd::stream::write) so the routed archive is self-sufficient. 3. class X extends DOMException (codegen + runtime): undici probes DOMException inheritability at module load (websocketerror.js), and the name was neither in the builtin-parent list nor backed by a subclass initializer — the compiled binary died at startup with 'DOMException is not a function'. Add js_dom_exception_subclass_init (stamps message/name/code onto the subclass instance) wired through both the explicit super() lowering and the implicit-ctor NativeInstanceBase chain walk. 4. panic-runtime dedup for prebuilt (panic=unwind) wrappers co-linked with a panic=abort auto-optimized stdlib (strip_dedup.rs): the name-containment rule never nominated the wrapper's panic_unwind member (stdlib bundles panic_abort under a different name), and the localize pass severed the std-cgu → panic_unwind __rust_drop_panic edge that abort stdlibs cannot re-provide. Nominate panic_unwind in the nosharedeps fixed-point (protected exactly when the stdlib can't cover it), and skip localizing panic symbols a sibling member still references. Allocator shims stay always-localized: leaving the wrapper's system-malloc shim global beats the runtime's mimalloc at link and breaks pointer classification (silent console loss). With these, sfw and sfw-free compile, link, and run as native arm64 binaries straight from their TypeScript entrypoints. * docs: changelog fragment for #7021 * feat(resolve): full Node.js '#' subpath-imports (package.json "imports") support Replace the happy-path '#' handling from #5039 with a spec-complete PACKAGE_IMPORTS_RESOLVE implementation (resolve/subpath_imports.rs): - package scope walk to the nearest package.json with an "imports" object, stopping at node_modules boundaries - exact keys; '*' wildcard patterns with Node's best-match rule (longest prefix, patternKeyCompare tie-break) - string / fallback-array / conditional-object targets; conditions matched in the exports resolver's priority order (perry, node, import, module, default, require - node above default) - bare-package targets re-enter node_modules resolution ('node:' builtins included) - spec rejections with descriptive errors: '#', '#/...', trailing '/', and targets or wildcard captures traversing '..'/node_modules or escaping the package directory - perry's TS-first extension probing, so "#lib/*": "./src/lib/*" resolves #lib/foo to src/lib/foo.ts Wired before the tsconfig-paths fallback in resolve_import (spec resolution outranks aliasing; falls through when no imports map governs the importer), and into check --check-deps so '#' imports stop producing false R003 "not found in node_modules" errors. * docs: key the changelog fragment to PR #7028 * fix: address stacked stdlib review * fix: address subpath imports review --------- Co-authored-by: Ralph Küpper <ralph@skelpo.com>

What this PR does
Gives nub one supply-chain safety rule — a 7-day "soak" — applied everywhere a dependency or tool can enter the repo: when a new version of anything is published, we wait 7 days before adopting it, so a compromised release gets caught by the ecosystem before we ever install it. Also brings workflow-security hardening (zizmor, 267 findings to zero), pinned-by-checksum external tooling, Socket Firewall in CI, and
core::hint::cold_path()branch hints in the vendored install engine.The soak, explained
"Soak time" = the minimum age a release must reach before we install it. Ours is 7 days, defined once in
scripts/soak/constants.mts(SOAK_DAYS = 7); every config file below derives from or is checked against it:rust-toolchain.toml.cargo/config.toml-Zmin-publish-ageskips crate versions younger than 7 daystools/pnpm-workspace.yamlminimumReleaseAge: 10080(same rule, in minutes).npmrcmin-release-age=7for npm ≥ 11.17tools/taze.config.mts.github/renovate.jsonminimumReleaseAge: "7 days", explicit — Renovate commits updated lockfiles, and-Zmin-publish-ageskips already-locked versions, so Renovate itself is the only place this path can be soakedscripts/soak/soak.mts --checkfails CI if any of those drift;--fixrewrites them. The pnpm soak surfaces live intools/(not the repo root): a rootpnpm-workspace.yamlmarks the repo root as a workspace root, and nub's own workspace detection then redirects the node-version test matrix through the rootengines— the exact false-green the matrix exists to catch. The root stays npm-only; a tracked fixture anchor plus a harness guard keep the matrix honest as defense-in-depth. Skipping the soak for one package requires a dated# published: YYYY-MM-DD | removable: YYYY-MM-DDcomment above the exclude pin (removable = published + 7d), and the gate fails once the window passes — soaked pins must come off. taze itself is exact-pinned in package.json (npm can't parse thecatalog:protocol and wpt-worker runs a rootnpm ci); the workspace catalog entry is the reference, and the gate fails if the two versions drift.A repo skill (
.claude/skills/soak/SKILL.md) documents the procedures — change the window, opt out, add an exclusion, bump the nightly — so agent sessions don't re-derive them; it follows Anthropic's skill-authoring + prompting guidance and links it for future edits.Workflow security: 267 zizmor findings to zero
zizmor (a GitHub Actions security linter) is now a CI gate (
zizmor.yml), so all 21 existing workflows were brought to zero findings first:persist-credentials: falseon every checkout that doesn't push (the homebrew-tap bump keeps its credentialed checkout, with an inline ignore explaining why).permissions:blocks — workflow-level{}plus per-job scopes matching only what each job's steps actually do.release.ymlalone) moved to env-var indirection:${{ matrix.foo }}inside a shell script becomes a step-levelenv:var referenced as"$FOO", so a crafted value can't execute as shell.# zizmor: ignore[rule] reasoncomments (e.g. caches on gate-only jobs that never feed published artifacts).No behavior changed — each job kept every permission its steps use.
Pinned tools, Socket Firewall, and the docker context
external-tools.jsonpins pnpm 11.8.0, npm 12.0.0, sfw, zizmor, agentshield, and skillspector by exact version + sha512 SRI checksum;scripts/soak/external-tools.mtsis the only install path (download → verify → rack → PATH symlink). npm-packaged tools get their deps installed by nub itself first (dogfooding), prod-only with scripts skipped.CI installs the pinned pnpm + npm, then writes firewall shims: bash wrappers named
npm/pnpm/cargo/… that route the real command throughsfw, wrapping the pinned binaries directly.SOCKET_SECURITY_KEYis the only credential env var — not set yet, which is fine: sfw's free tier is keyless, and the enterprise tier engages automatically when the secret lands.untracked1.6.4 (pinned via the same catalog rule) maintains a generated section in.dockerignoreto keep docker build contexts small; apackage.jsonwhitelist rescues the load-bearing.cargoconfigs (crates/nub-native/.cargoroutes in-container build output to the shared target dir — the docker-smoke builders depend on it).Toolchain + cold-path hints
rust-toolchain.tomlpinsnightly-2026-07-04(newest nightly ≥ 7 days old at adoption; nightly is what activates-Zmin-publish-age), withrust-versionmoving 1.88 → 1.95 as the stable floor.core::hint::cold_path()(stable since 1.95) marks rare branches in the vendored install engine's hot loops — the per-file link fallbacks, lockfile-parser bails, semver cache misses, and tar validation errors — so the optimizer keeps the common path tight. Hints only; no behavior change.Testing + review trail
node:testunit tests cover the soak checkers, fixers, parsers, and CLIs (pnpm run test:scripts, also in the CI gate). Review findings live on as regression cases (impossible calendar dates, expired bypasses, flow-style exclude lists, catalog drift, the--npm --cargoselector bug), and the security-relevant paths are pinned by tests: the downloader sendsGITHUB_TOKENto github.com only and rejects any checksum mismatch, the sfw flavor flips onSOCKET_SECURITY_KEY, and both CLIs round-trip end-to-end through their entrypoints.cargo testgreen. The two registry-mock test failures first blamed on main turned out to be the Socket Firewall shim MITM-proxying cargo's local-registry mocks; the Test job now setsSFW_SHIM_ACTIVE_CARGO=1so the shim execs the real cargo for the mock-registry suites while staying active for everything else.--deletethat could empty a vendor tree on a failed staging step; a cache-wipe recipe that now fails loudly on an empty path substitution).Hardening rounds after opening (CI + review driven)
tarparsesC:\…as a remote host (extract cwd-relative); Git Bash's GNU tar shadows System32 bsdtar and can't read zip (invoke%SystemRoot%\System32\tar.exe); a copied handle strands pnpm's SEA launcher from itsdist/siblings (handles now forward to the absolute rack target via.cmd+ bash shims); and the forwarder must strip.exefrom the handle base — pwsh resolvespnpmtopnpm.exefirst, and a bash script wearing that name is "not a valid application for this OS platform"..github/(the vendored aube workflows are inert); 24dtolnay/rust-toolchainsites repinned — the old SHA was genuine but left dangling by an upstream force-push (impostor-commit finding); docker action pins carry exact version comments (ref-version-mismatch).scripts/soak/remotes.mtsdeclares where PRs live vs where branches push (--check/--fix/--print), so tooling reads it instead of rediscovering it.