fix(scripts, install): honor test:fast "without build" promise and orient Node.js engine mismatches (#2026)

* fix(scripts): defer dist/ imports in verifier helpers so test:fast runs without build

Two helpers under scripts/lib/ reached into ../../dist/ at module load even
though the top-level import was only needed by one of their exports. That
turned mocked, dist-free tests into failing tests under `npm run test:fast`,
which docs/testing.md § test scripts explicitly promises runs "without build":

- tests/conformance/providers/hopper/realHopperSemantic.test.ts only calls
  requireCurrentDocument and resolveFixtureProcedure (both mock-only), but
  scripts/lib/real-hopper-semantic.mjs eagerly imported
  requireFunctionDossierOracle from dist/application/RealHopperAssertions.js.
  Moved the dist-dependent dossier oracle into a lazy loader called from
  verifyRealHopperFixture, which is where it is actually used (against the
  real Hopper bridge, after build:cached).

- tests/evaluation/package/packagePlatformDiagnostics.test.ts only uses
  assertLinuxPackageProviderFailure, which depends on `json`, but
  scripts/lib/verify-package-core.mjs eagerly re-exported helpers from
  mcp-verifier-results.mjs, which in turn pulls four dist/ modules. Moved
  that import into verifyCompleteToolCatalog (the only consumer in this
  file). Downstream scripts (verify-recorded-crash.mjs, verify-package-mcp.mjs,
  verify-windows-package.mjs) are unaffected because they already run after
  build:cached.

Reproduction before fix:
  rm -rf dist && npm run test:fast → 2 failed (both import-time errors)

Reproduction after fix:
  rm -rf dist && npm run test:fast → 388 passed, 4924 tests, 0 failed
  npm run build:cached && npm run test:fast → 388 passed, 4924 tests, 0 failed

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(install): orient Node.js version mismatch failures with actionable remediation

install.sh previously told operators "use Node.js 22.x (>=22.19), 24.x (>=24.11),
or 26+" without saying how to install one. The curl|bash onboarding path then
left the operator to guess between nvm, fnm, n, Volta, system packages, or
nodejs.org — a lookup that bounces across search engines for a first-time user.

Replace the one-line failure with a block that:
- Names the detected Node.js version (so the operator confirms it is theirs).
- Shows the three most common install paths (nvm, fnm, direct tarball).
- Pins the Node.js release line (24) that satisfies the engine, so copy-paste
  commands produce a supported runtime instead of whichever "latest" is on top.
- Maps uname -m / uname -s to the Node.js tarball label pair for this host
  (e.g. darwin/arm64, linux/x64), so the operator can grab the right archive
  from https://nodejs.org/dist/latest-v24.x/ without a second lookup.

Verified with four Node.js versions (24.18 compatible, 25.9/20.11 outside the
supported ranges, 22.18 below the minor floor) that remediation triggers only
on real mismatches and names the host labels correctly. shellcheck install.sh
and npm run check:fast remain clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
astralyx0129
2026-10-12 02:54:57 -03:00
committed by GitHub
parent 3bdc8730bd
commit 69e097a8ca
5 changed files with 90 additions and 9 deletions
+16 -2
View File
@@ -86,8 +86,22 @@ harm_rank,location,trigger,current_copy,user_risk,proposed_replacement,implement
77,install.sh:node missing,node is absent,"Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+ is required; install it from nodejs.org and retry.",Low risk; exact versions and recovery,Keep current copy,unchanged,verified by shell branch inspection
78,install.sh:npm missing,npm is absent,npm is required; install it with Node.js and retry.,Low risk; exact recovery,Keep current copy,unchanged,verified by shell branch inspection
79,install.sh:node unreadable,node exists but does not report a parseable version,could not determine the active Node.js version.,No cause-local recovery,"The active Node.js version could not be read. Check that node works and is on PATH, then retry.",implemented,verified by exact unreadable-Node-version installer test
80,install.sh:Node 22 floor,Node 22 minor version is below 19,"Node.js VERSION is unsupported; use Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+.",Low risk; exact detected version and supported alternatives,Keep current copy,unchanged,verified by exact installer test using Node 20 and branch inspection
81,install.sh:Node 24 floor,Node 24 minor version is below 11,"Node.js VERSION is unsupported; use Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+.",Low risk; exact detected version and floor,Keep current copy,unchanged,verified by shell branch inspection
80,install.sh:Node 22 floor,Node 22 minor version is below 19,"Node.js VERSION is unsupported; use Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+.",No install guidance; operator must guess between nvm/fnm/system packages/nodejs.org,"Node.js VERSION is unsupported.
REA requires Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+. Install one via:
nvm: nvm install 24 && nvm use 24 (https://github.com/nvm-sh/nvm)
fnm: fnm install 24 && fnm use 24 (https://github.com/Schniz/fnm)
Direct download for OS/ARCH:
https://nodejs.org/dist/latest-v24.x/ (grab the OS-ARCH tarball)
Then rerun this installer.",implemented,verified by exact installer test using Node 20 against the `linux/x64` fake host and by shell branch inspection
81,install.sh:Node 24 floor,Node 24 minor version is below 11,"Node.js VERSION is unsupported; use Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+.",No install guidance; operator must guess between nvm/fnm/system packages/nodejs.org,"Node.js VERSION is unsupported.
REA requires Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+. Install one via:
nvm: nvm install 24 && nvm use 24 (https://github.com/nvm-sh/nvm)
fnm: fnm install 24 && fnm use 24 (https://github.com/Schniz/fnm)
Direct download for OS/ARCH:
https://nodejs.org/dist/latest-v24.x/ (grab the OS-ARCH tarball)
Then rerun this installer.",implemented,verified by shell branch inspection
82,install.sh:version syntax,Explicit version is not exact semantic version,version must be an exact semantic version.,Low risk; exact correction,Keep current copy,unchanged,verified by shell branch inspection
83,install.sh:release lookup,Latest GitHub release request fails,could not resolve the latest GitHub release.,No fallback despite supporting pinned versions,"The latest REA release could not be resolved. Check network access or pass --version VERSION, then retry.",implemented,verified by exact release-lookup installer test
84,install.sh:release response,Latest release response is not valid expected JSON,Node JSON parser stack followed by the safe release-response message,Leaks a parser stack and buries recovery guidance,The release response was invalid. Retry later or pass --version VERSION.,implemented,verified by exact malformed-release test with parser stderr suppressed
1 harm_rank location trigger current_copy user_risk proposed_replacement implementation_status verification_result
86 77 install.sh:node missing node is absent Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+ is required; install it from nodejs.org and retry. Low risk; exact versions and recovery Keep current copy unchanged verified by shell branch inspection
87 78 install.sh:npm missing npm is absent npm is required; install it with Node.js and retry. Low risk; exact recovery Keep current copy unchanged verified by shell branch inspection
88 79 install.sh:node unreadable node exists but does not report a parseable version could not determine the active Node.js version. No cause-local recovery The active Node.js version could not be read. Check that node works and is on PATH, then retry. implemented verified by exact unreadable-Node-version installer test
89 80 install.sh:Node 22 floor Node 22 minor version is below 19 Node.js VERSION is unsupported; use Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+. Low risk; exact detected version and supported alternatives No install guidance; operator must guess between nvm/fnm/system packages/nodejs.org Keep current copy Node.js VERSION is unsupported. REA requires Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+. Install one via: nvm: nvm install 24 && nvm use 24 (https://github.com/nvm-sh/nvm) fnm: fnm install 24 && fnm use 24 (https://github.com/Schniz/fnm) Direct download for OS/ARCH: https://nodejs.org/dist/latest-v24.x/ (grab the OS-ARCH tarball) Then rerun this installer. unchanged implemented verified by exact installer test using Node 20 and branch inspection verified by exact installer test using Node 20 against the `linux/x64` fake host and by shell branch inspection
90 81 install.sh:Node 24 floor Node 24 minor version is below 11 Node.js VERSION is unsupported; use Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+. Low risk; exact detected version and floor No install guidance; operator must guess between nvm/fnm/system packages/nodejs.org Keep current copy Node.js VERSION is unsupported. REA requires Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+. Install one via: nvm: nvm install 24 && nvm use 24 (https://github.com/nvm-sh/nvm) fnm: fnm install 24 && fnm use 24 (https://github.com/Schniz/fnm) Direct download for OS/ARCH: https://nodejs.org/dist/latest-v24.x/ (grab the OS-ARCH tarball) Then rerun this installer. unchanged implemented verified by shell branch inspection
91 82 install.sh:version syntax Explicit version is not exact semantic version version must be an exact semantic version. Low risk; exact correction Keep current copy unchanged verified by shell branch inspection
92 83 install.sh:release lookup Latest GitHub release request fails could not resolve the latest GitHub release. No fallback despite supporting pinned versions The latest REA release could not be resolved. Check network access or pass --version VERSION, then retry. implemented verified by exact release-lookup installer test
93 84 install.sh:release response Latest release response is not valid expected JSON Node JSON parser stack followed by the safe release-response message Leaks a parser stack and buries recovery guidance The release response was invalid. Retry later or pass --version VERSION. implemented verified by exact malformed-release test with parser stderr suppressed
94 85 install.sh:release tag Latest release tag does not match REA release convention latest release tag is not a valid rea-agents release. Exposes package naming detail and omits fallback The latest release tag was invalid. Retry later or pass --version VERSION. implemented verified by exact invalid-release-tag installer test
95 86 install.sh:npm prefix npm cannot return its global prefix could not determine the npm global prefix. No recovery instruction The npm global prefix could not be read. Repair the npm configuration, then retry. implemented verified by exact macOS npm-prefix installer test
96 87 install.sh:npm install npm global installation exits nonzero npm package installation failed. No likely causes or recovery npm could not install REA. Check registry access and npm permissions, then retry. implemented verified by exact installer test
97 88 install.sh:binary absent npm exits successfully without executable the rea command was not installed at local path Leaks local path and no recovery npm completed without installing the rea command. Check the npm global bin directory and PATH, then retry. implemented verified by exact absent-installed-command test
98 89 install.sh:version read Installed rea executable cannot report its version could not read the installed REA version. No recovery instruction The installed REA version could not be read. Reinstall the requested version, then retry. implemented verified by exact unreadable-installed-version test
99 90 install.sh:version mismatch Installed rea reports a different version installed version ACTUAL does not match EXPECTED. Low risk; gives exact integrity mismatch Keep current copy unchanged verified by exact installer test
100 90.5 @modelcontextprotocol/server:tool input validation Caller sends arguments that violate a declared tool schema Input validation error: Invalid arguments for tool TOOL: FIELD: Invalid input: expected TYPE, received TYPE Low risk; public tool and field names plus exact type correction Keep SDK-generated copy unchanged verified by real in-memory MCP client/server call with wrong argument type
101 90.6 incur:unknown CLI command Caller enters an unknown REA command 'COMMAND' is not a command for 'rea'. Suggested command: rea --help Low risk; identifies input and exact discovery action Keep framework-generated copy unchanged verified by real packaged dispatcher invocation
102
103
104
105
106
107
+33 -1
View File
@@ -25,6 +25,38 @@ fail() {
exit 1
}
# Resolve the current host's Node.js tarball label pair; a label we cannot map
# is kept verbatim so the operator can still read it. Falls back on the raw
# `uname` outputs rather than inventing a label the Node.js release index does
# not publish, so a mismatch is obvious instead of silently wrong.
node_host_labels() {
case "$(uname -m)" in
arm64|aarch64) node_arch_label="arm64" ;;
x86_64|amd64) node_arch_label="x64" ;;
*) node_arch_label="$(uname -m)" ;;
esac
case "$(uname -s)" in
Darwin) node_os_label="darwin" ;;
Linux) node_os_label="linux" ;;
*) node_os_label="$(uname -s | tr '[:upper:]' '[:lower:]')" ;;
esac
}
# Exit with actionable remediation when the active Node.js is unsupported.
# The engine floor stays in sync with package.json#engines.node — change both.
fail_node_version() {
local observed="$1"
node_host_labels
printf 'REA installation failed: Node.js %s is unsupported.\n\n' "$observed" >&2
printf 'REA requires Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+. Install one via:\n' >&2
printf ' nvm: nvm install 24 && nvm use 24 (https://github.com/nvm-sh/nvm)\n' >&2
printf ' fnm: fnm install 24 && fnm use 24 (https://github.com/Schniz/fnm)\n' >&2
printf ' Direct download for %s/%s:\n' "$node_os_label" "$node_arch_label" >&2
printf ' https://nodejs.org/dist/latest-v24.x/ (grab the %s-%s tarball)\n' "$node_os_label" "$node_arch_label" >&2
printf 'Then rerun this installer.\n' >&2
exit 1
}
usage() {
cat <<'EOF'
Usage: install.sh [--version VERSION] [--dry-run] [--no-setup] [--no-prompt] [--verbose]
@@ -78,7 +110,7 @@ main() {
[[ "$node_major" =~ ^[0-9]+$ && "$node_minor" =~ ^[0-9]+$ ]] || fail "the active Node.js version could not be read. Check that node works and is on PATH, then retry."
if ! [[ "$_node_patch" =~ ^[0-9]+(\+[0-9A-Za-z.-]+)?$ ]] ||
! ((node_major == 22 && node_minor >= 19 || node_major == 24 && node_minor >= 11 || node_major >= 26)); then
fail "Node.js $node_version is unsupported; use Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+."
fail_node_version "$node_version"
fi
if [[ -n "$version" ]]; then
+14 -1
View File
@@ -1,5 +1,17 @@
import assert from "node:assert/strict";
import { requireFunctionDossierOracle } from "../../dist/application/RealHopperAssertions.js";
/**
* `requireFunctionDossierOracle` is only used inside `verifyRealHopperFixture`,
* which runs against the real Hopper bridge after `build:cached`. Loading it
* lazily keeps this module importable from `test:fast` (which docs promise to
* run "without build" — see docs/testing.md § test scripts) so that mock-only
* callers such as `requireCurrentDocument` and `resolveFixtureProcedure` do
* not require `dist/`.
*/
const loadFunctionDossierOracle = () =>
import("../../dist/application/RealHopperAssertions.js").then(
(module) => module.requireFunctionDossierOracle,
);
/** Require truthful tri-state permissions from real Hopper memory regions. */
export const requireTruthfulMemoryRegions = (segments) => {
@@ -87,6 +99,7 @@ export async function verifyRealHopperFixture({
oracle,
normalizedResult,
}) {
const requireFunctionDossierOracle = await loadFunctionDossierOracle();
const main = await resolveFixtureProcedure(
client,
options,
+13 -4
View File
@@ -2,10 +2,17 @@ import { execFile } from "node:child_process";
import { lstat } from "node:fs/promises";
import { promisify } from "node:util";
import { Ajv2020 } from "ajv/dist/2020.js";
import {
requireMcpLifecycleResult,
sortVerifierNames,
} from "./mcp-verifier-results.mjs";
/**
* `mcp-verifier-results.mjs` reaches into `dist/` at module load, so eager
* importing it propagates that build dependency to every consumer in this
* file. `verifyCompleteToolCatalog` is the only consumer, so we lazy-load
* the module there and keep the other helpers (`exec`, `run`, `json`,
* `pathExists`) usable from `test:fast` callers that only need `dist/`-free
* utilities — see docs/testing.md § test scripts, which promises `test:fast`
* runs "without build".
*/
const loadMcpVerifierResults = () => import("./mcp-verifier-results.mjs");
/** Promisified child_process execution helper. */
export const exec = promisify(execFile);
@@ -35,6 +42,8 @@ export const json = (text) => JSON.parse(text);
/** Verify schema validity and that the MCP catalog matches session metadata. */
export const verifyCompleteToolCatalog = async (client, options) => {
const { requireMcpLifecycleResult, sortVerifierNames } =
await loadMcpVerifierResults();
const listed = await client.listTools(undefined, options);
const ajv = new Ajv2020({ strict: false, validateFormats: false });
for (const tool of listed.tools) {
@@ -17,6 +17,19 @@ import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
const execFileAsync = promisify(execFile);
// install.sh:fail_node_version emits this recovery block verbatim. The fake
// `uname` in createFixture reports `x86_64` / `Linux`, so the host labels
// resolve to `linux/x64`.
const UNSUPPORTED_NODE_RECOVERY_LINUX_X64 =
"REA installation failed: Node.js 20.0.0 is unsupported.\n" +
"\n" +
"REA requires Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+. Install one via:\n" +
" nvm: nvm install 24 && nvm use 24 (https://github.com/nvm-sh/nvm)\n" +
" fnm: fnm install 24 && fnm use 24 (https://github.com/Schniz/fnm)\n" +
" Direct download for linux/x64:\n" +
" https://nodejs.org/dist/latest-v24.x/ (grab the linux-x64 tarball)\n" +
"Then rerun this installer.\n";
describe("curl installer scenarios", { timeout: 20_000 }, () => {
it("installs only a pinned REA CLI with closed stdin", async () => {
const fixture = await createFixture();
@@ -87,7 +100,7 @@ describe("curl installer scenarios", { timeout: 20_000 }, () => {
"unsupported Node",
["--version", "0.3.0"],
{ FAKE_NODE_VERSION: "20.0.0" },
"REA installation failed: Node.js 20.0.0 is unsupported; use Node.js 22.x (>=22.19), 24.x (>=24.11), or 26+.\n",
UNSUPPORTED_NODE_RECOVERY_LINUX_X64,
],
[
"unreadable Node version",