### What?
Add a repository skill for building local React stable and experimental
bundles, syncing them into Next.js, and verifying the integration.
### Why?
Testing upstream React changes in Next.js requires a precise bundle
list, both React release channels, and the repository-pinned pnpm
version. Capturing the workflow as a skill makes it repeatable and
avoids global pnpm versions bypassing workspace linking.
### How?
Add `$react-sync` with a reusable build helper and document it in
`AGENTS.md`. The sync command puts the Corepack shim first on `PATH` so
nested pnpm commands use the version pinned by Next.js.
### Verification
- `bash -n .agents/skills/react-sync/scripts/build-for-next.sh`
- `python3
~/.codex/skills/.system/skill-creator/scripts/quick_validate.py
.agents/skills/react-sync`
- Prettier check for `AGENTS.md` and `SKILL.md`
- Full local React build and `pnpm sync-react` workflow completed
successfully
- `pnpm --filter=next build`
<!-- NEXT_JS_LLM_PR -->
Fixes#95125 in two ways
The OP admits that they misread the message in a few ways, but we can
fix all of them
1. Be specific about what function might need an annotation in the error
message. A user with a nearby `path.join` might think that that is where
the annotation belongs instead of on the function with the warning.
2. Be more flexible with how these `ignore` comments are interpreted
`fs.readFileSync(path.join(process.cwd(), someVar))` shouldn't require 2
annotations. Handle this by checking for and detecting `turbopackIgnore`
comments in all expressions! This will then bubble up through the linker
and just cause us to ignore corresponding fs access apis.
## Follow-ups for the experimental TypeScript CLI checker
Two independent improvements to the `experimental.useTypeScriptCli`
build path.
### 1. Make `tsc` responsive to interruption
When `next build` is interrupted (Ctrl-C / `SIGTERM`), the TypeScript 7
native compiler could keep running to completion instead of stopping —
leaving a CPU-heavy process alive after the build was abandoned.
The teardown already handled termination signals and killed the whole
process group; the problem was the signal it sent. The native compiler
ignores `SIGTERM`/`SIGINT`, so the graceful signal never stopped it. We
now escalate to `SIGKILL` (Windows: `taskkill /T /F`), which reaps the
compiler on interrupt (measured ~200ms vs. running to completion).
The compiler's signal handling may be improved upstream — see
[microsoft/typescript-go#4592](https://github.com/microsoft/typescript-go/pull/4592),
which threads an interruption `context` through `tsc build`. That work
is still in progress; until it lands and ships, this escalation is what
makes interruption reliable.
### 2. Skip the jest worker for the CLI checker
The type-check runs in a jest worker to isolate the TypeScript
compiler-API heap so it can be freed after checking. In CLI mode the
compiler runs in a separate `tsc` process, so there is no heap to
isolate and the worker adds nothing but an extra process and
indirection. CLI mode now runs the setup/config path in-process and
spawns `tsc` directly. The TypeScript-API checker is unchanged and still
uses the worker.
### Testing
- Unit tests for `runTypeScriptCli` (spawn options, group-SIGKILL
teardown, signal handling, listener cleanup, captured-output decoding).
- Existing `test/production/app-dir/typescript-cli` integration suite
passes (TS 6, TS 7, opt-in-required, `ignoreBuildErrors`,
`--debug-build-paths`).
- Manually verified against a TypeScript 7 project large enough to
distinguish a real kill from natural completion: the native compiler is
reaped ~200ms after interrupt.
<!-- NEXT_JS_LLM_PR -->
## Summary
The graph-based CSS chunker's `linearize` step produces the global
module order that `split_into_chunks` then cuts into chunks. It
previously broke ties between ready modules using a weight-sorted stack.
It now prefers the ready module that shares the most chunk groups with
the previously placed module, so modules that are loaded together end up
adjacent in the global order and split into better-aligned chunks. On a
real world example this measurably lowers the modeled chunk-loading
cost, with fewer requests and less over-fetched CSS.
The shared-group count is read from edge weights (how many chunk groups
two modules co-occur in). Since `make_acyclic` deletes edges to break
cycles, it now returns the edges it cut so `linearize` can still count
those conflicting-order co-occurrences — reconstructing the full
co-occurrence without cloning the graph.
## Verification
- `cargo test -p turbopack-core style_groups_graph::tests` (all pass)
- Validated on a real world example that the resulting global order is
deterministic and the modeled chunk-loading cost decreases.
<!-- NEXT_JS_LLM_PR -->
Ensures the module graph is not bundled. Right now this has no effect
since all the modules imported by `navigation-test-lock` have legit uses
in prod builds.
Also nice to not have to fork every single implementation inline.
## What
Adds `next experimental-diagnose` and an MCP `get_request_insights` tool
so agents and local users can inspect Request Insights without opening
the DevTools overlay.
Stack:
1. request insights: record local framework spans (1/5)
2. request insights: derive request history and fetch data (2/5)
3. request insights: expose dev snapshots to tools and HMR (3/5)
4. request insights: add agent diagnosis access (4/5) ← current
5. request insights: add DevTools request panel (5/5)
## Review Focus
- CLI error handling and raw JSON output.
- MCP gating when Request Insights is unavailable.
- Telemetry registration for the new MCP tool name.
## Proof In This PR
- `pnpm jest packages/next/src/telemetry/events/mcp-telemetry.test.ts
--runInBand`
- `pnpm --filter=next types`
- Stack-tip smoke verifies `next experimental-diagnose --url
http://localhost:3003 --json` against a real dev server.
## Deferred Coverage
- A more curated agent summary format is intentionally deferred until
the raw private snapshot contract settles.
<!-- NEXT_JS_LLM_PR -->
---------
Co-authored-by: Tim Neutkens <[email protected]>
When using `nextConfig.i18n`, then static files for `404.html` and
`500.html` were added multiple times to the list.
These duplicate entries in `outputs.staticFiles` ended up causing race
conditions in the Vercel adapter because the same output file path was
written concurrently by multiple file operations.
## What
Adds the `experimental.requestInsights` flag, a private dev snapshot
endpoint, and HMR snapshot/update transport so running dev servers can
expose recent request insights to tools and browser clients.
Stack:
1. request insights: record local framework spans (1/5)
2. request insights: derive request history and fetch data (2/5)
3. request insights: expose dev snapshots to tools and HMR (3/5) ←
current
4. request insights: add agent diagnosis access (4/5)
5. request insights: add DevTools request panel (5/5)
## Review Focus
- Dev-only gating for `experimental.requestInsights`.
- Private endpoint behavior when enabled and disabled.
- HMR snapshot/update shape and client-side retention.
## Proof In This PR
- `pnpm --filter=next types`
- Stack-tip Cache Components smoke verifies the endpoint and HMR-fed
overlay state against a running dev server.
## Deferred Coverage
- A dedicated isolated dev-server transport test can follow if reviewers
want cheaper per-PR coverage than the stack-tip smoke.
<!-- NEXT_JS_LLM_PR -->
---------
Co-authored-by: Tim Neutkens <[email protected]>
## What
Builds the bounded request history store on top of local spans and fetch
metrics. Request Insights now keeps the last 100 requests, dedupes fetch
records, and preserves raw span details for later verbose consumers.
Stack:
1. request insights: record local framework spans (1/5)
2. request insights: derive request history and fetch data (2/5) ←
current
3. request insights: expose dev snapshots to tools and HMR (3/5)
4. request insights: add agent diagnosis access (4/5)
5. request insights: add DevTools request panel (5/5)
## Review Focus
- The request insight schema shared by server, tools, and DevTools.
- Fetch cache/status mapping from existing `AppRender.fetch` spans.
- Dedupe between completed fetch spans and direct fetch metrics.
## Proof In This PR
- `pnpm jest packages/next/src/server/lib/trace/request-insights.test.ts
packages/next/src/server/lib/patch-fetch.test.ts --runInBand`
- `pnpm --filter=next types`
## Deferred Coverage
- Browser/HMR transport, CLI/MCP access, and the visual panel are later
PRs.
<!-- NEXT_JS_LLM_PR -->
---------
Co-authored-by: Tim Neutkens <[email protected]>
## What
Adds a dev-only local recorder for framework OTEL spans. This lets Next
mirror span data into an in-memory store when local span capture is
enabled, without requiring a user to run an OTEL provider.
Stack:
1. request insights: record local framework spans (1/5) ← current
2. request insights: derive request history and fetch data (2/5)
3. request insights: expose dev snapshots to tools and HMR (3/5)
4. request insights: add agent diagnosis access (4/5)
5. request insights: add DevTools request panel (5/5)
## Review Focus
- Local recorder behavior when no OTEL provider is installed.
- Preserving existing OTEL span API behavior when a provider is
installed.
- Request identity propagation through existing app render async
storage.
## Proof In This PR
- `pnpm jest packages/next/src/server/lib/trace/tracer.test.ts
packages/next/src/server/lib/trace/span-store.test.ts --runInBand`
- `pnpm --filter=next types`
## Deferred Coverage
- Request-level aggregation, fetch enrichment, dev transport, CLI/MCP
access, and the DevTools panel are later PRs in this stack.
<!-- NEXT_JS_LLM_PR -->
---------
Co-authored-by: Tim Neutkens <[email protected]>
We forked test assertions in places in the early days of merging
Turbopack. Now that we more closely align, a bunch of these had the
exact same assertions. Fold them into one.
## 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 -->
Adds a **Building** guide (`docs/01-app/02-guides/building.mdx`): what
`next build` does, how to read the route table (`○` Static, `◐` Partial
Prerender, `ƒ` Dynamic), prerendering dynamic routes with
`generateStaticParams`, streaming request-time work, and debugging
prerender errors. Also links the guide from the deploying page and the
`next build` CLI reference.
## Summary
Adds a clear incremental adoption path to the Cache Components migration
guide and reorganizes the workflow sections so they read in dependency
order.
- New **Adopting incrementally** section documenting the staged flow:
enable the flag, opt routes out with `instant = false` (including the
`cache-components-instant-false` codemod for a one-pass opt-out), fix
synchronous IO, then convert routes one at a time.
- Makes explicit that **synchronous IO cannot be deferred** by `instant
= false` (it is an unconditional prerender build error), and documents
the actual fix: move the call to request time via `<Suspense>` +
`connection()` or a Client Component. Previously the guide only said to
"fix it," implying every route could be deferred.
- Groups the workflow sections (`Following validation`, `Opting out of
validation`, `Adopting incrementally`) together before the per-API
reference, instead of leaving them stranded in the middle of the
reference list.
- Cross-links the adoption skill's **Incremental** mode to the manual
`Adopting incrementally` flow so readers see they are the same workflow,
automated vs. by hand.
## Verification
- `pnpm prettier --with-node-modules --ignore-path .prettierignore
--write docs/01-app/02-guides/migrating-to-cache-components.mdx` (clean)
- Verified the sync-IO behavior against source (`throwIfSyncIOUsed` runs
before the `allowEmptyStaticShell` / `instant = false` gate in
`dynamic-rendering.ts`).
- All intra-page anchor links and the `cache-components-instant-false`
codemod anchor confirmed to resolve.
<!-- NEXT_JS_LLM_PR -->
This PR makes Sync IO behavior more restrictive when
`partialPrefetching` is on (either globally or for a page).
- Previously, we allowed `await cookies(); Date.now()` in in segments
that won't ever be runtime prefetched. This was achieved by separating
segments into "early" and "late" stages and only erroring in the "early"
ones.
- After this PR, it will always be an error to do sync IO anywhere other
than after `io()`/`connection()`/uncached IO (i.e. outside the Dynamic
stage).
We're doing this mainly because with `partialPrefetching`, `cookies()`
resolves in App Shells, and sync IO causes a severe deopt in any
prerender. `allow-runtime` also opts a route into `partialPrefetching`,
which means that even the segments above the runtime prefetch boundary
will need an App Shell and can't tolerate Sync IO.
This PR removes all early/late stage separation, because we no longer
need to vary the Sync IO behavior on the segment. Instead, we now pick
whether a stageController should use
- `SyncIOMode.AllowedInRuntimeOrDynamic` (legacy)
- `SyncIOMode.AllowedInDynamic` (`partialPrefetching`)
- `SyncIOMode.Untracked` if it needs to opt out
I've removed the existing tests that asserted that sync IO is allowed in
segments above the `allow-runtime` boundary. Instead, we check that sync
io either errors (if partialPrefetching is on) or is allowed (if
partialPrefetching is off)
Closes NAR-855
We have a couple switches in `StagedRenderingController` where the main
purpose ends up being eliminating `Before/Dynamic/Abandoned` (i.e. the
start and the end stages). Since we migrated everything to
`RENDER_STAGE_ORDER` loops, we no longer really need to list all the
intermediate stages out explicitly in these switches. It's enough to
rule out the "special" stages and let the rest of the code be generic.
This saves some work when adding/removing intermediate stages.
View with "hide whitespace", otherwise it looks messy.
Recreation of https://github.com/vercel/next.js/pull/95370
### What's the problem?
A `POST` (or `PUT`/`PATCH`) request that passes through middleware
returning `NextResponse.next()` hangs indefinitely when the downstream
handler reads the body via Node's `Readable.toWeb()`. The request never
completes and eventually times out.
Reproduction: https://github.com/abir-taheer/next-js-readable-stream-bug
### Root cause
When middleware runs, `runMiddleware` clones the request body and later
calls `finalize()`, which grafts the buffered stream back onto the
original `IncomingMessage` via `replaceRequestBody()`
(`packages/next/src/server/body-streams.ts`). `replaceRequestBody`
copies the buffered stream's enumerable properties onto the request.
The buffered stream (`p2`) was a `PassThrough` — a `Duplex` — so its
writable-side internals (`_writableState` plus the enumerable `Writable`
methods like `write`/`end`) were copied onto the `IncomingMessage`.
Because that `_writableState.finished` is `false`, Node stream utilities
that inspect it — including `Readable.toWeb()`, which uses
`finished()`/end-of-stream detection — treat the request as a still-open
writable stream and wait forever.
`NextResponse.rewrite()` is unaffected (it builds a new internal request
and skips this path), and `GET`/`HEAD` requests are fine because there
is no body to clone.
### The fix
`p2` is only ever fed with `.push()`, so it never needs a writable side.
Making it a plain `Readable` instead of a `PassThrough` keeps the
finalized request a pure `Readable`, so `Readable.toWeb()` (and any
other duck-typing based on `_writableState`) behaves correctly. No
behavior change for the existing consumers, which only read the stream.
### Testing
Added `test/unit/body-streams.test.ts`, which drives the real clone →
`finalize()` flow and asserts the finalized request:
- is no longer writable (`_writableState` is `undefined`), and
- is fully consumable via `Readable.toWeb()` (this hangs before the
fix).
Fixes#95335
<!-- NEXT_JS_LLM_PR -->
Co-authored-by: UditDewan <[email protected]>
Co-authored-by: Baradhan-Madhu <[email protected]>
Guidance for those who don't customize their own not-found page, pages
router's 404, etc, because the defaults honor the system style, not any
app-level theme settings.
See NAR-876. We discovered this problem in #95384, when removing the
Early/Late stage split, but it turns out that this was a preexisting
race that occurs for runtime-prefetchable segments the problem existed
before for runtime (which currently render in EarlyStatic, a task
earlier than non-runtime-prefetchable segments)
adds failing tests for `passHref` with allow-runtime (i.e. when the
segment runs early), and passing tests for it in the dynamic stage (when
the passHref is gated behind dynamic, which seems to delay it long
enough for the debug channel to initialize)
<!-- Thanks for opening a PR! Your contribution is much appreciated.
To make sure your PR is handled as smoothly as possible we request that
you follow the checklist sections below.
Choose the right checklist for the change(s) that you're making:
## For Contributors
### Improving Documentation
- Run `pnpm prettier-fix` to fix formatting issues before opening the
PR.
- Read the Docs Contribution Guide to ensure your contribution follows
the docs guidelines:
https://nextjs.org/docs/community/contribution-guide
### Fixing a bug
- Related issues linked using `fixes #number`
- Tests added. See:
https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md
### Adding a feature
- Implements an existing feature request or RFC. Make sure the feature
request has been accepted for implementation before opening a PR. (A
discussion must be opened, see
https://github.com/vercel/next.js/discussions/new?category=ideas)
- Related issues/discussions are linked using `fixes #number`
- e2e tests added
(https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs)
- Documentation added
- Telemetry added. In case of a feature if it's used or not.
- Errors have a helpful link attached, see
https://github.com/vercel/next.js/blob/canary/contributing.md
### Signed commits
- This repository requires verified commit signatures on protected
branches.
- If this pull request is blocked for unsigned commits, re-sign the
commits and force-push the branch.
- A `Signed-off-by` line in the commit message is not enough.
## For Maintainers
- Minimal description (aim for explaining to someone not on the team to
understand the PR)
- When linking to a Slack thread, you might want to share details of the
conclusion
- Link both the Linear (Fixes NEXT-xxx) and the GitHub issues
- Add review comments if necessary to explain to the reviewer the logic
behind a change
### What?
### Why?
### How?
Closes NEXT-
Fixes #
-->
This PR simplifies the way that we're preserving
`registration.updateViaCache` after the code-gen that rewrites the
options in the service worker registration
This is a follow-up to https://github.com/vercel/next.js/pull/95431; I
think we can directly mention this in this guide to make people's lives
easier (the PWA guide is quite long for this small chunk of code)
2026-07-09 23:05:00 -07:00
Sam PoderGitHubvercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
We recently rolled out this new syntax for bundling service workers with
Turbopack:
```
await navigator.serviceWorker.register(new URL('../lib/service-workers/1.js', import.meta.url), ...)
```
This PR updates the documentation that previously mentioned service
workers.
---------
Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
## Summary
- add `experimental.useTypeScriptCli` so projects can explicitly run
their local `tsc` during `next build`, including TypeScript 7 while the
legacy JavaScript API is unavailable
- preserve the TypeScript API backend as the default, with TypeScript
6-compatible dependency installation and actionable TypeScript 7
migration guidance
- load effective compiler metadata through `tsc --showConfig`, preserve
inherited path origins, stream native diagnostics, redirect incremental
state, and forward worker termination to the compiler process tree
- document the experimental behavior and cover TypeScript 6/7, both
bundlers, raw diagnostics, full-project checking, dependency selection,
and cleanup
## Demo
```
pnpm build
> [email protected] build /Users/timneutkens/projects/sandbox/cna
> next build
▲ Next.js 16.3.0-canary.81 (Turbopack)
✓ Running next.config.ts took 23ms
- Cache Components enabled
- Experiments (use with caution):
✓ useTypeScriptCli
Creating an optimized production build ...
✓ Compiled successfully in 3.9s
Running TypeScript ...
app/page.tsx:8:3 - error TS2322: Type 'string' is not assignable to type 'number'.
8 return count++ + "abc";
~~~~~~
Found 1 error in app/page.tsx:8
Failed to type check.
Next.js build worker exited with code: 1 and signal: null
ELIFECYCLE Command failed with exit code 1.
```
## Verification
- `pnpm test-start-turbo
test/production/app-dir/typescript-cli/typescript-cli.test.ts`
- `pnpm test-start-webpack
test/production/app-dir/typescript-cli/typescript-cli.test.ts`
- `pnpm test-start-turbo
test/production/ci-missing-typescript-deps/index.test.ts`
- `pnpm test-dev-turbo
test/development/typescript-native-preview/index.test.ts`
- `pnpm test-start-turbo test/production/typescript-basic/index.test.ts`
<!-- NEXT_JS_LLM_PR -->
Reduce the size, i don't really expect much of a win, but this is low
hanging fruit and removes a few branches as well.
Consolidate the implementation of CollectibleSource, no need for
OperationVc to delegate to RawVc here.
### What?
Add two dynamic routes to the render-pipeline benchmark and include both
in the default route suite:
- `/attributes` renders 1,000 rows with string, data, ARIA, title, and
inline-style attributes.
- `/tailwind` renders a realistic dashboard with dense Tailwind-style
utility class names across navigation, metrics, project cards, and an
activity table.
### Why?
The existing benchmark routes focus on lightweight rendering or Suspense
and Flight payload stress. They do not provide focused integration
coverage for React Fizz attribute serialization.
`/attributes` provides a synthetic stress case modeled after the
upstream React benchmark for facebook/react#36899. `/tailwind`
complements it with a production-like component tree and realistic class
strings, including responsive, state, dark-mode, and arbitrary-value
utilities.
### How?
Both fixtures run through the real Next.js production-server
render-pipeline harness and are forced dynamic so every request
exercises server rendering.
The Tailwind-style fixture contains 18 project cards, 36 activity rows,
and 101 `className` attributes. It intentionally does not install or
configure Tailwind because the benchmark targets React serialization of
the class strings, not generated CSS or visual styling.
The new routes are registered in the default stress suite and documented
in the benchmark playbook and render-pipeline README.
### Verification
- Prettier for all changed files
- ESLint for both benchmark fixtures and
`bench/render-pipeline/benchmark.ts`
- Production `next build` + `next start` smoke benchmark for
`/tailwind`; single-client and under-load phases completed with zero
request errors
- Three interleaved base/head `/attributes` runs with 500 serial and
5,000 loaded requests
- `/attributes` PR-head median throughput: +8.26% single-client and
+10.85% under load
- `/attributes` PR-head median p95: 7.91% better single-client and 9.61%
better under load
- `git diff --check`
<!-- NEXT_JS_LLM_PR -->
### What?
Document loading request-independent assets at module scope, and update
the OG image examples to follow it.
### Why?
Reading a local asset inside the component is async I/O during render,
so the route defers to request time. A top-level `await` at module scope
keeps the route prerendered without reaching for `use cache`. The
pattern previously only lived as an inline snippet in the OG image docs.
### How?
- Add an "Asynchronous I/O at module scope" section to the Cache
Components guide as the canonical explanation.
- Load fonts and local assets at module scope in `opengraph-image.mdx`
and `image-response.mdx`, and link both to the new section.
<!-- NEXT_JS_LLM_PR -->
---------
Co-authored-by: Joseph <[email protected]>
Make the ppr test accept diverse solutions as long as they lead to a
static shell. Achieved by `expect(environment).toSatisfyCriterion`
(a.k.a., llm-as-judge)
Agents have started flagging the managed `nextjs-agent-rules` block as
prompt injection and refusing to follow or commit it (#95130). Headless
A/B evals (Claude Code + Sonnet 5, pnpm-monorepo and standalone-npm
fixtures, `[email protected]`) isolate the triggers:
`node_modules/next/dist/docs/` doesn't resolve from the workspace root
in non-hoisted installs, the "maintained by `next dev`" claim is
unverifiable, and the **Keep this block** paragraph reads as a
self-persisting instruction. The failure concentrates on the uncommitted
state a fresh `next dev` run produces.
The block now anchors the docs path to the block file's directory with a
monorepo note, and replaces the commit imperative with a verify pointer
to `generate-agent-files.js` plus the reason committing it keeps the
tree clean. This is the only change in this PR, and it was ablated in
isolation — an A/A run of the baseline first established the noise floor
(block committal 0/5, 0/8, 1/8 across three independent baseline
batches):
| Metric | Old block | New block |
|
---------------------------------------------------------------------------------
| --------- | --------- |
| Uncommitted block, diff-review prompt — committed the block | 1/21 |
8/20 |
| Uncommitted block, diff-review prompt — final message calls it a
prompt injection | 12/21 | 4/20 |
| Committed block, task prompts — injection flags | 0/15 | 0/15 |
| Docs reads/run — standalone app, write tests (n=2) | 4.5 | 5.0 |
| Docs reads/run — monorepo, add page (n=3) | 3.7 | 4.0 |
| Docs reads/run — monorepo, write tests (n=10) | 3.4 | 2.6 |
Committal improves at p = 0.008 and injection accusations drop at p =
0.016 (Fisher exact); runs that follow the verify pointer conclude
"genuine framework-scaffolded content, not injected". A candidate
follow-up — appending "commit this change" to the dev-server log line —
was ablated separately and does nothing: the line reaches the agent's
transcript in 12/12 dev-session runs but committal stayed 0/6 vs 0/6, so
it is not included. The upsert machinery that delivers this rewording to
existing projects is #95470.
Once the managed agent-rules block is installed, `next dev` never
touches it again — `ensureAgentRulesForDev` gates on marker presence —
and `@next/codemod upgrade` doesn't refresh it either, so changes to the
block's content (like the wording fix in #95467) never reach existing
projects.
`next dev` now gates on `hasCurrentAgentRules` and re-upserts a block
whose content differs from canonical, targeting whichever file already
hosts it so an upgrade rewrites the block in place instead of
duplicating it into a second file; legacy `NEXT-AGENTS-MD` blocks
migrate the same way, and CRLF files keep their line endings.
`@next/codemod upgrade` refreshes the block after the version bump by
delegating to the installed package's own generator
(`next/dist/server/lib/generate-agent-files`), so the codemod never
carries a copy of the wording and no-ops below 16.3 or when the project
never adopted the block. A malformed block (start marker without end)
converges to a single canonical block within two runs rather than
growing unboundedly.
The e2e test now covers the in-place upgrade (surrounding content
preserved, exactly one block, no `CLAUDE.md` created when `AGENTS.md`
hosts it) and idempotence across dev-server restarts, using the real
generator's output rather than hardcoded wording.