Repository navigation
Add a Node island conformance lane: every package native, in an island, and under Node - #3159
Conversation
Run every package program three ways (under Node, as its default build, and with each imported package forced into a Node island) and compare stdout, stderr and the exit status. known-failures.json lists every island way that does not match Node; the lane fails on a new, changed or fixed entry, so the list only shrinks with a fix. Co-Authored-By: Claude Code <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
npm/node24-promise-timers with its fixture package in an island fails in most runs and matches Node in some. An entry marked unstable is pinned by category when it fails, passes when the race does not fire, and is kept by an update, so it is added and removed by hand. Co-Authored-By: Claude Code <noreply@anthropic.com>
The workflow turns the lane off for pushes to main; the selector test still expected it on. Co-Authored-By: Claude Code <noreply@anthropic.com>
|
The macOS failures here are one corpus program, |
main added the islands lane (#3176 with #3159): a sixth lane output that pushes to main do not run. This branch added the merge_group trigger and runs a merge queue group as a push to main. The conflicts, in the lanes step of ci.yml, the header of scripts/ci-lanes.mjs and the push test of tests/harness/ci-lanes.test.ts, keep both: a push to main and a merge queue group run every lane except macos and islands. Co-Authored-By: Claude Code <noreply@anthropic.com>
main added the islands lane (#3176 with #3159): a sixth lane output and island rows in tests/harness/ci-lanes.test.ts. This branch added the effect_shards output and turns the effects lane on, for one shard, when a pull request changes compiler or runtime sources. The conflicts keep both: the lanes job has the islands output and the effect_shards output, and the rows main rewrote carry islands with this branch's effects answer. Four of main's new island rows name compiler or runtime sources (the island decision, the boundary lowering, an island runtime unit, the host script), so by this branch's rule they sample the effects lane too. Co-Authored-By: Claude Code <noreply@anthropic.com>
|
The review bot left one finding in a review body here that was not answered before the merge:
Checked on main |
The list was recorded for vercel-labs#3176 at 7dbcdc9. The island crossing changes that merged around it changed what several of its programs do, and the lane has not run on main since: pushes to main do not run it, and vercel-labs#3159 and vercel-labs#3176 were selected by a base revision that predates the lane, so their own island jobs were skipped. At main's head the lane fails on eleven entries with nothing applied. Regenerated with SCRIPTC_ISLAND_CONFORMANCE=update at 4935b78 (Linux x64, a libnode pack built from that commit), and the same list came out at dfa7753: - new text of a failed crossing ("…, got Object from a Node island (an island object stays an island value: …"), same failure otherwise: corpus/7037-js-var-open-writes, npm/hono-zod-default-hook, npm/typed-callbacks, and the default builds of packages/lodash and packages/yaml; - npm/zod3-infer fails at another member, with that text, and npm/any-chaining now stops at "this native object cannot cross into a Node island yet"; - refused at build time with SC8010 instead of failing or diverging when they run: npm/island-json-stringify and npm/zod3-adoption; and npm/zod4-boundary reports SC8010 beside its SC1090; - npm/decl-pass-through with ref-marks in an island matches Node and leaves the list. 86 entries become 85. Only the list changes. Co-Authored-By: Claude Code <noreply@anthropic.com>
A test lane that proves a package behaves the same whether it is compiled natively, run in a Node island, or run under Node, and a list of the cases where it does not.
Why
When scriptc cannot compile a package natively it runs it in an embedded Node. That path is exercised only where a package happens to need it. A comparison with Bun's ahead-of-time compiler forced four packages into islands and one matched Node; a compile-speed scout hit the same family from the other side. scriptc has three ways to run a package and nothing checked that they agree.
What it does
tests/harness/island-conformance.test.tsruns every program three ways:default);island:<package>, the build's--node-island=<package>).It compares stdout, the exit status and (where the program compares it) stderr for every argv list.
The programs (
tests/harness/island-conformance/cases.ts), 234 in all, 482 ways:npm/…, 152), with its argv lists;node_modulesand need no island today (corpus/…, 68);tests/fixtures/island-conformance(packages/…): commander 15, yaml 2.9, lodash 4.18, chalk 6, semver 7.8, debug 4.4, uuid 14, date-fns 4.4, plus a smallprocess-readerpackage that readsprocessstate (argv,env,cwd,exitCode,isTTY), one program per aspect. zod and hono are already in the npm suite.The lane measures the island, so it charges the island only with what the island changes:
The list
tests/harness/island-conformance/known-failures.jsonlists every island way that does not match Node, keyed by program and way, with a category and a signature (the diagnostic codes of a refused build, or the line that names the difference). The lane passes when exactly the listed failures occur. It fails on a failure the list does not have, on a listed failure that changed, and on a listed failure that no longer occurs. So a fix removes its entries in the same change, and nothing else shrinks or grows the list.SCRIPTC_ISLAND_CONFORMANCE=updaterewrites the entries of the programs it ran (the deliberate edit); one racing failure is pinned by category only ("unstable": true).Scoreboard on main
7dbcdc9dcnode tests/harness/island-conformance/scoreboard.mjs --markdown:85 island failures in 79 of 234 programs.
forcedis a package the lane put in an island;automaticis a default build whose own island fails (yaml, lodash).What is behind the numbers:
not-islanded:--node-island=<package>accepted and ignored. Packages scriptc compiles from its own implementation (chalk 5, hot-shots, undici, fs-extra,@hono/node-server), builtin-alias packages, workspace packages compiled from source, types-only packages.exit:JSON.stringify of a runtime handle is not supported yet(Follow the value in island method calls and serialize island objects in JSON.stringify #3124's subject).exit: an island object or array meets a program type outside the compile-time crossing rule and the checked conversion throwsexpected … at $…, got Object: yaml (parse(text) as {…}), lodash (a callback's array parameter), zod and hono cases.exitandstdout): the named run-time refusalthis native object cannot cross into a Node island yet.build: the island build is refused where the native one is not (SC1090 ×4, SC2004 with SC2013 ×2, SC8006 ×2, SC0001 ×2, SC2013 ×1:versions.sort(semver.compare)with semver in an island).process.argv(commander,packages/process-argv; Node islands: fix error properties, process.argv, thenable awaits and work after 'exit'; pin lifecycle behaviour with 40 corpus programs #3123's subject),process.cwd()andprocess.exitCode(packages/process-cwd,packages/process-exit-code).env,pid,platformandisTTYagree.stdout, exit status matching Node: the silent ones. They are listed one by one in the follow-ups file of the fleet.npm/node24-promise-timerswith its fixture package in an island), pinned by category.Of the nine popular packages the scouts tried: chalk 6, uuid and debug match Node in an island; commander, yaml, lodash, semver and date-fns do not; zod, which matched in the study's one program, fails in eight of the npm suite's zod cases.
Where it runs
Measured on one machine (Intel Xeon @ 2.50GHz, 8 vCPU, four shards side by side): 214 to 422 s per shard with cold build caches, about 23 minutes of shard time in all; 67 to 177 s per shard warm.
That is too much for every pull request, and most pull requests cannot move the list. So the lane is a CI lane of its own,
islandsinscripts/ci-lanes.mjs: on when a change touches the island decision (packages/compiler/src/node-islands), the boundary lowering, the island runtime units and host scripts, the libnode bridge, or the lane's own programs and packages; on in the nightly sweep and manual runs; off on pushes to main.island_conformanceinci.ymlruns it in four shards and is part of the required check when its lane is on. Linux x64 only: the list pins that platform's results. WithoutSCRIPTC_ISLAND_CONFORMANCEthe file only checks that every entry names a program and a way the lane runs, so the ordinary test lanes are unchanged. Recorded in DECISIONS.md.For the open island pull requests
The lane job does not run in this pull request: CI takes the lane selector from the base revision, which does not know the
islandslane yet. The list here was produced in a Sandbox at7dbcdc9dc.Merged into one scratch tree with this lane, #3123, #3124 and #3125 (which carries #3093 and #3005) bring the list from 85 to 73: 12 entries go (nine
JSON.stringifyones,packages/process-argv, one checked conversion, one timer), and 16 change to the next failure behind the one they fix. For example commander in an island gets its arguments from #3123 and then fails on the action callback's options record (expected object at $[1], got Object).Whichever lands second, this lane or one of those, regenerates the list (
SCRIPTC_ISLAND_CONFORMANCE=update). After that the lane runs in every pull request that touches island code.Proof
All on Linux x64 Sandboxes (Intel Xeon @ 2.50GHz, 8 vCPU), Node 24.22.1, a libnode pack built from this branch's bridge, base
7dbcdc9dc.827bc093d, where the list was first produced and is identical: all green. 412 ways ran, 70 were skipped (a native difference, or a package the default build already runs in an island)."unstable": true); nothing else moved across the runs.tests/harness/ci-lanes.test.tsandci-stack-scope.test.ts: 65 pass, with the new lane in the selector, the workflow outputs and the required check.SCRIPTC_ISLAND_CONFORMANCEthe file runs one test (the list names only programs and ways the lane runs) and skips the rest.pnpm lintandpnpm format:check: clean.exporton four helpers ofnpm-suite.ts. The zero-cost gate has nothing to compare.🤖 Generated with Claude Code