The "Test new and changed tests when deployed" jobs install Next.js from
the preview tarballs built for the commit under test, but nothing made
them wait for those tarballs to exist. Until #96438 the job was ordered
behind `test-prod` and the other new-test jobs, which delayed it long
enough that the tarballs had usually landed by the time it looked for
them. Now that it starts immediately it frequently fails with "Artifacts
not found for commit ...".
The ordering cannot be expressed as a `needs:` entry, because the
tarballs are published by a different workflow and, for pull requests
from branches in this repository, a different event than the
`build-and-test` run that consumes them. We therefore add a
`wait-for-preview-tarball` job that polls for the tarball instead, and
make both deploy test jobs depend on it. Doing the waiting in one cheap
job keeps the deploy test matrices off their 16-core runners until there
is something for them to install.
The new job is also listed in `tests-pass`. A dependency failure skips
the jobs that need it, and a skipped job does not count as a failure, so
without that entry a tarball that never arrives would have let the
required check pass with the deploy tests never having run.
`test-new-tests-deploy-cache-components` gets the same treatment,
dropping the incidental ordering it still inherited from
`test-cache-components-prod` and picking up the docs-only guard that
`test-new-tests-deploy` already had.
## Summary
Adds one protected `pull_request_target` workflow for advisory automated
review of non-draft, member-authored PRs.
The existing Vercel reviewer runs Codex, Claude, and synthesis with
read-only GitHub access. This base-owned workflow treats that result as
untrusted, validates it, and owns the only GitHub mutation.
## Write boundary
- no checkout, PR code execution, dependency installation, Actions
secrets, or shell interpolation
- globally empty permissions; the job has only OIDC and pull-request
read
- exact repository, event, author association, PR state, base SHA, and
head SHA validation
- final read-only PR revalidation immediately before publishing
- HTML comments stripped, mentions neutralized, token patterns redacted,
and output capped at 48 KB
- a write token is minted only after validation
- exactly one top-level comment `POST` or `PATCH`
- existing comments are eligible only when attributed to one of gh-sts's
immutable `general` GitHub App IDs and begin with the fixed marker
- both actions are pinned to immutable commit SHAs
The workflow intentionally runs once on `opened` or `ready_for_review`;
it does not run on `synchronize`.
## Validation
- `actionlint`
- Prettier
- syntax validation of all three embedded `github-script` programs
- upstream commit verification for both pinned actions
- independent Codex and Claude autoreview
The workflow remains inert until the matching gh-sts policy is approved.
These are equally important if not more important (since they test build
adapters) than next-start tests so we now run them immediately instead
of waiting for test-prod.
They job is now skipped entirely for docs changes. Which saves a few
seconds for docs-only changes.
This is a pretty minor win, but it can save a few seconds per job.
https://github.blog/changelog/2026-06-25-actions-steps-can-now-be-run-in-parallel/
Also remove the installation of `lld` for rustc with `apt-get`. It's
very slow, and rustc should already ship with `lld` these days? The
composite action to install the rust toolchain isn't run often (test
shards download a prebuilt native binary), but `build-native` it can
block other jobs from running and become a bottleneck.
Testing the full `build-and-deploy` job, which uses the rust install
action: https://github.com/vercel/next.js/actions/runs/30138483769
Failing to publish (especially a partial publish) leaves Next.js in a
corrupted state that needs to be addressed immediately. However, we only
created alerts when deploy tests failed which requires a successful
publish.
Now we send special message to the same channel as broken canary and
deploy tests go.
This won't catch a failing deploy-target job. We'll see if deploy-target
also frequently fails/flakes.
CI jobs in `build_and_test` printed a "Cache save failed." warning from
the "Save passed-tests cache" step even when they never run tests. The
step ran for every job with an `afterBuild` command, but
`.next-test-passed.txt` is only ever written by `run-tests.js`, so jobs
like lint, rust-check, or types-and-precompiled warned on every run
because the path never existed.
`run-tests.js` already owns the decision of whether result caching is
active (`profile.cachingEnabled`), so it now signals the workflow
itself: when it opens the passed-tests file (which happens before any
test runs, and only when result caching is enabled), it writes the path
to the `passed_tests_file` step output. The "Save passed-tests cache"
step in `build_reusable.yml` only runs when that output is present and
takes its `path` from it. Jobs that never run `run-tests.js`, and
flake-detection runs (where `NEXT_FLAKE_DETECTION` disables the result
cache), never emit the output and skip the save silently. A job that
fails midway through its tests has already emitted the output, so its
partial file still saves and a genuine warning is still possible.
The `Stats (webpack)` / `Stats (turbopack)` CI jobs have been
intermittently failing on PRs and canary (e.g. [this
run](https://github.com/vercel/next.js/actions/runs/29476089144/job/87563318723)).
The jobs run on arm64 runners, where the `ubuntu:24.04` base image
fetches apt packages from `ports.ubuntu.com`, which is intermittently
unreachable. Since `apt-get update` only warns when index fetches fail,
the Docker build breaks one layer later at `apt-get install` with
"Unable to locate package curl".
This change switches the base image to `buildpack-deps:noble-scm`, which
already ships curl, ca-certificates, and git, so the build no longer
makes any apt calls at all. The `apt-get upgrade` step is dropped along
with it; it was already a silent no-op whenever the mirror was down, and
the ephemeral CI container does not rely on it.
Both `.github/next-stats-action.Dockerfile` (the one CI builds via
`action.yml`) and the duplicate
`.github/actions/next-stats-action/Dockerfile` are updated to stay in
sync.
## Summary
Documents the policy around AI-assisted contributions, for both humans
and agents:
- `.github/pull_request_template.md`: notes that AI use is encouraged
for researching, creating, and reviewing changes, but contributors must
deeply understand their contributions, and PR descriptions from external
contributors must be written by a human.
- `.github/ISSUE_TEMPLATE/1.bug_report.yml`: adds the equivalent note
for bug reports — issue descriptions from external contributors must be
written by a human, though AI may help create reproductions.
- `AGENTS.md`: replaces the old "PR Descriptions" section with two new
sections. "GitHub Pull Requests" distinguishes branch PRs (agents may
write descriptions) from fork PRs targeting `vercel/next.js` (agents may
not). "GitHub Issues, Comments, and Discussions" restricts agent-written
issues, discussions, and comments to members of the
`vercel`/`vercel-labs` GitHub orgs, tells agents how to check membership
via the GitHub API, lists what agents can still help non-members with
(drafting details, reviewing, reproductions, translation, searching for
duplicates), and carves out exceptions (commenting on the user's own PR,
Vercel-operated bots, GitHub/Graphite review bots, and forks of the
repo).
- Renames the LLM watermark marker from `NEXT_JS_LLM_PR` to
`NEXT_JS_LLM` everywhere it appears (`AGENTS.md`,
`contributing/repository/pull-request-descriptions.md`, and the
`create-pr`/`backport-pr` skills), since it now also applies to issues,
discussions, and comments. Nothing in the repo consumes the old marker
name.
- `.agents/skills/create-pr/SKILL.md`: adds a "Fork PRs vs Branch PRs"
section referencing the `AGENTS.md` policy. Assorted typo fixes along
the way.
## Verification
- `gh api /user/memberships/orgs` and `gh api
orgs/vercel/members/<login>` (confirmed the documented membership checks
work and return the documented statuses)
- Not run: no test/build commands (docs-only change; prettier applied
via the pre-commit hook)
<!-- NEXT_JS_LLM -->
The hypothesis is that this is just installing some font stuff that
isn't actually needed for Chromium to work:
```
The following additional packages will be installed:
libnss3-tools xfonts-encodings xfonts-utils
Recommended packages:
fonts-ipafont-mincho fonts-liberation-sans-narrow fonts-tlwg-loma
The following NEW packages will be installed:
fonts-freefont-ttf fonts-ipafont-gothic fonts-liberation fonts-tlwg-loma-otf
fonts-unifont fonts-wqy-zenhei xfonts-cyrillic xfonts-encodings
xfonts-scalable xfonts-utils
The following packages will be upgraded:
libnss3 libnss3-tools
2 upgraded, 10 newly installed, 0 to remove and 35 not upgraded.
```
`apt` is very slow, so skipping this step should significantly reduce
setup time per test shard.
It seems like this isn't always true for other browsers, so only skip
this for Chromium (that's fine, that's what most of our jobs use
anyways).
On 16 core arm runners, "Install Playwright Browsers" typically took
18-24 seconds. Now it takes 5-6s.
Let's estimate that saves about 15s per Linux job that installs
Chromium. Claude estimates that there are 73 chromium-only Linux jobs on
this PR:
<img width="891" height="148" alt="Screenshot 2026-07-06 at 10 30 16 PM"
src="https://github.com/user-attachments/assets/725893fa-9f6f-4806-a3b4-9858ab0ebe66"
/>
So that's 15*73/60=18.25 minutes saved per PR push (the average PR gets
many pushes). At the public (before any volume discounts) price of
$0.026/minute for the 16 core arm runners, that comes out to $0.4745 per
PR push saved.
This workflow has had 3,894 runs in the last 30 days, so that could be
$1,847/mo saved, but many of those workflow runs were canceled, and some
likely failed before getting to these jobs, so the actual savings are
probably somewhere around half of that -- scroll through
https://github.com/vercel/next.js/actions/workflows/.github/workflows/build_and_test.yml
and see that a little less than half of the workflow runs end up
canceled.
We used to not post a comment when a part of the bundler matrix got
cancelled. Conceptually, a cancelled job is just like a failed on in the
sense of not being able to produce stats.
Now we always aggregate stats and post stats from the bundlers that did
produce them.
The risk here is a race between a run that was cancelled because another
run superseded it and the run that superseded it. Though practically,
aggregating the stats isn't slower than running the full workflow. Not
impossible (especially given how slow the Webpack job is) but incredibly
rare.
This is the first change in a series that will let `next dev` cancel a
Server Components HMR refresh once a newer refresh supersedes it. Here
we only introduce the `experimental.serverComponentsHmrCancellation`
flag and plumb it to every place later changes will read it, without
adding any behavior yet, so on its own this change is an inert no-op.
The flag is declared in `ExperimentalConfig`, validated in the Zod
schema, and defaults to `false` in `defaultConfig`. We thread it into
the runtime config via the `NextConfigRuntime` pick list and
`getNextConfigRuntime`, expose it to user-bundled client code through
`define-env` as `process.env.__NEXT_SERVER_COMPONENTS_HMR_CANCELLATION`,
and pass it into the app render options in `base-server.ts` and the
`app-page` build template. The edge SSR template hardcodes it to
`false`, because edge rendering does not expose the Node response-close
signal the cancellation relies on.
So that the following changes are exercised as soon as their behavior
lands, `config.ts` honors a
`__NEXT_EXPERIMENTAL_SERVER_COMPONENTS_HMR_CANCELLATION` environment
variable that forces the flag on while still respecting an explicit user
config value, mirroring how `cachedNavigations` is rolled out. Both the
`test-cache-components-dev` and `test-cache-components-prod` CI shards
export that variable: the development shard so the feature is exercised
once its behavior lands, and the production shard as a guard that this
development-only feature stays inert in production even with the flag
enabled. The default stays `false`; a later change flips it once the
implementation is complete and drops the shard overrides.
Instead of hardcoding the list, we now automatically detect any
`.browser` sibling and create a compiler alias for that module.
Together with a linting rule, we'll ensure no future regressions are
handled automatically (detection of conditional require and missing
.browser sibling).
This isn't the smoothest DX yet. Ideally we'd generate the list at
Next.js compile time (i.e. before publishing) to avoid having to rerun a
script (I'll follow-up). Generating the list during build time (next dev
or next build) is just another tiny cost users would have to pay.
This is embarrassing, I wasn't paying enough attention when I put up
#95374. We have to both change the build dependency *and* switch the
machine type...
This should be a more robust version of
https://github.com/vercel/next.js/pull/95332 that re-introduces some
x86-64 runners, but also properly segments the artifact cache.
Even if we don't end up merging this, this does some cleanup of
`uploadNativeArtifact` that might be useful to pull out into a separate
PR.
On #95332, the `fetch-test-timings` job failed. That caused downstream
jobs to get a "skip" state, instead of a failure state, which then
caused `tests-pass` to succeed. This is because if a dependency job
fails, GitHub's default behavior is for the downstream job to skip, and
to not cascade the failure.
Depending on all of these helper jobs seems like the simplest way to
prevent this from happening in the future.
A 16 core x86-64 runner is $0.042/minute. A 16 core arm64 runner is
$0.026/minute.
https://docs.github.com/en/billing/reference/actions-runner-pricing
It also looks like they're faster! After factoring the performance
improvement, this appears to be a ~50% cost reduction for migrated jobs.
Datadog SQL:
```
SELECT
REGEXP_REPLACE(ci_job_name, '\s*\([0-9]+/[0-9]+\)', '') AS ci_job_name,
labels,
branch,
commit_date,
SUM(duration) / 1000 / 1000 / 1000 / 60 as minutes,
ci_pipeline_id
FROM dd.ci_pipelines(
columns => ARRAY ['@git.commit.committer.date', '@ci.pipeline.id', '@duration', '@git.branch', '@ci.node.labels', '@ci.job.name'],
event_type => 'job',
filter => '@git.repository.id_v2:github.com/vercel/next.js @ci.pipeline.name:build-and-test @ci.provider.instance:github-actions @ci.pipeline.id:(28266192824-1 OR 28266190122-2) -@ci.job.name:*changed*',
from_timestamp => now() - INTERVAL '30 days',
to_timestamp => now()
) AS (
commit_date VARCHAR,
ci_pipeline_id VARCHAR,
duration BIGINT,
branch VARCHAR,
labels VARCHAR,
ci_job_name VARCHAR
)
GROUP BY
REGEXP_REPLACE(ci_job_name, '\s*\([0-9]+/[0-9]+\)', ''),
labels,
commit_date,
branch,
ci_pipeline_id
ORDER BY
REGEXP_REPLACE(ci_job_name, '\s*\([0-9]+/[0-9]+\)', ''),
branch,
commit_date,
minutes;
```
https://app.datadoghq.com/ddsql/editor?ddsql_dataset_id=34de66b9-30f4-48dd-ac98-354dc7e67fdf
Full Data:
https://docs.google.com/spreadsheets/d/1SszR3RsPeJ13nVW5ggMakwy87fkQMqWMHbZDehHH8IA/edit?usp=sharing
Here's the abbreviated data for jobs longer than 5 minutes:
<img width="1187" height="656" alt="Screenshot 2026-06-26 at 4 38 00 PM"
src="https://github.com/user-attachments/assets/9341039e-c4ea-405c-8d24-cdbc5ed1ba24"
/>
It seems like for the jobs switched over to arm64, they run in about
~73-82% of the time as arm.
A `pnpm install` installs native bindings for Next.js by default. This
makes sense for local development where half of the team doesn't work on
native code and just needs the most recent bindings. However, CI almost
exclusively wants to test with bindings that are built from the current
commit. The rest doesn't need native bindings at all.
The only exception is `test_examples` which is likely not functional.
Though we still support it by allowing to opt-into automatically
installing native bindings in CI.
## test plan
- [x] fetch test timings still skips:
https://github.com/vercel/next.js/actions/runs/28087707205/job/83157705446?pr=95114#step:7:228
Add a `preview` release type so `@preview` npm releases can be cut
**ad-hoc from `canary`** via the `Trigger Release` workflow, retiring
the dedicated long-lived preview branch. The GitHub environment has been
updated accordingly.
A preview cut produces **two commits**, pushed in a single update:
1. **Bump-to-preview** — all packages set to the computed preview
version, tagged `v…-preview.N`. This tag's build publishes `@preview`
(relies on #95085).
2. **Revert-to-canary** — restores the `-canary.N` versions so `canary`
keeps advancing its own line and isn't stranded on a preview version.
```
B canary HEAD before cut (e.g. 16.3.0-canary.61)
│
P v16.3.0-preview.N ← tag here; tag build publishes @preview
│
R canary HEAD after cut (versions restored to 16.3.0-canary.61)
```
### Preview version numbering
`base = max(canary major.minor.patch, npm @preview major.minor.patch)`:
- base still on the published preview line → continue it (`n + 1`)
- canary advanced to a higher base (or no `@preview` published) → reset
to `.0`
So if `canary` jumps to `18.0.0-canary.x` since the last
`16.3.0-preview.x`, the next cut is `18.0.0-preview.0`, not a
continuation.
## Test plan
The dry mode of `start-release.js` was extended to intercept the GH
requests and log the relevant data.
```console
$ node ./scripts/start-release.js --release-type preview --dry-run
Dry run: keeping commits locally, skipping git push and GitHub release creation
Running pnpm release-preview...
[...]
> lerna version 16.3.0-preview.4 --force-publish -y --no-push --allow-branch '**'
[...]
Creating GitHub-signed release commit(s) for v16.3.0-preview.4 from local commits 0f5c1e865cc34edb257a16823ed6525d9cd58bfc..0062348c411776271fe7eac64e97b8bad2775b77
[dry-run] GitHub API POST /repos/vercel/next.js/git/blobs [...]
[dry-run] GitHub API POST /repos/vercel/next.js/git/commits {"message":"v16.3.0-preview.4","tree":"0000000000000000000000000000000000000016","parents":["0f5c1e865cc34edb257a16823ed6525d9cd58bfc"]}
[dry-run] GitHub API POST /repos/vercel/next.js/git/commits {"message":"Restore canary version 16.3.0-canary.61 after v16.3.0-preview.4 preview release","tree":"000000000000000000000000000000000000002d","parents":["0000000000000000000000000000000000000017"]}
[dry-run] GitHub API POST /repos/vercel/next.js/git/refs {"ref":"refs/tags/v16.3.0-preview.4","sha":"0000000000000000000000000000000000000017"}
[dry-run] GitHub API PATCH /repos/vercel/next.js/git/refs/heads/sebbie/preview-release-from-canary {"sha":"000000000000000000000000000000000000002e","force":false}
Dry run: skipping local branch sync; would set sebbie/preview-release-from-canary to 000000000000000000000000000000000000002e and tag v16.3.0-preview.4 at 0000000000000000000000000000000000000017
Created GitHub-signed release tag v16.3.0-preview.4 at 0000000000000000000000000000000000000017; branch sebbie/preview-release-from-canary now at 000000000000000000000000000000000000002e
Dry run: skipping GitHub release creation
Release process is finished
```
That way we can push multiple commits when doing a release. For
canaries, nothing will change.
In a follow-up, we'll start cutting `@preview` from the `canary` branch.
To support that case, we need to immediately revert the version in
`lerna.json`. We shouldn't push commits indidually to avoid having an
incomplete release if something else is pushed between `@preview`
version bumps and `@canary` version reverts.
## test plan
After merge, the next canary release should publish from the
`v…-canary.N` tag run (`deploy-target = production`), while the canary
branch push for the same commit shows `skipped` — exactly one production
run per release.
<!-- NEXT_JS_LLM_PR -->
Because it's not sure which one you'll want, playwright installs TWO
copies of Chromium by default. A full copy and a stripped down headless
version.
Most of the install time is spent in `apt-get`/`dpkg`, but this at least
buys us a few seconds back.
Before:

After:
<img width="1700" height="552" alt="Screenshot 2026-06-16 at 6 24 50 PM"
src="https://github.com/user-attachments/assets/d382cb8e-a875-4d67-92d6-32ff51c06488"
/>
Playwright also installs ffmpeg in case you want to perform a screen
recording (we don't), but it's much smaller (2.3 MiB), and there doesn't
appear to be a way to disable it.
https://github.com/vercel/next.js/pull/94622 tried to be more strict
with when a release is considered healthy. However, `@next/swc-wasm-web`
is not enrolled in Trusted Publishing and blocks the release train.
This PR restores the behavior for `@next/swc-wasm-*` packages prior to
https://github.com/vercel/next.js/pull/94622 i.e. we continue the
release train even if any `@next/swc-wasm-*` fails. Other NAPI bindings
will continue to block the release train if they fail to publish.
The bail vs no-bail behavior is controlled with a repository variable so
that we can mor easily test if `@next/swc-wasm-web` is enrolled in
Trusted Publishing.
In ci when testing our benchmarks use our test profile
`release-with-assertions`
This avoids an expensive `lto` step and ensures that our tests run with
debug asserts.
I noticed that our workflow for 'test cargo benches' was very slow and a
lot of that time was the build presumably due to lto overheads across so
many benchmark binaries.
Compare:
* cargo benches for this pr:
https://github.com/vercel/next.js/runs/79974366863?pr=94538
- build=4m42s
- test=1m46s
* cargo benches before this pr:
https://github.com/vercel/next.js/actions/runs/27097792020/job/79973082715#logs
- build=8m23s
- test=6m56s
Of course doing this revealed a few things
* a birthday paradox panic in one of the persistence tests
* some 'top level read' errors to address in the benchmarks and in the
turbopack-cli server used for some benchmarks.
And finally, that the persistence tests are just extremely slow in test
mode due to their initialization overhead (populating >1GB dbs), so that
harness is rewritten to remove some variants and also to reduce max
sizes in test mode
I noticed the `node-version-file` option for `setup-node` while
reviewing https://github.com/vercel/nextjs-react-compiler/pull/2
We should use it in places where we're already checking out the
repository anyways. It should make it easier to keep node versions in
sync.
If `binstall` fails to download a precompiled artifact it falls back to
`cargo install` and many tools (like `nextest`) require the `--locked`
flag. So pass the `--locked` flag.
## Summary
- upgrade `@rspack/core` to `2.0.0-rc.0`
- sync the in-tree next rspack Rust binding/toolchain with newer rspack
releases
- align Next server runtime selection so `NEXT_RSPACK` uses the
turbo-like app runtime path
- include missing compiled `@next/react-refresh-utils` output in
`ncc-compiled`
## What is done
- `@next/rspack` Rust binding builds locally
- `packages/next` builds locally
- rspack-related tests are running and have been reduced to deeper
runtime compatibility failures
## Rspack test investigation
Latest checked CI run: `build-and-test` run `27129243230` at head
`484e7bf1b30655a9f94d0855b9e00774b4b38cd0`.
Confirmed Rspack / webpack parity gaps:
-
`test/e2e/edge-runtime-configurable-guards/edge-runtime-configurable-guards.test.ts`:
Rspack does not expose the webpack parser hooks used by
`MiddlewarePlugin`. The current Rspack path falls back to SWC analysis
in `finishModules`, which can report diagnostics but cannot apply the
parser-time transforms that wrap `eval` / `new Function`, track
`InnerGraph` usage for used vs unused dynamic code, or apply
`unstable_allowDynamic` with webpack parity. Dev therefore misses the
expected dynamic-code runtime warnings and also reports false
`process.cwd` Edge warnings from Next internals; prod fails
allowed/unused dynamic-code cases.
-
`test/development/middleware-overrides-node.js-api/middleware-overrides-node.js-api.test.ts`:
same parser-hook limitation. Webpack can observe that `process.cwd` is
overwritten before use, while the Rspack SWC fallback statically flags
`process.cwd` as an unsupported Edge Node API.
- `test/production/css-features/css-compilation.test.ts`: the CSS is
minified and prefixed as expected, but the Rspack lightningcss path
currently does not expose/control source map generation the way this
test expects, so emitted CSS has no `sourceMappingURL` comment or `.map`
file to assert.
- `test/e2e/app-dir/server-source-maps/server-source-maps.test.ts`:
invalid source maps are not handled with the same graceful diagnostic as
webpack/Node source-map handling. Rspack logs the raw `webpack-internal`
frame for the damaged map case instead of surfacing the expected
`Invalid source map. Only conformant source maps...` message with the
cause.
-
`test/e2e/app-dir/parallel-routes-revalidation/parallel-routes-revalidation.test.ts`:
raw CI output observed a dev-mode timeout waiting for `networkidle`
after refresh/back/forward lazy fetching. This was not the final
structured failure in the latest run, but it remains a Rspack dev parity
follow-up around router cache lazy fetches and network settling.
- `test/e2e/middleware-src/middleware-src.test.ts`: after the test adds
root middleware files, Rspack dev still lets `src/middleware`
participate; the expected behavior is that only the root middleware
runs.
- `test/e2e/env-config/env-config.test.ts`: changing `.env` logs `Reload
env:` but the client keeps the previous `NEXT_PUBLIC_` value, pointing
at an Rspack dev env invalidation/HMR gap.
-
`test/e2e/app-dir/use-cache-without-experimental-flag/use-cache-without-experimental-flag.test.ts`:
after enabling the `useCache` flag, the dev server does not finish
restarting, pointing at build-error recovery/restart parity.
-
`test/development/app-dir/server-navigation-error/server-navigation-error.test.ts`:
middleware navigation-error cases hit `ERR_CONNECTION_REFUSED`,
consistent with the Rspack dev server exiting or restarting unexpectedly
during those error flows.
- `test/e2e/opentelemetry/instrumentation/opentelemetry.test.ts`: the
custom-server dev process exits before the SDK handle is initialized,
then cleanup fails on `shutdown`; this needs follow-up in Rspack
dev/custom-server startup.
- `test/e2e/next-image-new/app-dir/app-dir.test.ts`: after toggling
image source, `onLoadingComplete` still reports the previous image
dimensions/source, pointing at an Rspack dev image/HMR or asset
invalidation parity issue.
- `test/e2e/node-cli-args/node-cli-args.test.ts`: on Node `20.19.x`,
`node --experimental-network-inspection` resolves instead of rejecting.
This looks like a Node-version behavior change rather than an Rspack
compiler issue.
Not attributed to this Rspack upgrade:
-
`test/e2e/app-dir/instant-navigation-testing-api/instant-navigation-testing-api.test.ts`
was marked by `scripts/pr-status.js` as a known flaky test across
branches.
-
`test/production/app-dir/build-output-prerender/build-output-prerender.test.ts`
ran in the webpack prod job and only differs by prerender error ordering
in the inline snapshot.
<!-- NEXT_JS_LLM_PR -->
---------
Co-authored-by: Benjamin Woodruff <benjamin.woodruff@vercel.com>
I determined that [`nvm` wasn't sufficient for our use-case since we
need to support
windows](https://github.com/vercel/next.js/pull/94175#issuecomment-4654622674),
so this doubles down on `fnm`.
This reduces risks of supply chain attacks with `fnm`.
This also cleans up some old stuff that was left over from self-hosted
runners and/or that was there for debugging, if it isn't useful anymore.