Full motivation and plan here:
https://app.notion.com/p/vercel/Turbopack-pnpm-Global-Virtual-Store-383e06b059c480579403ddfd71cc2d40?source=copy_link
The goal is to allow `DiskFileSystem` to traverse outside of it's own
root to other configured `DiskFileSystem`s when following symlinks. We
may allow traversal in other situations in the future, but this is
limited to symlink resolution for now.
## Global Virtual Store
The motivation for this is to enable [pnpm's Global Virtual Store
feature](https://pnpm.io/global-virtual-store) (and there are other
package managers doing this, including nub and bun).
We'd expose the ability to manually configure this in `next.config.js`,
but we should also auto-configure ourselves for popular package managers
(or at least make a best effort to do so, the `PNPM_HOME` semantics can
be complicated). The `ignoreIfMissing` option is provided for this
situation: We can configure a bunch of roots automatically, and they
only actually get set up if they exist, the check for directory
existence is cheap.
## NFT changes
This requires a couple extensions to the `*.nft.json` file format:
https://github.com/vercel/next.js/pull/98469
## Related Issues
- #93556
- https://github.com/pnpm/pnpm/issues/14972
### What?
Adds a CI job that runs selected Rust unit tests under
[Miri](https://github.com/rust-lang/miri) to detect undefined behavior
in unsafe code.
The job currently covers the low-level crates that are compatible with
Miri:
- `turbo-persistence`
- `turbo-rcstr`
- `turbo-tasks-malloc`
It also makes those crates Miri-compatible by:
- using provenance-free tagged-pointer operations in `turbo-rcstr`;
- disabling mimalloc and native compression under Miri;
- making mmap support a default-enabled Cargo feature that is disabled
for Miri and wasm;
- running persistence through file I/O when mmap is disabled;
- skipping only tests measured to exceed the Miri time budget;
- removing leaked test arenas from the analyzer predicate tests.
### Why?
Miri can detect invalid memory access and other undefined behavior that
normal Rust tests may not expose. Running it in CI gives low-level
unsafe code an additional correctness check.
The job uses an explicit package allowlist because Turbo Tasks builds
its generated registry from linker sections, and Miri does not support
the linker-defined section symbols required by that registry.
### How?
- Installs the `miri` Rust component in CI and the development
container.
- Adds a dedicated `test-cargo-unit-miri` task and reusable workflow
configuration.
- Makes `memmap2` optional behind the default `mmap` feature and
disables that feature in Miri CI and the wasm dependency graph.
- Uses structural `cfg(miri)` branches for allocator and compression
paths Miri cannot execute.
- Re-enables three persistence compaction tests after measuring them
successfully under Miri.
- Keeps normal native behavior unchanged and documents measured Miri
exclusions at the affected tests.
Verification included focused normal and Miri tests for persistence,
rcstr, allocation accounting, compression, and the refactored leak-free
predicate cases.
<!-- NEXT_JS_LLM -->
<!-- fleet fb42942f-173a-484c-b4de-4e18354834c6 -->
---------
Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
Removes the `Test examples` workflow and its orphaned test suite. The
workflow fails every scheduled run: its pinned Playwright docker image
(v1.35.1) is incompatible with the Playwright version the repo now
requires (v1.61.0), so the browser cannot launch. It also no longer runs
what it was created for: since the `run-tests.js` glob root moved from
`test/` to the repository root, the `--type examples` string-prefix
filter `examples/` matches test files inside example apps instead of
`test/examples/examples.test.ts`, leaving that suite without any runner.
This deletes `.github/workflows/test_examples.yml`, the `test/examples/`
suite, and the `--type examples` filter in `run-tests.js`. The
`examples/` prefix stays in the default-run exclusion list so test files
inside example apps (which run with each example's own jest/vitest
setup) are still not picked up by bare `run-tests.js` invocations.
Co-authored-by: Claude Code <noreply@anthropic.com>
The manifest is no longer considered for skipping test since
https://github.com/vercel/next.js/pull/81170. It also didn't update
anymore automatically so I removed updating areweturboyet.com which was
broken for a while now anyway when we switched away from static tokens.
Became orphaned in https://github.com/vercel/next.js/pull/97792. Another
script and the test harness needed some utils from `next-stats-action`
so I moved that into `test/lib/link-packed-packages`.
### What?
Adds a CI job that checks Turbopack compiles for
`wasm32-wasip1-threads`, so wasm portability
regressions are caught rather than rediscovered.
### Why?
Everything under `#[cfg(target_family = "wasm")]` is invisible to host
builds **and** to host clippy —
it is only checked when you deliberately build for the target. Four
separate defects in this stack were
caught only that way (a wrong build-script condition, two bad imports,
and a value that compiled but was
wrong). Without a gate, the next one lands unnoticed.
This also re-enables coverage that had been off for ~2 years: the old
`test-next-napi-bindings-wasi` job
was disabled with `if: false` pending napi-rs/napi-rs#2009, which closed
in April 2024.
### How?
Modelled on `rust-check` via `build_reusable.yml` (`needsRust`,
`skipInstallBuild`, `skipNativeBuild`),
so it does not pay for a JS build. Beyond `rustup target add` it needs
two things:
- **a WASI C toolchain**, because `lzzzz` (LZ4, via `turbo-persistence`)
and `zstd-sys` have C build
scripts. The SDK build is selected from `$RUNNER_ARCH` —
`build_reusable.yml` defaults to an arm64
runner, and an x86_64 clang fails there with `Exec format error` — with
a pinned sha256 per arch,
unpacked under `$RUNNER_TEMP` so the workspace stays clean.
- **emnapi**, because `next-napi-bindings`' build script calls
`napi_build::setup()`, whose wasi path
panics without `EMNAPI_LINK_DIR` — so it is required even for `cargo
check`. It is installed into a
scratch directory rather than the root `package.json`, because this job
runs with `skipInstallBuild`
and therefore never runs `pnpm install`.
Two things worth recording, both of which cost a CI round trip to find:
- pnpm is invoked from the repo root with `--dir`, not by `cd`-ing into
the scratch directory: corepack
resolves the pnpm version from the nearest `package.json`, and outside
the repo it picks the latest
pnpm (11.x), which cannot run on the pinned Node 20
(`ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING`).
- the job is added to `tests-pass`, whose `needs:` list is what actually
blocks a PR — a job that runs
but is absent from that list looks like coverage while blocking nothing.
The `emnapi@2.0.0-alpha.4` pin is deliberate and is the one fragility
here: the archive must define
`emnapi_create_env` / `emnapi_delete_env`, which exists only in emnapi
v2, still a prerelease. Move to
the stable release once it ships.
<!-- fleet b6d0486f-97c7-42a7-bdaf-3490774cdec3 -->
Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Tobias Koppers <1365881+sokra@users.noreply.github.com>
Change detection (`git diff HEAD~1`) only covers the whole change set
when the checkout is the PR merge commit. `build-and-test` runs on
`pull_request` while `build-and-deploy` ran on `push` for upstream PR
branches, so a multi-commit push whose last commit was docs-only made
`build-and-deploy` skip the preview build while deploy tests in
`build-and-test` waited up to 30 minutes for a preview tarball that
never got published.
`build-and-deploy` now triggers on the same events as `build-and-test`
(`push` to the default branch and `pull_request`), while still accepting
release tags and workflow dispatch. The `deploy-target` fork guard is
removed since upstream PRs are no longer covered by a push run, and
`create-release-branch.js` now rewrites the workflow's branch list
alongside the existing `refs/heads/canary` replacement so release
branches keep their staging deploys.
Preview tarball creation now uses the PR head sha instead of falling
back to the merge-commit sha on `pull_request` `opened` events
(`github.event.after` only exists on `synchronize`), matching the sha
that `upload_preview_tarballs.yml` publishes under and that tests poll
for. This fallback was a pre-existing bug that only affected fork PRs;
it would have started affecting every upstream PR's first run once
previews moved to the `pull_request` event.
## Summary
- enable the existing auto-close workflow once per hour
- keep manual dispatch available
- leave the workflow logic and permissions unchanged
## Verification
- production `workflow_dispatch` succeeded end to end:
https://github.com/vercel/next.js/actions/runs/33788887152
- production returned a clean empty-queue response after OIDC
authentication and Vercel Trusted Sources validation
- Prettier and `git diff --check` pass
## Summary
- add a manually dispatchable workflow; the hourly schedule is left
commented until initial production verification
- keep the workflow YAML small and place delivery in
`.github/scripts/next-maintainer-auto-close.js` for normal code review
- use a short-lived exact-audience GitHub Actions OIDC token with no
long-lived secret
- trust the authenticated queue contract instead of reimplementing its
Zod validation in the workflow
- post the verifier-authored comment and close with GitHub native
completed, not planned, or duplicate state reasons
- retain one invisible marker only to prevent duplicate public comments
across retries
If an issue is open with the marker, the workflow leaves it open. This
intentionally lets a human reopen win and avoids timeline
reconstruction.
## Permissions
The job grants only `contents: read`, `id-token: write`, and `issues:
write`; every other permission remains none. Both GitHub actions are
pinned to full commit SHAs. Checkout is sparse to the one trusted
JavaScript file and has credential persistence disabled.
## Verification
- mocked delivery harness passes ten scenarios: empty queue, completed,
not planned, duplicate, pull-request rejection, already-closed recovery,
independent close, open marker, transferred issue, and transient failure
- `node --check`, Prettier, and ESLint pass for the extracted
implementation
- Vercel Agent Review, Vercel Security Review, Socket Security, workflow
change detection, and documentation validation pass
- after merge, dispatch the registered workflow on `canary` and add the
production run link here before enabling the hourly schedule
## Dependency
This is the narrow GitHub write-side companion to
vercel-labs/next-maintainer-agent#541, which is deployed.
vercel-labs/next-maintainer-agent#546 further reduces the queue DTO and
changes the delivery limit to 25 first claims per rolling week; this
workflow is compatible with both DTO versions.
The last remaining `Lock Threads` failures are likely due to missing
permissions for discussions which `dessant/lock-threads` handles by
default.
This disables discussion handling to get the workflow green to start
tracking regressions. We can discuss if we want to lock inactive
discussions in a follow-up (which would be new behavior this workflow
never performed).
Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com>
This has devlow use `commander` to parse its arguments.
There's a breaking change here to make it fit better into `commander`'s
patterns without extra code. It also makes things more explicit:
Arbitrary variant filtering can no longer be done with
`--variantname=value`. Now that there are more flags, these share the
namespace with these, so instead we use `--filter variantname=value`, or
`-F variantname=value`.
Co-authored-by: vercel-fleet-prod[bot] <318278635+vercel-fleet-prod[bot]@users.noreply.github.com>
Co-authored-by: Will Binns-Smith <755844+wbinnssmith@users.noreply.github.com>
The `popular` workflow posted weekly Slack digests of the most-reacted
issues, PRs, and feature requests. That reporting is now handled by the
Next.js maintainer agent, so this PR deletes the workflow along with its
backing `next-repo-actions` action (sources and checked-in ncc bundles),
which had no other consumer, and regenerates the `.github` lockfile
without the action's dependencies.
Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com>
Code freezes are now managed by toggling the existing `code freeze`
repository ruleset in the repository settings, so the `code_freeze`
workflow is no longer needed. It was also broken beyond repair: it
targeted the legacy branch-protection API with a `CODE_FREEZE_TOKEN`
secret that no longer exists instead of the rulesets the repository uses
now, and it referenced a `releaseType` input that was never declared.
This PR deletes the workflow and `scripts/code-freeze.js`, which had no
other consumer.
Note on the investigated alternative: resurrecting the workflow on top
of the rulesets API with just `GITHUB_TOKEN` is not possible. Creating
or updating repository rulesets requires the "Administration" repository
permission, which the workflow token cannot be granted (`permissions:
administration: write` is rejected as an invalid workflow, and even
`write-all` gets `403 Resource not accessible by integration`). Verified
empirically in
https://github.com/eps1lon/github-actions-repository-dispatch-admin-permissions.
Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com>
The `issue_wrong_template.yml` workflow closed issues labeled `please
use the correct issue template`. That label was only ever applied
manually by maintainers and has not been used since January 2025 (9
issues total, ever), so every recent run of the workflow was skipped.
This change removes the workflow along with its backing
`wrong-issue-template` action (source, checked-in ncc bundle, and build
script).
Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com>
The `issue_stale` workflow ran `actions/stale` once a day to mark issues
with no activity in 545 days (~1.5 years) as stale and close them a week
later, and to auto-close issues labeled `please add a complete
reproduction` (2 days), `please simplify reproduction` (14 days), or
`please verify canary` (14 days). Marking and closing issues based on
inactivity is no longer wanted, so the workflow is deleted.
The `STALE_TOKEN` secret has already been revoked.
Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com>
Follow-up to #97256, pairing with
[vercel/vercel-packages#107](https://github.com/vercel/vercel-packages/pull/107).
The token exchange endpoint no longer returns a client-upload token
derived from a store read-write token; it presigns the upload URL with
the deployment's own Vercel OIDC identity and returns the URL, pinning
the pathname, expiry, size cap, and access and overwrite policy
server-side. No store credential exists anywhere after this lands, and
no Blob SDK is needed here for the new path — the script PUTs the
tarball bytes to the URL directly.
The client only uses presigned-URL. The server still supports client
tokens until we landed this and synced the mirror.
Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com>
The previous detection mechanism special-cased `canary` which lead to
large diffs when backport branches ran CI.
We stop special casing `canary` reducing complexity and giving us
another flake-detecting attempt when the change is merged.
That way we can safely enable flake detection on backport branches
without having to test large diffs when PRs are merged targetting
backport branches. New backport branches will no longer have flake
detecton and new deploy test runs disabled.
Replaces the static `PREVIEW_BUILDS_BLOB_READ_WRITE_TOKEN` secret with a
short-lived access token to upload to Vercel Blob in exchange for a
GitHub OIDC (endpoint side:
https://github.com/vercel/vercel-packages/pull/98):
- the job mints GitHub Actions OIDC tokens for the
`https://vercel-packages.vercel.app` audience itself (the workflow gains
`id-token: write`) and re-mints shortly before expiry since a token
lives about five minutes while a package batch can take longer
- `upload-preview-tarballs.js` exchanges the token at vercel-packages
for scoped client-upload tokens via POST and uploads the tarball bytes
directly to Blob storage with `@vercel/blob/client`'s `put`, so the blob
read-write token never leaves vercel-packages
- the `preview-builds` environment is dropped from the job: it only
existed to selectively expose the static token and restrict uploads to
canary, and the canary binding is now enforced by the `job_workflow_ref`
match at vercel-packages.
---------
Co-authored-by: Claude Code (kimi-k3[1m]) <noreply@anthropic.com>
CI authenticated to Vercel Remote Cache with a long-lived Personal
Access Token in the `TURBO_TOKEN` repository secret. That token never
expired, is scoped to a team member rather than the team, and is
readable by every job that inherits secrets.
Each job now mints its own short-lived, cache-only token instead, using
`vercel/setup-turborepo-remote-cache-action` against a Turborepo CLI
OIDC policy configured on the Vercel team following
https://vercel.com/docs/monorepos/remote-caching/external-ci-cd#openid-connect-oidc
Forks skip the step entirely since they won't have access to repository
variables. During outages or any other permission errors, the steps
outcome will simply be ignore and we fall back to uncached behavior.
This could lead to silent regressions or hiding new, incorrect callsites
lacking necessary permissions. A Datadog monitor is not as simple as I'd
like since DD does not track outcome but conclusion (which is always
success for continue-on-error). Adding custom tags via DD CLI feels to
heavy. We'll revisit if this becomes a recurring issue.
## Summary
- Use the job-scoped `GITHUB_TOKEN` with explicit `actions: write`
permission to dispatch `trigger_release.yml` after a stable backport.
- Avoid requesting Actions write access from the release GitHub App,
which currently prevents the dispatch token from being created.
- Preserve the existing forced preminor canary release behavior.
This fixes the failure that left canary semver-behind stable after the
`v16.3.1` backport. The evaluation succeeded, but the dispatch failed
while creating the App token in the [failing dispatch
job](https://github.com/vercel/next.js/actions/runs/31750536217/job/94615208366).
## Verification
https://github.com/vercel/next.js/actions/runs/31830288265/job/94864091005
```
should_dispatch=true
reason=Dispatching canary preminor release because stable release 16.3.1 is ahead of 16.3.1-canary.17
released_version=16.3.1
current_canary_version=16.3.1-canary.17
dispatch_input=false
auto_dispatch_enabled=true
```
## Summary
- Let issue authors and people who commented before closure reopen an
issue within 14 days using `Reopen: <reason>`.
- Limit the automation to unlocked issues closed by triage-or-higher
human maintainers, with ordered timeline checks, bot filtering,
idempotency, and state revalidation.
- Align resolved-issue and contributor guidance with the new reopening
window.
## Verification
- `Prettier 3.6.2 --check .github/workflows/issue_reopen.yml
.github/comments/resolved.md contributing/repository/triaging.md`
- Mocked workflow harness covering 19 closure, eligibility, boundary,
bot, idempotency, and race scenarios
- `autoreview --mode local --stream-engine-output` (Codex and Claude
clean)
- Live GitHub API spot-check for ordered same-second `commented` and
`closed` timeline events
- Not run: live issue close/reopen smoke test (issue-triggered workflows
run only from the default branch)
Replaces the shared, static `PREVIEW_BUILDS_READ_TOKEN` secret with OIDC
tokens for reading auth-protected preview builds from vercel-packages
(see https://github.com/vercel/vercel-packages/pull/96):
- CI jobs mint a GH oidc token for the
`https://vercel-packages.vercel.app` audience
- deploys have a `.npmrc` written that uses the Vercel OIDC token
Tarball polling now also fails fast on 401/403 (retrying can't change
the authorization outcome) and failure messages include response headers
so request IDs are available for debugging.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This is mostly just to remove some confusion and clean up unused default
values in some places.
When you run on a backport branch or older tag, you probably never want
to use `canary` and just the version at that point. Running on `canary`
branch will implicitly use the latest `@canary` from NPM.
Since our setup job already checks for the version declared in the
checkout, we should use that everywhere so that we only have to check a
single place to be sure about what version is/will be used.
I'll backport this especially because it'll fix a bug in 15.x where we
ignore the release tag.
The weekly `test-e2e-project-reset-cron` workflow never defined
`VERCEL_TURBOPACK_TEST_TEAM` or `VERCEL_TURBOPACK_TEST_TOKEN`, even
though `run-e2e-test-project-reset.mjs` iterates over all three deploy
test teams. Because `resetProject` defaulted `teamId` and `token` to the
base team, and a destructuring default fires on an explicit `undefined`,
the turbopack iteration silently resolved to `vtest314-next-e2e-tests`.
The cron has therefore been deleting and recreating the base team's
project twice per run while never resetting the turbopack team's
project, and reporting success throughout. This dates back to #89458,
which wired the env pair into `build_reusable.yml` and
`test_e2e_deploy_release.yml` but missed the cron.
This drops the defaults in favor of explicitly passing the team.
Gets rid of of all usages of the static `GH_TOKEN_PULL_REQUESTS` token
that was issued for `vercel-release-bot`.
We already create commits with `nextjs-bot`. `nextjs-bot` already had
permissions to open PRs which is already being used by React sync.
Noticed when auto-merge didn't work. `vercel-release-bot` isn't in the
exemption list (for good reason).
Since `vercel-release-bot` still uses a static token, it's time to use
the app fully.
The rest of the usage I'll do in a follow-up.
## test plan
It'll fail for now when enabling auto-merge. Everything else works
though. I'll fix the perms for auto-merge in a follow-up.
- [x] [sync from this
branch](https://github.com/vercel/next.js/actions/runs/30954178134/job/92143210455)
-> https://github.com/vercel/next.js/pull/96688
### What
Fixes the **Create Release Branch** workflow, which currently fails on
every run.
1. Drop `permission-environments: write` from the GitHub App token
request.
- The release App installation no longer grants it, and
`actions/create-github-app-token` fails hard on an ungranted permission.
2. Drop the runtime `deployment-branch-policies` API call (and its 5s
sleep) from `scripts/create-release-branch.js`.
- No longer needed: the `release-stable` environment now statically
allows branches conforming to `next-NN` / `next-NN-*`. This is what the
permission above existed for.
3. Create the setup commit as a GitHub-signed commit via
`scripts/github-utils/signed-commit.js` instead of `git push`.
- Branch protection requires signed commits; `git push` pushed an
unsigned one. Uses the same `createSignedCommit` + `upsertBranchRef`
pattern as `scripts/automated-update-workflow.js`.
### Why
The most recent run
([30923742579](https://github.com/vercel/next.js/actions/runs/30923742579/job/92040655448))
dies at the first step, `Create GitHub App token`, with a 422 — so the
job never even clones the repo.
### Note for review
Release tags are **annotated** tag objects, so `git rev-parse v16.0.0`
returns the tag object SHA, not the commit. `createSignedCommit` passes
`baseSha` straight into `parents: [...]`, so the base is dereferenced
with `^{commit}`. Its internal `^{tree}` lookup happens to resolve
either way, so this would otherwise only have failed at commit-creation
time during a real release.
The static patterns cover `next-16-2`-style names but not a single-digit
major like `next-9-5`. Not a concern for current or future releases, but
noting it since the previous code created the policy for any branch
name.
### Verification
The workflow is `workflow_dispatch`-only and writes to the real repo, so
it can't be run end-to-end from a PR. Verified locally against the real
`v16.0.0` tag in a throwaway clone:
- `git reset --hard v16.0.0` leaves HEAD exactly at `v16.0.0^{commit}`,
confirming the base SHA is right.
- Replaying the lerna.json / `build_and_deploy.yml` /
`build_and_test.yml` mutations produces exactly those 3 changed files
and nothing else.
- Driving `createTreeFromLocalCommit` with a mocked `request` (no
network) uploads 3 blobs with correct `100644` modes and a `base_tree`
that matches the tag's tree.
- `node --check`, prettier, and eslint all clean.
Still needs a real `workflow_dispatch` run after merge to confirm the
token step succeeds and the resulting commit shows as **Verified**.
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 -->