From 9c87030edf88f5a5bfbff58ade979f49e7c86b8b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jul 2026 20:15:31 +0000 Subject: [PATCH 1/9] chore(deps)(deps): bump @ladybugdb/core in /gitnexus Bumps [@ladybugdb/core](https://github.com/LadybugDB/ladybug) from 0.18.1 to 0.18.2. - [Release notes](https://github.com/LadybugDB/ladybug/releases) - [Commits](https://github.com/LadybugDB/ladybug/compare/v0.18.1...v0.18.2) --- updated-dependencies: - dependency-name: "@ladybugdb/core" dependency-version: 0.18.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- gitnexus/package-lock.json | 46 +++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/gitnexus/package-lock.json b/gitnexus/package-lock.json index 9aa2ef8da..fe1643b96 100644 --- a/gitnexus/package-lock.json +++ b/gitnexus/package-lock.json @@ -1254,9 +1254,9 @@ } }, "node_modules/@ladybugdb/core": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/@ladybugdb/core/-/core-0.18.1.tgz", - "integrity": "sha512-0c1kXDpdv7z/GB0oyFYnLEjLsXFwPHz1YD4wxtrk9hav8zJX5T1PHQMr+XRfdDI1NQjx4iNdbPQGGT7Bx/X2aw==", + "version": "0.18.2", + "resolved": "https://registry.npmjs.org/@ladybugdb/core/-/core-0.18.2.tgz", + "integrity": "sha512-222FjGciEO5Z+/MRQGU+b4IaGAjOgSQzj7fMpOuhMQN4F8nf654kuKRk1iybSiNy6XSw69hIJ0mKwUeBQ8y6Fg==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -1265,17 +1265,17 @@ "node-addon-api": "^6.0.0" }, "optionalDependencies": { - "@ladybugdb/core-darwin-arm64": "0.18.1", - "@ladybugdb/core-darwin-x64": "0.18.1", - "@ladybugdb/core-linux-arm64": "0.18.1", - "@ladybugdb/core-linux-x64": "0.18.1", - "@ladybugdb/core-win32-x64": "0.18.1" + "@ladybugdb/core-darwin-arm64": "0.18.2", + "@ladybugdb/core-darwin-x64": "0.18.2", + "@ladybugdb/core-linux-arm64": "0.18.2", + "@ladybugdb/core-linux-x64": "0.18.2", + "@ladybugdb/core-win32-x64": "0.18.2" } }, "node_modules/@ladybugdb/core-darwin-arm64": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/@ladybugdb/core-darwin-arm64/-/core-darwin-arm64-0.18.1.tgz", - "integrity": "sha512-M5YZuAONRAv3awkr+cfaibn9Da+3pgDzRiek/JabWQuz48xgzW3Vh9yQH4s8Dq/bfQo6YTsaLIBRcUCCUzCtcg==", + "version": "0.18.2", + "resolved": "https://registry.npmjs.org/@ladybugdb/core-darwin-arm64/-/core-darwin-arm64-0.18.2.tgz", + "integrity": "sha512-gAwxsdijBFTz4aZ9ITG6zdQw3lAki0eY33hNBLCfKXjKJLvW/8wCvgCVBglqfqBF5WyI7icFUt+wfy/Fbdfl5A==", "cpu": [ "arm64" ], @@ -1286,9 +1286,9 @@ ] }, "node_modules/@ladybugdb/core-darwin-x64": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/@ladybugdb/core-darwin-x64/-/core-darwin-x64-0.18.1.tgz", - "integrity": "sha512-kq+pyTskfCx++Mrbk7QssE/f/CpSuU50T8lhRtv4PaOKhC2Jf8/wAUOA17UxI594wAru3ERpqVBFUBWGcPk2ag==", + "version": "0.18.2", + "resolved": "https://registry.npmjs.org/@ladybugdb/core-darwin-x64/-/core-darwin-x64-0.18.2.tgz", + "integrity": "sha512-oUjYLc1fW3ntCrO9te55PoPfvhFo8AKeNa/sU66fiQyEAZB7qZJxeHnnLgl/bLueTF2os3RSawq46ZftoD/9Eg==", "cpu": [ "x64" ], @@ -1299,9 +1299,9 @@ ] }, "node_modules/@ladybugdb/core-linux-arm64": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/@ladybugdb/core-linux-arm64/-/core-linux-arm64-0.18.1.tgz", - "integrity": "sha512-fu7ke1haa5rPINcQn0+kxQijZ0A8ZDWP9e+X8xcDH94RagDbPWwG8yFC890cGSdc/j7mTV+xkA/y/kVHpmVI6w==", + "version": "0.18.2", + "resolved": "https://registry.npmjs.org/@ladybugdb/core-linux-arm64/-/core-linux-arm64-0.18.2.tgz", + "integrity": "sha512-UppokeTaPl9pN0xOsdMa+hmM68zbN2eKReTZhZNYM16qX0d2OlgbS/NlXi09Wdot+w5qvlZ9Q0iCCPfr7qvPaw==", "cpu": [ "arm64" ], @@ -1312,9 +1312,9 @@ ] }, "node_modules/@ladybugdb/core-linux-x64": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/@ladybugdb/core-linux-x64/-/core-linux-x64-0.18.1.tgz", - "integrity": "sha512-qp5HilHzDGuArfOyD+VyA7lVJ7IwQDKd81NZKKTmUwIAOJtdwqniYx6JZICPnlr36zFJBx/lGYoSsEzbC+TVdw==", + "version": "0.18.2", + "resolved": "https://registry.npmjs.org/@ladybugdb/core-linux-x64/-/core-linux-x64-0.18.2.tgz", + "integrity": "sha512-GypOxCnP2ix/FWM8YhQ41aQYlS+ruoNMJp7pmaF5laJHhL/a+P/apywNTE+9N41Walsl+Emgg9xwxwTC93slow==", "cpu": [ "x64" ], @@ -1325,9 +1325,9 @@ ] }, "node_modules/@ladybugdb/core-win32-x64": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/@ladybugdb/core-win32-x64/-/core-win32-x64-0.18.1.tgz", - "integrity": "sha512-vHcXr7Df2X1dbb5ORK+SBmNstd/3tApGFImbAnaWiTuLDFlAdfY8lbiSBSp3OgFjc0BB7F3GYUUdvgDRJjK3zA==", + "version": "0.18.2", + "resolved": "https://registry.npmjs.org/@ladybugdb/core-win32-x64/-/core-win32-x64-0.18.2.tgz", + "integrity": "sha512-hvFwjhTYdwG2sijapx963a27jP9mlLW0ZFv5Yfj19e0B3T/FqD9CaKULPy23mU2XlkISN2LUkY5qdgvCFztl/g==", "cpu": [ "x64" ], From b751418985169170f5d1c41f751502545989326a Mon Sep 17 00:00:00 2001 From: Gergo Magyar Date: Tue, 21 Jul 2026 06:06:42 +0000 Subject: [PATCH 2/9] fix(test): discover the installed FTS extension version dir instead of assuming it equals lbug.VERSION resolveInstalledFtsExtension (extension-binary-real.test.ts) and resolveSeedExtension (fts-extension-e2e.test.ts) both hardcoded the on-disk FTS extension path as .lbdb/extension//..., but LadybugDB's native INSTALL/LOAD resolves its own extension-ABI version directory, which does not always track the npm package version. Bumping @ladybugdb/core from 0.18.1 to 0.18.2 in this PR still installs into a 0.18.1 directory, so both hardcoded lookups came up empty and failed hard under GITNEXUS_REQUIRE_FTS=1 in CI (all platforms, shard 3). Add findInstalledFtsExtension() to discover the real installed file by scanning every version subdirectory, and use it from both test files. Co-Authored-By: Claude Sonnet 5 --- gitnexus/test/helpers/fts-availability.ts | 36 +++++++++++++++++++ .../integration/extension-binary-real.test.ts | 20 +++-------- .../integration/fts-extension-e2e.test.ts | 35 ++++++++---------- 3 files changed, 55 insertions(+), 36 deletions(-) diff --git a/gitnexus/test/helpers/fts-availability.ts b/gitnexus/test/helpers/fts-availability.ts index 28d8eb7dd..bc3620662 100644 --- a/gitnexus/test/helpers/fts-availability.ts +++ b/gitnexus/test/helpers/fts-availability.ts @@ -1,3 +1,39 @@ +import { existsSync, readdirSync, statSync } from 'node:fs'; +import { join } from 'node:path'; + +/** A valid `libfts.lbug_extension` is ~2.2MB; anything smaller is truncated/corrupt. */ +const MIN_VALID_FTS_EXTENSION_BYTES = 1024 * 1024; + +/** + * Find the installed FTS extension file under a `.lbdb/extension` root, + * discovering the version directory instead of assuming it equals the npm + * `@ladybugdb/core` package version. LadybugDB's native INSTALL/LOAD resolves + * its own extension-ABI version directory, which does not always track the + * npm package version — e.g. #2587: bumping the package from 0.18.1 to 0.18.2 + * still installs into a `0.18.1` directory, because the underlying + * extension-ABI build did not change with that patch release. + * + * Scans every version subdirectory for a `/fts/libfts.lbug_extension` + * file and returns the most recently modified one (the one an install/load + * actually just resolved), or null when nothing is installed. + */ +export const findInstalledFtsExtension = (extensionRoot: string): string | null => { + if (!existsSync(extensionRoot)) return null; + let best: { path: string; mtimeMs: number } | null = null; + for (const versionEntry of readdirSync(extensionRoot)) { + const versionDir = join(extensionRoot, versionEntry); + if (!statSync(versionDir).isDirectory()) continue; + for (const platformEntry of readdirSync(versionDir)) { + const candidate = join(versionDir, platformEntry, 'fts', 'libfts.lbug_extension'); + if (!existsSync(candidate)) continue; + const stat = statSync(candidate); + if (stat.size < MIN_VALID_FTS_EXTENSION_BYTES) continue; + if (!best || stat.mtimeMs > best.mtimeMs) best = { path: candidate, mtimeMs: stat.mtimeMs }; + } + } + return best?.path ?? null; +}; + export const FTS_UNAVAILABLE_NOTE = 'FTS extension unavailable (load-only policy; LOAD failed on this machine)'; diff --git a/gitnexus/test/integration/extension-binary-real.test.ts b/gitnexus/test/integration/extension-binary-real.test.ts index 27293c1a5..a2734d40f 100644 --- a/gitnexus/test/integration/extension-binary-real.test.ts +++ b/gitnexus/test/integration/extension-binary-real.test.ts @@ -2,21 +2,21 @@ import { copyFileSync, existsSync, mkdtempSync, - readdirSync, readFileSync, rmSync, - statSync, writeFileSync, } from 'node:fs'; import { homedir, tmpdir } from 'node:os'; import { join } from 'node:path'; import { afterAll, describe, expect, it } from 'vitest'; -import lbug from '@ladybugdb/core'; import { diagnoseExtensionLoad, inspectExtensionBinary, } from '../../src/core/lbug/extension-load-error.js'; -import { requireFtsResourceOrSkip } from '../helpers/fts-availability.js'; +import { + findInstalledFtsExtension, + requireFtsResourceOrSkip, +} from '../helpers/fts-availability.js'; /** * #2374: exercise the language-independent structural classifier against REAL @@ -49,17 +49,7 @@ function resolveLbugNative(): string | null { /** The actual installed FTS extension binary for the running lbug version. */ function resolveInstalledFtsExtension(): string | null { const home = process.env.USERPROFILE ?? process.env.HOME ?? homedir(); - const base = join(home, '.lbdb', 'extension', lbug.VERSION); - try { - const platformDir = readdirSync(base).find((entry) => - statSync(join(base, entry)).isDirectory(), - ); - if (!platformDir) return null; - const ext = join(base, platformDir, 'fts', 'libfts.lbug_extension'); - return existsSync(ext) ? ext : null; - } catch { - return null; - } + return findInstalledFtsExtension(join(home, '.lbdb', 'extension')); } const lbugNative = resolveLbugNative(); diff --git a/gitnexus/test/integration/fts-extension-e2e.test.ts b/gitnexus/test/integration/fts-extension-e2e.test.ts index 2c072a2fe..94d56cd1b 100644 --- a/gitnexus/test/integration/fts-extension-e2e.test.ts +++ b/gitnexus/test/integration/fts-extension-e2e.test.ts @@ -25,9 +25,9 @@ import path from 'path'; import fs from 'fs'; import os from 'os'; -import lbug from '@ladybugdb/core'; import { getExtensionInstallChildProcessArgs } from '../../src/core/lbug/extension-loader.js'; import { cleanupTempDirSync } from '../helpers/test-db.js'; +import { findInstalledFtsExtension } from '../helpers/fts-availability.js'; /** `.lbdb/extension///fts/libfts.lbug_extension`, discovered not hardcoded. */ let extensionRelPath: string; @@ -52,16 +52,12 @@ const makeTmpDir = (label: string): string => { * home — the production installer script, not a reimplementation. */ const resolveSeedExtension = (): void => { - const relBase = path.join('.lbdb', 'extension', lbug.VERSION); - const realVersionDir = path.join(os.homedir(), relBase); - const platformDirs = fs.existsSync(realVersionDir) ? fs.readdirSync(realVersionDir) : []; - for (const platform of platformDirs) { - const candidate = path.join(realVersionDir, platform, 'fts', 'libfts.lbug_extension'); - if (fs.existsSync(candidate) && fs.statSync(candidate).size > 1024 * 1024) { - extensionRelPath = path.join(relBase, platform, 'fts', 'libfts.lbug_extension'); - seedExtensionFile = candidate; - return; - } + const realExtensionRoot = path.join(os.homedir(), '.lbdb', 'extension'); + const installed = findInstalledFtsExtension(realExtensionRoot); + if (installed) { + extensionRelPath = path.relative(os.homedir(), installed); + seedExtensionFile = installed; + return; } // No local copy — run the real installer against a hermetic probe home. const probeHome = makeTmpDir('seed-home'); @@ -70,16 +66,13 @@ const resolveSeedExtension = (): void => { timeout: 120_000, env: { ...process.env, HOME: probeHome, USERPROFILE: probeHome }, }); - const probeVersionDir = path.join(probeHome, relBase); - const probePlatforms = fs.existsSync(probeVersionDir) ? fs.readdirSync(probeVersionDir) : []; - for (const platform of probePlatforms) { - const candidate = path.join(probeVersionDir, platform, 'fts', 'libfts.lbug_extension'); - if (install.status === 0 && fs.existsSync(candidate)) { - extensionRelPath = path.join(relBase, platform, 'fts', 'libfts.lbug_extension'); - seedExtensionFile = candidate; - networkAvailable = true; - return; - } + const probeExtensionRoot = path.join(probeHome, '.lbdb', 'extension'); + const probeInstalled = findInstalledFtsExtension(probeExtensionRoot); + if (install.status === 0 && probeInstalled) { + extensionRelPath = path.relative(probeHome, probeInstalled); + seedExtensionFile = probeInstalled; + networkAvailable = true; + return; } }; From 30ec68fa7a03ac446c0ea6c898744e418e0f57a1 Mon Sep 17 00:00:00 2001 From: Gergo Magyar Date: Tue, 21 Jul 2026 06:14:58 +0000 Subject: [PATCH 3/9] fix(test): harden findInstalledFtsExtension for cross-OS filesystem quirks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wrap the version-directory scan in try/catch so a transient FS error (permission denial, an AV file lock on Windows, a directory vanishing mid-scan) fails closed to null instead of throwing — matching the original callers' contract, and safer across the Windows/macOS/Linux CI matrix where these error modes differ. Also drop the redundant USERPROFILE/HOME manual chain in extension-binary-real.test.ts in favor of the repo's established os.homedir() convention (already used ~15 other places here), which Node resolves correctly per-OS and already honors env overrides. Co-Authored-By: Claude Sonnet 5 --- gitnexus/test/helpers/fts-availability.ts | 31 ++++++++++++------- .../integration/extension-binary-real.test.ts | 10 ++++-- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/gitnexus/test/helpers/fts-availability.ts b/gitnexus/test/helpers/fts-availability.ts index bc3620662..30ba05b8a 100644 --- a/gitnexus/test/helpers/fts-availability.ts +++ b/gitnexus/test/helpers/fts-availability.ts @@ -18,20 +18,27 @@ const MIN_VALID_FTS_EXTENSION_BYTES = 1024 * 1024; * actually just resolved), or null when nothing is installed. */ export const findInstalledFtsExtension = (extensionRoot: string): string | null => { - if (!existsSync(extensionRoot)) return null; - let best: { path: string; mtimeMs: number } | null = null; - for (const versionEntry of readdirSync(extensionRoot)) { - const versionDir = join(extensionRoot, versionEntry); - if (!statSync(versionDir).isDirectory()) continue; - for (const platformEntry of readdirSync(versionDir)) { - const candidate = join(versionDir, platformEntry, 'fts', 'libfts.lbug_extension'); - if (!existsSync(candidate)) continue; - const stat = statSync(candidate); - if (stat.size < MIN_VALID_FTS_EXTENSION_BYTES) continue; - if (!best || stat.mtimeMs > best.mtimeMs) best = { path: candidate, mtimeMs: stat.mtimeMs }; + // Fail closed on any FS error (permission quirks, AV file locks on Windows, + // a directory vanishing mid-scan) — same contract as the callers this + // replaces: "not found" is a valid outcome, a thrown exception is not. + try { + if (!existsSync(extensionRoot)) return null; + let best: { path: string; mtimeMs: number } | null = null; + for (const versionEntry of readdirSync(extensionRoot)) { + const versionDir = join(extensionRoot, versionEntry); + if (!statSync(versionDir).isDirectory()) continue; + for (const platformEntry of readdirSync(versionDir)) { + const candidate = join(versionDir, platformEntry, 'fts', 'libfts.lbug_extension'); + if (!existsSync(candidate)) continue; + const stat = statSync(candidate); + if (stat.size < MIN_VALID_FTS_EXTENSION_BYTES) continue; + if (!best || stat.mtimeMs > best.mtimeMs) best = { path: candidate, mtimeMs: stat.mtimeMs }; + } } + return best?.path ?? null; + } catch { + return null; } - return best?.path ?? null; }; export const FTS_UNAVAILABLE_NOTE = diff --git a/gitnexus/test/integration/extension-binary-real.test.ts b/gitnexus/test/integration/extension-binary-real.test.ts index a2734d40f..f46958a2b 100644 --- a/gitnexus/test/integration/extension-binary-real.test.ts +++ b/gitnexus/test/integration/extension-binary-real.test.ts @@ -46,10 +46,14 @@ function resolveLbugNative(): string | null { return null; } -/** The actual installed FTS extension binary for the running lbug version. */ +/** + * The actual installed FTS extension binary for the running lbug version. + * `os.homedir()` already honors `$HOME` (POSIX) / `%USERPROFILE%` (Windows) — + * the same resolution LadybugDB's native layer uses — so it stays correct + * under the hermetic-home overrides other tests in this suite set via env vars. + */ function resolveInstalledFtsExtension(): string | null { - const home = process.env.USERPROFILE ?? process.env.HOME ?? homedir(); - return findInstalledFtsExtension(join(home, '.lbdb', 'extension')); + return findInstalledFtsExtension(join(homedir(), '.lbdb', 'extension')); } const lbugNative = resolveLbugNative(); From 86cde93652651a71766e0569cd9095f1c385ef6a Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 20 Jul 2026 10:38:50 +0000 Subject: [PATCH 4/9] docs(plans): add require-node-22.18 plan Co-Authored-By: Claude Fable 5 --- ...6-07-20-gitnexus-plan-require-node-2218.md | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 docs/plans/2026-07-20-gitnexus-plan-require-node-2218.md diff --git a/docs/plans/2026-07-20-gitnexus-plan-require-node-2218.md b/docs/plans/2026-07-20-gitnexus-plan-require-node-2218.md new file mode 100644 index 000000000..62e9b2e96 --- /dev/null +++ b/docs/plans/2026-07-20-gitnexus-plan-require-node-2218.md @@ -0,0 +1,113 @@ +# GitNexus Engineering Plan — Raise the gitnexus Node floor to 22.18+ + +> Task: Make `npm install` in `gitnexus/` warning-free by raising the supported Node floor to `^22.18.0 || >=24.11.0` (matching Babel 8, kept), removing the deprecated `@types/uuid` stub, and moving the CI lanes pinned below the new floor. +> Base commit: 2ea00a2b22c65073c77148d6f8303e0e31612851 (branch worktree-fix-ebadengine-babel8). +> Executed in overlay clone /home/node/gn-fix-ebadengine (the /workspace worktree is a 9p mount where the safe plan-writer's renameat2(RENAME_NOREPLACE) fails). + +## 1. Objective + +Eliminate the nine `npm warn EBADENGINE` warnings from the `@babel/*@8.x` +devDependencies and the `@types/uuid@11` deprecation warning, by adopting Node +22.18+ as the supported minimum rather than pinning Babel back to 7. This +supersedes the initial "pin Babel to 7" approach on maintainer direction: +the project will *support 22.18+* going forward. + +## 2. Current behaviour + +- `gitnexus/package.json` declares `engines: node >=22.0.0` but four + devDependencies (`@babel/generator|parser|traverse|types`) are at `^8.0.0` + (arrived via Dependabot #2518–#2520). Babel 8 declares + `engines: node ^22.18.0 || >=24.11.0`, so every dev install on Node <22.18 + warns nine times (the direct four plus five transitive Babel packages). +- `@types/uuid@11.0.0` is a deprecated stub — `uuid@14` (a runtime dep) ships + its own types, and no tsconfig `types` array references uuid. +- The only consumer of the Babel devDeps is the bench mutation oracle + (`gitnexus/bench/impact-pdg/mutation-oracle.mjs`), lazily imported by + `measure.mjs` only under `--mutation`. +- Several CI lanes pin Node below 22.18: `ci-tests.yml` `node-floor-compat` + (22.14.0) and the containment-canary job (22.16.0), + `gitnexus-review-agent.yml` (22.16.0), `gitnexus-skill-evolution.yml` + (22.16.0). Under a 22.18 floor these run below the supported minimum. + +## 3. Approach decision + +Two ways to make installs warning-free: + +- **(A) Pin Babel to 7** — keeps the floor at `>=22.0.0`; suppresses the + warning without changing what the project supports. Requires a Dependabot + ignore so the Babel 8 bump does not return. +- **(B) Raise the floor to 22.18+ and keep Babel 8** — chosen. `engines` + becomes `^22.18.0 || >=24.11.0`, matching Babel 8 exactly, so the warnings + vanish honestly and no Dependabot ignore is needed. Cost: a user-facing + raise of the minimum Node (drops 22.0–22.17), which is why it moves the + documented floor and the CI floor gate deliberately. + +The `engines` string mirrors Babel 8's own constraint (`^22.18.0` = the +22.18-and-up 22.x line; `>=24.11.0` = the 24.x line that got the relevant +backport) so that no Babel-8 EBADENGINE can reappear on any Node the project +claims to support. `>=22.18.0` would be looser but would re-warn on Node 23.x +and 24.0–24.10, which Babel 8 excludes. + +## 4. Proposed changes + +| File | Change | +| ---- | ------ | +| `gitnexus/package.json` | `engines.node` `>=22.0.0` → `^22.18.0 \|\| >=24.11.0`; remove `@types/uuid` devDependency. Babel stays `^8.0.0`. | +| `gitnexus/package-lock.json` | Mirror both edits (engines + `@types/uuid` entry removed). Hand-applied to avoid npm-version `libc` metadata churn (local npm 10.9.2 strips the `libc` platform arrays a newer npm wrote; those drive musl/glibc optional-dep resolution and must be preserved). Verified consistent via `npm ci` exit 0. | +| `CONTRIBUTING.md` | Prerequisite floor `>=22.0.0` → `^22.18.0 \|\| >=24.11.0`. | +| `.github/workflows/ci-tests.yml` | `node-floor-compat` retargeted 22.14.0 → 22.18.0 (name, comment, pin, version assertion) so the floor gate guards the *new* minimum; containment-canary pin 22.16.0 → 22.18.0. | +| `.github/workflows/gitnexus-review-agent.yml` | Pinned Node 22.16.0 → 22.18.0. | +| `.github/workflows/gitnexus-skill-evolution.yml` | Pinned Node 22.16.0 → 22.18.0. | + +CI lanes using `node-version: 22` (latest ≥22.18) or `24` are already at/above +the floor and unchanged. `CHANGELOG.md` and `package.json` `version` are +release-owned (per repo convention) and deliberately untouched. + +## 5. Implementation sequence + +1. `gitnexus/package.json` + `gitnexus/package-lock.json`: engines bump and + `@types/uuid` removal (one atomic commit). +2. CI + docs: floor-gate retarget, the three pinned-lane bumps, CONTRIBUTING + prerequisite (one atomic commit). + +Both orderings leave the tree coherent; each is `detect_changes`-gated +(config/manifest files carry no indexed symbols → empty affected set). + +## 6. Verification + +- `npm ci` in `gitnexus/` exits 0 (lockfile ↔ package.json consistency after + the hand-edit). +- On local Node 22.16.0 (now *below* the floor), the only EBADENGINE lines are + the nine Babel 8 packages plus `gitnexus` itself, all reporting the identical + `required: ^22.18.0 || >=24.11.0` — i.e. they satisfy together at ≥22.18 and + vanish by construction. No other engine warning; no `@types/uuid` + deprecation. (A true zero-warning run requires Node ≥22.18, unavailable in + this sandbox; CI's ≥22.18 lanes are the authoritative check.) +- Mutation oracle single-fixture run + (`measure.mjs --mutation --only=intra-control-branch --json`) exits 0 on + Babel 8 with a fingerprint identical to the Babel 7 run — the bench consumer + is unaffected. +- `npm run test:unit` passes. + +## 7. Risks + +- **User-facing floor raise** — dropping Node 22.0–22.17 is a support-policy + change. Deliberate and the point of this PR; belongs in the release notes at + release time (not edited here per CHANGELOG convention). +- **Lockfile hand-edit** — mitigated by the `npm ci` exit-0 consistency proof + and by preserving `libc` platform metadata (musl/Alpine native resolution). +- **node-floor-compat** — its original #2372 failure mode (`module.registerHooks` + ≥22.15 on a sub-22.15 floor) can no longer occur at a 22.18 floor; the gate + is retained, retargeted to 22.18.0, to guard the new minimum generally. + +## 8. Definition of Done + +1. `gitnexus/package.json` `engines.node` = `^22.18.0 || >=24.11.0`; Babel at + `^8.0.0`; `@types/uuid` absent. +2. `npm ci` exits 0; no `@types/uuid` deprecation; the only EBADENGINE lines on + sub-floor Node are Babel 8 + gitnexus-self, all with the new required range. +3. Mutation oracle single-fixture run exits 0 on Babel 8. +4. `npm run test:unit` passes. +5. No CI lane pins Node below 22.18; `node-floor-compat` guards 22.18.0. +6. `CONTRIBUTING.md` floor updated; `CHANGELOG.md`/`version` untouched. +7. Diff limited to the six files above (+ this plan). From c26b78d15309e4c03289e58b0cc98f569e1986d6 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 20 Jul 2026 10:39:06 +0000 Subject: [PATCH 5/9] fix(deps)!: require Node >=22.18 and drop @types/uuid stub Babel 8 (devDep for the bench mutation oracle, pulled in by dependabot previous floor (>=22.0.0), so every dev install on Node <22.18 emitted nine EBADENGINE warnings. Rather than pin Babel back to 7, adopt Node 22.18+ as the supported minimum: set engines to ^22.18.0 || >=24.11.0, matching Babel 8 exactly so the warnings resolve honestly with no dependabot ignore needed. @types/uuid@11 is a deprecated stub - uuid@14 ships its own types and no tsconfig references it. Lockfile edited by hand (engines + @types/uuid entry) to preserve the libc platform metadata a newer npm wrote; verified consistent via npm ci (exit 0). BREAKING CHANGE: the gitnexus package now requires Node ^22.18.0 || >=24.11.0 (previously >=22.0.0). Node 22.0-22.17 are no longer supported. Co-Authored-By: Claude Fable 5 --- gitnexus/package-lock.json | 14 +------------- gitnexus/package.json | 3 +-- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/gitnexus/package-lock.json b/gitnexus/package-lock.json index d9462f5a6..e3d28cbd4 100644 --- a/gitnexus/package-lock.json +++ b/gitnexus/package-lock.json @@ -60,7 +60,6 @@ "@types/express": "^5.0.6", "@types/js-yaml": "^4.0.9", "@types/node": "^26.0.0", - "@types/uuid": "^11.0.0", "@vitest/coverage-v8": "^4.0.18", "gitnexus-shared": "file:../gitnexus-shared", "tsx": "^4.0.0", @@ -68,7 +67,7 @@ "vitest": "^4.0.18" }, "engines": { - "node": ">=22.0.0" + "node": "^22.18.0 || >=24.11.0" }, "optionalDependencies": { "@huggingface/transformers": "^4.1.0", @@ -1990,17 +1989,6 @@ "@types/node": "*" } }, - "node_modules/@types/uuid": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-11.0.0.tgz", - "integrity": "sha512-HVyk8nj2m+jcFRNazzqyVKiZezyhDKrGUA3jlEcg/nZ6Ms+qHwocba1Y/AaVaznJTAM9xpdFSh+ptbNrhOGvZA==", - "deprecated": "This is a stub types definition. uuid provides its own type definitions, so you do not need this installed.", - "dev": true, - "license": "MIT", - "dependencies": { - "uuid": "*" - } - }, "node_modules/@vitest/coverage-v8": { "version": "4.1.10", "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.10.tgz", diff --git a/gitnexus/package.json b/gitnexus/package.json index fbbafb3ec..387c89811 100644 --- a/gitnexus/package.json +++ b/gitnexus/package.json @@ -107,7 +107,6 @@ "@types/express": "^5.0.6", "@types/js-yaml": "^4.0.9", "@types/node": "^26.0.0", - "@types/uuid": "^11.0.0", "@vitest/coverage-v8": "^4.0.18", "gitnexus-shared": "file:../gitnexus-shared", "tsx": "^4.0.0", @@ -120,6 +119,6 @@ } }, "engines": { - "node": ">=22.0.0" + "node": "^22.18.0 || >=24.11.0" } } From eea9ac92dccead0287c2aa8bb044247f9718f834 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 20 Jul 2026 10:39:18 +0000 Subject: [PATCH 6/9] ci: move Node pins to the 22.18 floor With the supported minimum raised to Node 22.18, retarget every lane and pinned runtime that sat at a lower version so nothing builds or runs the package on an unsupported (EBADENGINE-warning) Node: - ci-tests.yml: node-floor-compat 22.14 -> 22.18.0 (name, comment, pin, version assertion) so the floor gate guards the new minimum; its #2372 registerHooks failure mode cannot recur above 22.15. Containment-canary pin 22.16.0 -> 22.18.0. - gitnexus-review-agent.yml + the pinned review/canary runtime: the reproducible runtime is version-locked in lockstep across .github/{gitnexus-review-runtime,claude-canary-runtime}/package.json and their lockfiles (engines), the workflow's node-version, its two 'node --version = v22.18.0' assertions, the lockfile-engines guard, and NODE_VERSION. Moved all of them 22.16.0 -> 22.18.0. - gitnexus-skill-evolution.yml: pinned runtime 22.16.0 -> 22.18.0. - CONTRIBUTING.md prerequisite floor updated. - review-agent-workflow.test.ts, which enforces the runtime lock, updated to expect 22.18.0. Co-Authored-By: Claude Fable 5 --- .../claude-canary-runtime/package-lock.json | 2 +- .github/claude-canary-runtime/package.json | 2 +- .../gitnexus-review-runtime/package-lock.json | 2 +- .github/gitnexus-review-runtime/package.json | 2 +- .github/workflows/ci-tests.yml | 25 ++++++++++--------- .github/workflows/gitnexus-review-agent.yml | 10 ++++---- .../workflows/gitnexus-skill-evolution.yml | 2 +- CONTRIBUTING.md | 2 +- .../test/unit/review-agent-workflow.test.ts | 10 ++++---- 9 files changed, 29 insertions(+), 28 deletions(-) diff --git a/.github/claude-canary-runtime/package-lock.json b/.github/claude-canary-runtime/package-lock.json index e78392daa..7716ef93f 100644 --- a/.github/claude-canary-runtime/package-lock.json +++ b/.github/claude-canary-runtime/package-lock.json @@ -11,7 +11,7 @@ "@anthropic-ai/claude-code": "2.1.214" }, "engines": { - "node": "22.16.0" + "node": "22.18.0" } }, "node_modules/@anthropic-ai/claude-code": { diff --git a/.github/claude-canary-runtime/package.json b/.github/claude-canary-runtime/package.json index 57076d892..50820742b 100644 --- a/.github/claude-canary-runtime/package.json +++ b/.github/claude-canary-runtime/package.json @@ -3,7 +3,7 @@ "version": "0.0.0", "private": true, "engines": { - "node": "22.16.0" + "node": "22.18.0" }, "dependencies": { "@anthropic-ai/claude-code": "2.1.214" diff --git a/.github/gitnexus-review-runtime/package-lock.json b/.github/gitnexus-review-runtime/package-lock.json index e805e759b..0677011c0 100644 --- a/.github/gitnexus-review-runtime/package-lock.json +++ b/.github/gitnexus-review-runtime/package-lock.json @@ -11,7 +11,7 @@ "gitnexus": "1.6.9" }, "engines": { - "node": "22.16.0" + "node": "22.18.0" } }, "node_modules/@emnapi/runtime": { diff --git a/.github/gitnexus-review-runtime/package.json b/.github/gitnexus-review-runtime/package.json index 237310bad..de0a1dd12 100644 --- a/.github/gitnexus-review-runtime/package.json +++ b/.github/gitnexus-review-runtime/package.json @@ -3,7 +3,7 @@ "private": true, "version": "1.0.0", "engines": { - "node": "22.16.0" + "node": "22.18.0" }, "dependencies": { "gitnexus": "1.6.9" diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml index c78a407b9..d9aa33a4e 100644 --- a/.github/workflows/ci-tests.yml +++ b/.github/workflows/ci-tests.yml @@ -378,15 +378,16 @@ jobs: "$PREFIX/bin/gitnexus" --version fi - # Node engines-floor gate (#2372). The embedding resolvers statically named - # `module.registerHooks`, which only exists on Node >= 22.15 / >= 23.5, so on - # the supported floor (engines: >=22.0.0) those ESM modules failed to LINK — - # a class vitest/tsx transforms structurally mask, and the default - # `node-version: 22` (resolves to latest) never hits. Build the dist on 22.x, - # then import-link every module R1 names as a load surface on a pinned 22.14 - # so a regression fails here instead of shipping to users on that Node range. + # Node engines-floor gate (#2372). A module that statically names an API + # newer than the supported floor (e.g. `module.registerHooks`, added in + # 22.15) fails to LINK on the floor — a class vitest/tsx transforms + # structurally mask, and the default `node-version: 22` (resolves to latest) + # never hits. Build the dist on 22.x, then import-link every module R1 names + # as a load surface on the pinned engines floor (22.18.0, per package.json + # `engines: ^22.18.0 || >=24.11.0`) so a regression fails here instead of + # shipping to users on the minimum supported Node. node-floor-compat: - name: node floor compat (22.14) + name: node floor compat (22.18) runs-on: ubuntu-latest timeout-minutes: 15 steps: @@ -415,14 +416,14 @@ jobs: # (so no package-manager cache is needed). - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: - node-version: '22.14.0' + node-version: '22.18.0' package-manager-cache: false - - name: Import-link the built dist on Node 22.14 + - name: Import-link the built dist on Node 22.18 shell: bash run: | set -euo pipefail node --version - node --version | grep -q '^v22\.14\.' || { echo "expected Node 22.14.x" >&2; exit 1; } + node --version | grep -q '^v22\.18\.' || { echo "expected Node 22.18.x" >&2; exit 1; } for m in \ core/embeddings/runtime-install \ core/embeddings/onnxruntime-node-resolver \ @@ -556,7 +557,7 @@ jobs: persist-credentials: false - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: - node-version: '22.16.0' + node-version: '22.18.0' cache: npm cache-dependency-path: | gitnexus/package-lock.json diff --git a/.github/workflows/gitnexus-review-agent.yml b/.github/workflows/gitnexus-review-agent.yml index 8582403ad..88526871e 100644 --- a/.github/workflows/gitnexus-review-agent.yml +++ b/.github/workflows/gitnexus-review-agent.yml @@ -325,7 +325,7 @@ jobs: if: steps.context.outputs.ready == 'true' uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: - node-version: '22.16.0' + node-version: '22.18.0' - name: Install and preflight Claude subprocess isolation id: isolation @@ -377,7 +377,7 @@ jobs: .github/claude-canary-runtime/package-lock.json \ "${runtime_dir}/package-lock.json" printf '%s\n' 'registry=https://registry.npmjs.org/' 'audit=false' 'fund=false' > "${npmrc}" - test "$(node --version)" = 'v22.16.0' + test "$(node --version)" = 'v22.18.0' test "$(uname -m)" = 'x86_64' # The trusted lock and these independent receipts pin both the thin @@ -398,7 +398,7 @@ jobs: if ( lock.lockfileVersion !== 3 || lock.packages?.['']?.dependencies?.['@anthropic-ai/claude-code'] !== '2.1.214' || - lock.packages?.['']?.engines?.node !== '22.16.0' + lock.packages?.['']?.engines?.node !== '22.18.0' ) { throw new Error('Claude runtime lock root is not exact'); } @@ -506,7 +506,7 @@ jobs: install -m 0600 .github/gitnexus-review-runtime/package.json "${runtime_dir}/package.json" install -m 0600 .github/gitnexus-review-runtime/package-lock.json "${runtime_dir}/package-lock.json" printf '%s\n' 'registry=https://registry.npmjs.org/' 'audit=false' 'fund=false' > "${npmrc}" - test "$(node --version)" = 'v22.16.0' + test "$(node --version)" = 'v22.18.0' npm ci \ --prefix "${runtime_dir}" \ --userconfig "${npmrc}" \ @@ -1241,7 +1241,7 @@ jobs: CLAUDE_CONFIG_DIR: ${{ runner.temp }}/gitnexus-review-claude-config CLAUDE_WORKING_DIR: ${{ runner.temp }}/gitnexus-review-control NPM_CONFIG_IGNORE_SCRIPTS: 'true' - NODE_VERSION: '22.16.0' + NODE_VERSION: '22.18.0' with: claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} path_to_claude_code_executable: ${{ runner.temp }}/gitnexus-review-claude-runtime/node_modules/@anthropic-ai/claude-code/bin/claude.exe diff --git a/.github/workflows/gitnexus-skill-evolution.yml b/.github/workflows/gitnexus-skill-evolution.yml index 66e87ad18..8d9454d56 100644 --- a/.github/workflows/gitnexus-skill-evolution.yml +++ b/.github/workflows/gitnexus-skill-evolution.yml @@ -110,7 +110,7 @@ jobs: - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: - node-version: '22.16.0' + node-version: '22.18.0' cache: npm cache-dependency-path: | gitnexus/package-lock.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e9922cf19..4e9e17369 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,7 +13,7 @@ This project uses the [PolyForm Noncommercial License 1.0.0](https://polyformpro ## Development setup -**Prerequisites:** Node.js — `gitnexus/` requires `>=22.0.0` and `gitnexus-web/` requires `^20.19.0 || >=22.12.0` (enforced via the `engines` field in each package). Use `nvm install` to match the local version. +**Prerequisites:** Node.js — `gitnexus/` requires `^22.18.0 || >=24.11.0` and `gitnexus-web/` requires `^20.19.0 || >=22.12.0` (enforced via the `engines` field in each package). Use `nvm install` to match the local version. 1. Clone the repository. 2. **Shared package:** `cd gitnexus-shared && npm install && npm run build` diff --git a/gitnexus/test/unit/review-agent-workflow.test.ts b/gitnexus/test/unit/review-agent-workflow.test.ts index faf9513d6..35657e6ea 100644 --- a/gitnexus/test/unit/review-agent-workflow.test.ts +++ b/gitnexus/test/unit/review-agent-workflow.test.ts @@ -589,12 +589,12 @@ describe('gitnexus review-agent workflow security contract', () => { } expect(runtimePackage.dependencies?.gitnexus).toBe('1.6.9'); - expect(runtimePackage.engines?.node).toBe('22.16.0'); + expect(runtimePackage.engines?.node).toBe('22.18.0'); expect(runtimeLock.packages?.['node_modules/gitnexus']?.version).toBe('1.6.9'); expect(runtimeLock.packages?.['node_modules/gitnexus']?.integrity).toMatch(/^sha512-/); expect(workflow).not.toMatch(/gitnexus@(latest|next|beta)/); - expect(workflow).toContain("node-version: '22.16.0'"); - expect(workflow).toContain('test "$(node --version)" = \'v22.16.0\''); + expect(workflow).toContain("node-version: '22.18.0'"); + expect(workflow).toContain('test "$(node --version)" = \'v22.18.0\''); expect(workflow).toContain('npm ci'); expect(workflow).not.toContain('--package-lock=false'); expect(workflow).toContain( @@ -682,7 +682,7 @@ describe('gitnexus review-agent workflow security contract', () => { '${{ runner.temp }}/gitnexus-review-claude-runtime/node_modules/@anthropic-ai/claude-code/bin/claude.exe'; expect(claudeRuntimePackage.dependencies?.['@anthropic-ai/claude-code']).toBe('2.1.214'); - expect(claudeRuntimePackage.engines?.node).toBe('22.16.0'); + expect(claudeRuntimePackage.engines?.node).toBe('22.18.0'); expect(claudeRuntimeLock.lockfileVersion).toBe(3); expect(claudeRuntimeLock.packages?.['node_modules/@anthropic-ai/claude-code']).toMatchObject({ version: '2.1.214', @@ -1119,7 +1119,7 @@ describe('gitnexus review-agent workflow security contract', () => { 'CLAUDE_CONFIG_DIR: ${{ runner.temp }}/gitnexus-review-claude-config', ); expect(analyze).toContain('CLAUDE_WORKING_DIR: ${{ runner.temp }}/gitnexus-review-control'); - expect(analyze).toContain("NODE_VERSION: '22.16.0'"); + expect(analyze).toContain("NODE_VERSION: '22.18.0'"); expect(analyze).toContain('checkout-index --all --force'); expect(analyze).toContain('find "${review_dir}" -type l -print0'); expect(analyze).toContain('Escaping copied review symlink'); From 1415bd5c2f7b7e13888eb5746cf107d86c5a2e0d Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 20 Jul 2026 11:26:51 +0000 Subject: [PATCH 7/9] docs(embeddings): update engines-floor comments for the 22.18 minimum The module.registerHooks compat seam and the onnxruntime resolvers cited the old '>=22.0.0' floor as the reason their sub-22.15 fallback was reachable. With the floor now ^22.18.0 || >=24.11.0 (all >=22.15), every supported runtime exposes the API; the fallback stays as defensive handling for below-floor runtimes (engines is advisory, not engine-strict). Comments only - no behavior change. Co-Authored-By: Claude Fable 5 --- gitnexus/src/core/embeddings/node-module-compat.ts | 6 ++++-- .../src/core/embeddings/onnxruntime-common-resolver.ts | 10 +++++----- .../src/core/embeddings/onnxruntime-node-resolver.ts | 4 ++-- gitnexus/src/core/embeddings/runtime-install.ts | 2 +- gitnexus/test/unit/node-module-compat.test.ts | 5 +++-- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/gitnexus/src/core/embeddings/node-module-compat.ts b/gitnexus/src/core/embeddings/node-module-compat.ts index b32e854e9..8f41c5d22 100644 --- a/gitnexus/src/core/embeddings/node-module-compat.ts +++ b/gitnexus/src/core/embeddings/node-module-compat.ts @@ -3,8 +3,10 @@ * * `module.registerHooks` — the synchronous ESM/CJS resolution-hook API the * embedding-stack resolvers rely on — was added in Node 22.15.0 (and 23.5.0 on - * the 23.x line). The gitnexus engines floor is `>=22.0.0`, which admits Node - * 22.0–22.14 AND 23.0–23.4, where the export is absent. + * the 23.x line). The gitnexus engines floor is `^22.18.0 || >=24.11.0`, so + * every supported runtime exposes it — but `engines` is advisory (not + * engine-strict), so a below-floor Node (22.0–22.14, or the unsupported + * 23.0–23.4 line) can still run, where the export is absent. * * In this `"type": "module"` package, a *static named* import of a missing * builtin export (`import { registerHooks } from 'node:module'`) is a diff --git a/gitnexus/src/core/embeddings/onnxruntime-common-resolver.ts b/gitnexus/src/core/embeddings/onnxruntime-common-resolver.ts index 2d494f2b7..8fd2ddf17 100644 --- a/gitnexus/src/core/embeddings/onnxruntime-common-resolver.ts +++ b/gitnexus/src/core/embeddings/onnxruntime-common-resolver.ts @@ -52,8 +52,8 @@ * per-resolution cost is a single string comparison. * * `module.registerHooks` is marked `@experimental` and requires Node >= 22.15 - * (the gitnexus engines floor is >= 22.0.0). On older runtimes it is absent and - * this is a graceful no-op: embeddings then resolve onnxruntime-common exactly + * (below the gitnexus engines floor of `^22.18.0 || >=24.11.0`). On below-floor + * runtimes it is absent and this is a graceful no-op: embeddings then resolve onnxruntime-common exactly * as before — fine on hoisted layouts. Any failure during installation is * swallowed. */ @@ -100,9 +100,9 @@ export const ensureOnnxRuntimeCommonResolvable = (): void => { attempted = true; try { - // Node < 22.15 / < 23.5 (the gitnexus engines floor is >= 22.0.0): no - // synchronous hooks API. Degrade gracefully — the import still works on - // hoisted layouts. + // Node < 22.15 / < 23.5 (below the gitnexus engines floor of + // ^22.18.0 || >=24.11.0): no synchronous hooks API. Degrade gracefully — + // the import still works on hoisted layouts. const registerHooks = getRegisterHooks(); if (typeof registerHooks !== 'function') return; diff --git a/gitnexus/src/core/embeddings/onnxruntime-node-resolver.ts b/gitnexus/src/core/embeddings/onnxruntime-node-resolver.ts index 65465eb00..f630384ca 100644 --- a/gitnexus/src/core/embeddings/onnxruntime-node-resolver.ts +++ b/gitnexus/src/core/embeddings/onnxruntime-node-resolver.ts @@ -36,8 +36,8 @@ * So CUDA-12 hosts, Windows (DirectML), macOS, and CPU-only hosts are * untouched. Idempotent; any failure is swallowed and leaves the default * resolution exactly as before. `module.registerHooks` requires Node >= 22.15 - * (the gitnexus engines floor is >= 22.0.0); on older runtimes the redirect is - * a no-op, but the default copy's CUDA major is still probed so an + * (below the gitnexus engines floor of `^22.18.0 || >=24.11.0`); on below-floor + * runtimes the redirect is a no-op, but the default copy's CUDA major is still probed so an * already-matching host (e.g. CUDA 12 + transformers' CUDA-12 build) keeps * auto-selecting the GPU. * `npm link` / symlinked local-dev checkouts are a known caveat: `resolveOurOrtNodeDir`/ diff --git a/gitnexus/src/core/embeddings/runtime-install.ts b/gitnexus/src/core/embeddings/runtime-install.ts index b7c054b0b..e52274746 100644 --- a/gitnexus/src/core/embeddings/runtime-install.ts +++ b/gitnexus/src/core/embeddings/runtime-install.ts @@ -191,7 +191,7 @@ export const ensureEmbeddingStackResolvable = (): void => { hookAttempted = true; try { - // Node < 22.15 / < 23.5 (engines floor is >= 22.0.0): no synchronous hooks + // Node < 22.15 / < 23.5 (below the engines floor of ^22.18.0 || >=24.11.0): no synchronous hooks // API. Degrade gracefully — normally-installed stacks still resolve; only // the runtime-prefix fallback is unavailable. Reachable now that the import // is a namespace access (see node-module-compat.ts) rather than a static diff --git a/gitnexus/test/unit/node-module-compat.test.ts b/gitnexus/test/unit/node-module-compat.test.ts index 6323bd836..47c5f5ba6 100644 --- a/gitnexus/test/unit/node-module-compat.test.ts +++ b/gitnexus/test/unit/node-module-compat.test.ts @@ -2,8 +2,9 @@ import { describe, it, expect, vi, afterEach } from 'vitest'; /** * Tests for the #2372 `node:module` compat seam. `module.registerHooks` was - * added in Node 22.15 / 23.5, but the engines floor is >=22.0.0, so on - * 22.0–22.14 and 23.0–23.4 the export is absent. `getRegisterHooks()` must + * added in Node 22.15 / 23.5. The engines floor is ^22.18.0 || >=24.11.0 (all + * >=22.15), but engines is advisory, so a below-floor 22.0–22.14 / 23.0–23.4 + * runtime can still run, where the export is absent. `getRegisterHooks()` must * hand back the real function when present and `undefined` when not — the value * the resolver guards degrade on. `isPrefixRuntimeLoadable()` (exported from * runtime-install.ts so CLI code never imports the compat module) is the From bb23d2998abf8e215c4058d96a1545324efd4be9 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 20 Jul 2026 11:39:40 +0000 Subject: [PATCH 8/9] test(eval): update containment-job node-version assertion to 22.18.0 test_eval_ci_uses_locked_uv_and_blocking_native_containment_jobs pins the eval-containment-linux job's setup-node version; move it in lockstep with the ci-tests.yml pin bumped to the 22.18 floor. Co-Authored-By: Claude Fable 5 --- eval/tests/test_workflow_bench.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eval/tests/test_workflow_bench.py b/eval/tests/test_workflow_bench.py index e4610e197..961c0edd4 100644 --- a/eval/tests/test_workflow_bench.py +++ b/eval/tests/test_workflow_bench.py @@ -172,7 +172,7 @@ def test_eval_ci_uses_locked_uv_and_blocking_native_containment_jobs(): } assert containment["timeout-minutes"] == 20 assert containment_node_setup["with"] == { - "node-version": "22.16.0", + "node-version": "22.18.0", "cache": "npm", "cache-dependency-path": "gitnexus/package-lock.json\ngitnexus-shared/package-lock.json\n", } From 694048a987ab826d5cf819779fd8a70da4294711 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 20 Jul 2026 11:47:59 +0000 Subject: [PATCH 9/9] chore: stop tracking docs/plans (planning output stays local) Reverses the prior convention: gitnexus-plan/gitnexus-work plan documents under docs/plans/ are working artifacts and no longer travel with the PR. Drops the require-node-22.18 plan doc from tracking; the .gitignore now ignores all of docs/. The workflow_bench snapshot features scan the filesystem, not git-tracked status, so they are unaffected. Co-Authored-By: Claude Fable 5 --- .gitignore | 3 +- ...6-07-20-gitnexus-plan-require-node-2218.md | 113 ------------------ 2 files changed, 1 insertion(+), 115 deletions(-) delete mode 100644 docs/plans/2026-07-20-gitnexus-plan-require-node-2218.md diff --git a/.gitignore b/.gitignore index 60795b24f..e16544f71 100644 --- a/.gitignore +++ b/.gitignore @@ -68,9 +68,8 @@ gitnexus-web/test-results/ eval/.coverage eval/.hypothesis/ -# Local docs (docs/plans/ stays tracked — gitnexus-plan output travels with the work) +# Local docs — planning output (gitnexus-plan / gitnexus-work) stays local, not tracked docs/* -!docs/plans/ gitnexus/test/fixtures/mini-repo/*.md gitnexus/test/fixtures/mini-repo/.claude diff --git a/docs/plans/2026-07-20-gitnexus-plan-require-node-2218.md b/docs/plans/2026-07-20-gitnexus-plan-require-node-2218.md deleted file mode 100644 index 62e9b2e96..000000000 --- a/docs/plans/2026-07-20-gitnexus-plan-require-node-2218.md +++ /dev/null @@ -1,113 +0,0 @@ -# GitNexus Engineering Plan — Raise the gitnexus Node floor to 22.18+ - -> Task: Make `npm install` in `gitnexus/` warning-free by raising the supported Node floor to `^22.18.0 || >=24.11.0` (matching Babel 8, kept), removing the deprecated `@types/uuid` stub, and moving the CI lanes pinned below the new floor. -> Base commit: 2ea00a2b22c65073c77148d6f8303e0e31612851 (branch worktree-fix-ebadengine-babel8). -> Executed in overlay clone /home/node/gn-fix-ebadengine (the /workspace worktree is a 9p mount where the safe plan-writer's renameat2(RENAME_NOREPLACE) fails). - -## 1. Objective - -Eliminate the nine `npm warn EBADENGINE` warnings from the `@babel/*@8.x` -devDependencies and the `@types/uuid@11` deprecation warning, by adopting Node -22.18+ as the supported minimum rather than pinning Babel back to 7. This -supersedes the initial "pin Babel to 7" approach on maintainer direction: -the project will *support 22.18+* going forward. - -## 2. Current behaviour - -- `gitnexus/package.json` declares `engines: node >=22.0.0` but four - devDependencies (`@babel/generator|parser|traverse|types`) are at `^8.0.0` - (arrived via Dependabot #2518–#2520). Babel 8 declares - `engines: node ^22.18.0 || >=24.11.0`, so every dev install on Node <22.18 - warns nine times (the direct four plus five transitive Babel packages). -- `@types/uuid@11.0.0` is a deprecated stub — `uuid@14` (a runtime dep) ships - its own types, and no tsconfig `types` array references uuid. -- The only consumer of the Babel devDeps is the bench mutation oracle - (`gitnexus/bench/impact-pdg/mutation-oracle.mjs`), lazily imported by - `measure.mjs` only under `--mutation`. -- Several CI lanes pin Node below 22.18: `ci-tests.yml` `node-floor-compat` - (22.14.0) and the containment-canary job (22.16.0), - `gitnexus-review-agent.yml` (22.16.0), `gitnexus-skill-evolution.yml` - (22.16.0). Under a 22.18 floor these run below the supported minimum. - -## 3. Approach decision - -Two ways to make installs warning-free: - -- **(A) Pin Babel to 7** — keeps the floor at `>=22.0.0`; suppresses the - warning without changing what the project supports. Requires a Dependabot - ignore so the Babel 8 bump does not return. -- **(B) Raise the floor to 22.18+ and keep Babel 8** — chosen. `engines` - becomes `^22.18.0 || >=24.11.0`, matching Babel 8 exactly, so the warnings - vanish honestly and no Dependabot ignore is needed. Cost: a user-facing - raise of the minimum Node (drops 22.0–22.17), which is why it moves the - documented floor and the CI floor gate deliberately. - -The `engines` string mirrors Babel 8's own constraint (`^22.18.0` = the -22.18-and-up 22.x line; `>=24.11.0` = the 24.x line that got the relevant -backport) so that no Babel-8 EBADENGINE can reappear on any Node the project -claims to support. `>=22.18.0` would be looser but would re-warn on Node 23.x -and 24.0–24.10, which Babel 8 excludes. - -## 4. Proposed changes - -| File | Change | -| ---- | ------ | -| `gitnexus/package.json` | `engines.node` `>=22.0.0` → `^22.18.0 \|\| >=24.11.0`; remove `@types/uuid` devDependency. Babel stays `^8.0.0`. | -| `gitnexus/package-lock.json` | Mirror both edits (engines + `@types/uuid` entry removed). Hand-applied to avoid npm-version `libc` metadata churn (local npm 10.9.2 strips the `libc` platform arrays a newer npm wrote; those drive musl/glibc optional-dep resolution and must be preserved). Verified consistent via `npm ci` exit 0. | -| `CONTRIBUTING.md` | Prerequisite floor `>=22.0.0` → `^22.18.0 \|\| >=24.11.0`. | -| `.github/workflows/ci-tests.yml` | `node-floor-compat` retargeted 22.14.0 → 22.18.0 (name, comment, pin, version assertion) so the floor gate guards the *new* minimum; containment-canary pin 22.16.0 → 22.18.0. | -| `.github/workflows/gitnexus-review-agent.yml` | Pinned Node 22.16.0 → 22.18.0. | -| `.github/workflows/gitnexus-skill-evolution.yml` | Pinned Node 22.16.0 → 22.18.0. | - -CI lanes using `node-version: 22` (latest ≥22.18) or `24` are already at/above -the floor and unchanged. `CHANGELOG.md` and `package.json` `version` are -release-owned (per repo convention) and deliberately untouched. - -## 5. Implementation sequence - -1. `gitnexus/package.json` + `gitnexus/package-lock.json`: engines bump and - `@types/uuid` removal (one atomic commit). -2. CI + docs: floor-gate retarget, the three pinned-lane bumps, CONTRIBUTING - prerequisite (one atomic commit). - -Both orderings leave the tree coherent; each is `detect_changes`-gated -(config/manifest files carry no indexed symbols → empty affected set). - -## 6. Verification - -- `npm ci` in `gitnexus/` exits 0 (lockfile ↔ package.json consistency after - the hand-edit). -- On local Node 22.16.0 (now *below* the floor), the only EBADENGINE lines are - the nine Babel 8 packages plus `gitnexus` itself, all reporting the identical - `required: ^22.18.0 || >=24.11.0` — i.e. they satisfy together at ≥22.18 and - vanish by construction. No other engine warning; no `@types/uuid` - deprecation. (A true zero-warning run requires Node ≥22.18, unavailable in - this sandbox; CI's ≥22.18 lanes are the authoritative check.) -- Mutation oracle single-fixture run - (`measure.mjs --mutation --only=intra-control-branch --json`) exits 0 on - Babel 8 with a fingerprint identical to the Babel 7 run — the bench consumer - is unaffected. -- `npm run test:unit` passes. - -## 7. Risks - -- **User-facing floor raise** — dropping Node 22.0–22.17 is a support-policy - change. Deliberate and the point of this PR; belongs in the release notes at - release time (not edited here per CHANGELOG convention). -- **Lockfile hand-edit** — mitigated by the `npm ci` exit-0 consistency proof - and by preserving `libc` platform metadata (musl/Alpine native resolution). -- **node-floor-compat** — its original #2372 failure mode (`module.registerHooks` - ≥22.15 on a sub-22.15 floor) can no longer occur at a 22.18 floor; the gate - is retained, retargeted to 22.18.0, to guard the new minimum generally. - -## 8. Definition of Done - -1. `gitnexus/package.json` `engines.node` = `^22.18.0 || >=24.11.0`; Babel at - `^8.0.0`; `@types/uuid` absent. -2. `npm ci` exits 0; no `@types/uuid` deprecation; the only EBADENGINE lines on - sub-floor Node are Babel 8 + gitnexus-self, all with the new required range. -3. Mutation oracle single-fixture run exits 0 on Babel 8. -4. `npm run test:unit` passes. -5. No CI lane pins Node below 22.18; `node-floor-compat` guards 22.18.0. -6. `CONTRIBUTING.md` floor updated; `CHANGELOG.md`/`version` untouched. -7. Diff limited to the six files above (+ this plan).