Skip to content

fix(cache): support segment prefetch inlining - #2855

Open
james-elicx wants to merge 5 commits into
mainfrom
codex/fix-segment-cache-prefetch-inlining-current
Open

fix(cache): support segment prefetch inlining#2855
james-elicx wants to merge 5 commits into
mainfrom
codex/fix-segment-cache-prefetch-inlining-current

Conversation

@james-elicx

Copy link
Copy Markdown
Member

Summary

  • measure concrete prerendered Flight segment sizes without rerunning user components
  • persist and inject validated prefetch-inlining hint trees for Cache Components builds
  • preserve worker affinity and clean up per-render head measurements on every render path
  • add local production parity coverage for the upstream prefetch-inlining suite

Upstream parity target

test/e2e/app-dir/segment-cache/prefetch-inlining/prefetch-inlining.test.ts

Validation

  • targeted Next.js wrapper: 7/7 tests, 7/7 snapshots
  • local production Playwright parity E2E: 1/1
  • focused Vitest suites: 296/296
  • targeted format, lint, and types: clean
  • independent final review: no findings

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@pkg-pr-new

pkg-pr-new Bot commented Aug 10, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@vinext/cloudflare@2855
npm i https://pkg.pr.new/create-vinext-app@2855
npm i https://pkg.pr.new/@vinext/types@2855
npm i https://pkg.pr.new/vinext@2855

commit: ac78402

@github-actions

Copy link
Copy Markdown
Contributor
Example Preview Production Original
app-router-cloudflare preview production
pages-router-cloudflare preview production
app-router-playground preview production original
realworld-api-rest preview production
nextra-docs-template preview production
benchmarks preview production
hackernews preview production original
workers-cache preview production
web preview production

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: segment prefetch inlining

Reviewed the 23 changed files with focus on correctness, security, dev/prod parity, and the build/runtime hint pipeline. This is a solid, well-tested change. Focused Vitest suites (app-route-tree-prefetch, app-prerender-endpoints, inject-pregenerated-paths) pass 36/36 locally, and targeted vp check is clean.

Findings

1. (Non-blocking, perf) Children-slot page is measured twice.
In measureAppRouteTreePrefetchSizes (app-route-tree-prefetch.ts:355-380), when a children slot's page is promoted to pageValue (line 375), the same decoded element is measured once via slotTasks and again via the page measurement in the final Promise.all. That's two _prerender + gzip passes over an identical model per children-slot route. It's correct (the test sizes.page).toBe(sizes.slots[childrenPage]) confirms parity), but each redundant measurement re-encodes a Flight model during the build. Consider reusing the slot measurement for page when pageValue === childrenSlotPageValue. Low priority given build-time only.

2. (Non-blocking, robustness) Early return in __collectPrefetchHints skips head-map cleanup.
In entries/app-rsc-entry.ts:741, when matchRoute misses / pattern mismatches / the route is a route handler, the function returns null before deleting the captured head from __prefetchHeadsByAffinity (the delete is at line 744, after the guard). This is currently harmless because the finally block in prerenderApp (build/prerender.ts:1666-1685) always issues the affinity DELETE, which calls discardPrefetchHead. So the entry is always reclaimed. But the safety net is the only thing preventing an unbounded per-build accumulation — worth a short comment noting the dependency, or moving the delete above the guard so cleanup is local to the collector.

Verified as correct (no action needed)

  • Slot-id reconstruction matches the wire codec. buildTree reconstructs ownerTreePath via route.routeSegments.slice(0, ownerPosition) and encodeSlotId(slot.name, ownerTreePath). ownerPosition is a tree position from layoutTreePositions, and this matches createAppPageTreePath(routeSegments, treePosition) (app-page-route-wiring.tsx:304-313) used to encode slot ids at render time. Repeated slot names at different tree paths stay distinct (covered by the new "keeps full parallel-slot identities" test).
  • Affinity header is a trusted-only channel. Read only when VINEXT_PRERENDER === "1" and the prerender secret matches, in both app-rsc-handler.ts:1694 and prod-server.ts:1141. It's added to VINEXT_INTERNAL_HEADERS and stripped by filterInternalHeaders (regression test added in request-pipeline.test.ts). No forgeability from external requests.
  • Body buffering in rscHandler. Switching body: req.body -> buffered arrayBuffer() (build/prerender.ts:1126) is correct and necessary: it avoids streaming-body/duplex issues when forwarding POSTs (prefetch-hints, static-params) to the render pool, and rscData.slice().buffer produces a right-sized transfer for the hints POST.
  • HEAD_OUTLINED bit is intentional (asserted by the "outlines an oversized measured head" test) and rides through stripMutableFields verbatim to the client.
  • getPrecomputedTree / isTreePrefetch provides sound defense-in-depth validation of the deep-frozen build-time hint tree, correctly guards the optional pattern field, and cycle-guards via a WeakSet.

Parity note

Confirm the head-inlining gate change from node.name === PAGE_SEGMENT to !hasChildren (leaf) in computePrefetchHints (app-route-tree-prefetch.ts:477-486) matches Next.js's algorithm. I could not diff against the upstream implementation because .nextjs-ref is not present in this environment. The change is plausible (a __PAGE__ node is always a leaf) and the parity E2E suite reportedly passes 7/7, so this is a request to double-check against packages/next/src/.../segment-cache rather than a suspected defect.

Overall: no blocking issues. The two non-blocking items are worth a follow-up but do not affect correctness given existing coverage.

@ask-bonk

ask-bonk Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review posted to PR #2855. Summary of my analysis:

Review outcome: no blocking issues

The PR is well-structured and well-tested (36/36 focused Vitest, clean vp check). I posted two non-blocking findings and one parity note.

Non-blocking findings:

  1. Redundant measurement — a children-slot page gets measured twice (once as a slot, once as the page) in measureAppRouteTreePrefetchSizes. Correct but wastes a build-time _prerender + gzip pass per such route.
  2. Head-map cleanup ordering__collectPrefetchHints returns early (route-handler/mismatch) before deleting the captured head from the affinity map; it only stays leak-free because prerenderApp's finally always fires the DELETE. Worth a comment or moving the delete above the guard.

Verified correct (no action):

  • Slot-id reconstruction (ownerTreePathencodeSlotId) matches the render-time wire codec; repeated slot names stay distinct.
  • Affinity header is trusted-only (gated on VINEXT_PRERENDER + secret) and stripped by filterInternalHeaders, with a new regression test.
  • Body buffering in rscHandler is a necessary fix for streaming-body forwarding.
  • HEAD_OUTLINED bit and isTreePrefetch/getPrecomputedTree validation are sound.

Parity note: I flagged the head-inlining gate change (node.name === PAGE_SEGMENT!hasChildren) for a double-check against upstream Next.js, since .nextjs-ref wasn't available in this environment to diff the algorithm directly. Plausible and E2E-covered, but unverified against source.

github run

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Performance benchmarks

Compared ac78402 against base d558d62 using alternating same-runner rounds. Next.js was unchanged and skipped.

1 improved · 0 regressed · 5 within ±1.5%

Scenario Framework Baseline Current Change
Client bundle size (gzip) vinext 135.2 KB 135.2 KB ⚫ +0.0%
Client entry size (gzip) vinext 122.7 KB 122.7 KB ⚫ +0.0%
Dev server cold start vinext 3.08 s 3.02 s 🟢 -1.8%
Production build time vinext 3.31 s 3.30 s ⚫ -0.3%
RSC entry closure size (gzip) vinext 114.2 KB 114.7 KB ⚫ +0.4%
Server bundle size (gzip) vinext 193.0 KB 193.7 KB ⚫ +0.4%

View detailed results and traces

🟢 improvement · 🔴 regression · ⚫ change below 1.5% · paired base/head

@james-elicx

Copy link
Copy Markdown
Member Author

Addressed every actionable Bonk item in 22a52d1:

  • children-slot pages now reuse the existing slot measurement, with a regression assertion that the cached React node is rendered exactly once
  • affinity-scoped head data is deleted before route mismatch/route-handler early returns
  • verified the head-inlining gate directly against current Next.js packages/next/src/server/app-render/collect-segment-data.tsx: upstream uses !hasChildren at its leaf gate, matching this implementation

Re-ran the exact targeted Next.js wrapper: 7/7 tests and 7/7 snapshots.

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk

ask-bonk Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

UnknownError: ProviderInitError

github run

@ask-bonk

ask-bonk Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

@james-elicx Bonk workflow failed. Check the logs for details.

View workflow run · To retry, trigger Bonk again.

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@james-elicx

Copy link
Copy Markdown
Member Author

Final follow-up on a64a915:

  • fixed Check by registering the generated-entry runtime with Knip; local vp run knip and full vp run check pass
  • removed the feature-only prefetch measurement runtime from generated App Router entries when prefetchInlining is disabled, addressing the default RSC/server bundle regression with codegen coverage
  • exact targeted Next.js wrapper remains 7/7 tests and 7/7 snapshots on this head

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up review: segment prefetch inlining (22a52d1)

Independent re-review after the feedback commit. Focus: verifying the two addressed items and the previously-unverified parity claim, plus a fresh pass over the affinity channel and the build-artifact injection path. Cloned .nextjs-ref@canary (unavailable in the prior run) to diff the upstream algorithm directly.

Verification results (36/36 → 242/242 focused Vitest green, targeted vp check clean).

Both prior findings correctly resolved

  1. Double-measurement of children-slot pages — fixed. measureAppRouteTreePrefetchSizes now reuses the slot's Promise<number|null> for page when pageValue === undefined (app-route-tree-prefetch.ts:356-379), and the new rendered.filter(... === "children page").toHaveLength(1) assertion locks the single-render contract.
  2. Head-map cleanup ordering — fixed. __collectPrefetchHints now reads + deletes the affinity head before the matchRoute/route-handler early return (app-rsc-entry.ts:740-743), so cleanup is local to the collector and no longer depends solely on the finally-block DELETE.

Parity claim verified against upstream (the prior open question)

The head-inlining gate change (node.name === PAGE_SEGMENT!hasChildren) matches Next.js packages/next/src/server/app-render/collect-segment-data.tsx:792-805. Upstream gates on !headInlineState.inlined && isBundleTerminal && isPageSegment, where isBundleTerminal = !didInlineIntoChild && !isStaticPrefetchDisabled and isPageSegment = segment === PAGE_SEGMENT_KEY. In vinext's tree model this reduces cleanly to !hasChildren:

  • Every branch leaf in buildTree is a __PAGE__ node (app-route-tree-prefetch.ts:242-293, including slot page ?? default leaves), so !hasChildren ⟺ isPageSegment.
  • didInlineIntoChild is always false at a leaf (no children), and vinext has no StaticPrefetchDisabled concept, so isBundleTerminal is constant-true at leaves.

The substitution is a sound simplification for vinext's flattened wire tree, not a divergence. HEAD_OUTLINED-on-root when no page accepts the head also matches upstream collect-segment-data.tsx:571-575 (createRouteTreePrefetch:543-545).

Fresh checks (no findings)

  • Affinity channel security. renderAffinity is a monotonic per-render counter (unique head-map key), while pool routing uses affinityIndex % poolSize — HTML render, hints POST, and DELETE cleanup all carry the same value, so they deterministically hit the same worker where the head was captured. The header is in VINEXT_INTERNAL_HEADERS (stripped by filterInternalHeaders) and re-attached only after === prerenderSecret validation at the prod-server boundary (prod-server.ts:1141-1161); the !== null check in app-rsc-handler.ts:1694-1698 is the documented downstream secondary gate after upstream verification.
  • Build-artifact injection. injectPregeneratedConcretePaths embeds JSON.stringify(prefetchHints) as a JS expression (not inside <script>), sourced from trusted build-time prerender output, and the runtime re-validates via isTreePrefetch/getPrecomputedTree (deep, cycle-guarded, pattern-optional). No external input reaches this path.
  • Fixture parity. no-inline.tsx now mirrors upstream's ≥2KiB-gzip "use cache" component with a 10k-iteration safety cap.

Verdict: no blocking issues. The feedback commit resolves both prior items, and the head-inlining gate is confirmed to match upstream.

@ask-bonk

ask-bonk Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

UnknownError: ProviderInitError

github run

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: segment prefetch inlining (a64a915)

Fresh independent pass focused on correctness, dev/prod parity, and build-failure modes. Type-checks clean on all touched server/build files; focused Vitest green (app-route-tree-prefetch, app-prerender-endpoints, inject-pregenerated-paths = 36/36). The change is well-structured and the two prior Bonk findings (double-measurement, head-map cleanup ordering) are correctly resolved. The upstream parity claim (!hasChildren leaf gate) is sound.

One item worth addressing before merge, plus a couple of low-priority notes.

1. (Should fix) A hint-collection null turns into a hard build failure

handlePrefetchHintsEndpoint returns notFoundResponse() (404) whenever collectPrefetchHints yields null (app-prerender-endpoints.ts:87), and __collectPrefetchHints returns null on any of: matchRoute(pathname) miss, match.route.pattern !== pattern, or match.route.routeHandler (app-rsc-entry.ts:746). In prerenderApp, a non-ok hints response is escalated to a thrown error that fails the whole build:

// build/prerender.ts:1594
const hintsResponse = await rscHandler(hintsRequest);
if (!hintsResponse.ok) {
  const detail = await hintsResponse.text();
  throw new Error(`[vinext] prerenderApp: prefetch hint collection returned ${hintsResponse.status} ...`);
}

For normal pages the concrete urlPath re-matches its own routePattern, so this is fine in the happy path. But __collectPrefetchHints re-runs matchRoute(pathname) independently of the route being prerendered — so any routing asymmetry between "the route we chose to prerender" and "what matchRoute resolves the concrete path to" (a more-specific static route shadowing a dynamic pattern, an intercept/parallel-route edge, a rewrite-affected path) would produce a null, a 404, and a hard build failure rather than a soft "no hints for this route."

This is a fragile failure mode for a build-time optimization: prefetch inlining is a performance feature, and its hint collection failing to match should degrade to "skip hints for this route," not abort the build. Recommend either:

  • have the endpoint distinguish "collector errored" (500 → throw) from "no hints / route mismatch" (e.g. return 200 with an empty/absent tree, or a dedicated 204), and treat the latter as prefetchHints = undefined in prerenderApp; or
  • at minimum, in prerenderApp treat a 404 from the hints endpoint as "no hints" (skip) and only throw on 5xx.

A regression test that prerenders a route whose concrete path resolves to a different pattern (or a route-handler-shadowed path) would lock this in.

2. (Low, type hygiene) pattern is read off AppRouteTreePrefetchRoute but not declared on it

getPrecomputedTree gates on "pattern" in route and then reads route.pattern (app-route-tree-prefetch.ts:431-432), but AppRouteTreePrefetchRoute (line 47) has no pattern field. It type-checks today only because the in narrows to Record<"pattern", unknown>, and it works at runtime because the concrete AppRscHandlerRoute structurally carries pattern. This is the load-bearing key that joins build-time writePrerenderIndex (prefetchHints[route.route]) to runtime lookup (__VINEXT_PREFETCH_HINTS__[route.pattern]). Adding an explicit optional pattern?: string to AppRouteTreePrefetchRoute would make that contract self-documenting and guard against a future rename silently breaking the precomputed-tree path (which has no runtime assertion that a hit was expected).

Verified correct (no action)

  • Affinity channel. renderAffinity is a monotonic counter used as (a) the head-map key and (b) affinityIndex % renderPorts.length pool routing, so HTML render, hints POST, and DELETE cleanup deterministically hit the worker that captured the head. Header is in VINEXT_INTERNAL_HEADERS, stripped by filterInternalHeaders, and only re-attached after === prerenderSecret at both boundaries (prod-server.ts:1141, app-rsc-handler.ts:1694). Not externally forgeable.
  • Head cleanup ordering. __collectPrefetchHints now reads + deletes the affinity head before the early return, so it no longer depends solely on the finally DELETE.
  • Body buffering in rscHandler (prerender.ts:1126) is required for POST forwarding of the hints body.
  • Build-artifact injection. injectPregeneratedConcretePaths deep-freezes the hint object in-process and emits a matching self-freezing IIFE into the bundle; runtime re-validates via isTreePrefetch/getPrecomputedTree (deep, cycle-guarded, optional pattern). Trusted build-time source only.
  • Second no-store gate (prerender.ts:1604) correctly still returns status: "skipped" while attaching hints for cache-component dynamic routes.

Overall: no correctness defects in the happy path and good coverage. Finding #1 is the one I'd want resolved (or explicitly waved off with reasoning) before merge — it converts a benign "no hints" into a build abort.

@ask-bonk

ask-bonk Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review posted to PR #2855.

Summary

I reviewed the segment prefetch inlining PR (a64a915) focusing on correctness, dev/prod parity, and build-failure modes. Type checks and focused Vitest suites (36/36) pass, and the two prior Bonk findings plus the upstream parity claim are correctly resolved.

One item worth fixing before merge:

  1. Hint-collection null becomes a hard build failure. __collectPrefetchHints returns null on route miss / pattern mismatch / route-handler, which the endpoint maps to a 404, which prerenderApp escalates to a thrown error aborting the entire build (prerender.ts:1594-1599). Since hint collection re-runs matchRoute(pathname) independently of the route being prerendered, any routing asymmetry (static route shadowing a dynamic pattern, intercept/parallel-route edge, rewrite) would turn a benign "no hints for this route" into a build abort. For a build-time perf optimization, this should degrade gracefully. I recommended distinguishing collector-error (5xx → throw) from route-mismatch (soft skip), plus a regression test.

Low-priority note:

  1. getPrecomputedTree reads route.pattern but AppRouteTreePrefetchRoute doesn't declare it — works via in-narrowing and structural typing, but this is the load-bearing key joining build-time storage to runtime lookup. An explicit pattern?: string would document the contract.

Verified correct (no action): affinity channel security & determinism, head-map cleanup ordering, body buffering, deep-freeze injection parity, and the second no-store gate for cache-component dynamic routes.

No happy-path correctness defects. Finding #1 is the one I'd want resolved or explicitly waved off before merge.

github run

@james-elicx

Copy link
Copy Markdown
Member Author

Addressed the exact-head Bonk finding in 4862dcb:

  • a 404/no matching route from build-time hint collection now soft-skips hints without losing the successfully rendered route
  • non-404 collector failures remain fatal
  • added a regression test covering the 404 path and documented pattern as the explicit precomputed-tree join key

Validation: 326/326 affected tests, full check + Knip, and the exact targeted Next.js wrapper at 7/7 tests + 7/7 snapshots.

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review for issues

@ask-bonk ask-bonk Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: segment prefetch inlining (4862dcb)

Independent pass on the final head, focused on whether the three prior Bonk findings are actually resolved and on the seams they didn't cover (layout-size alignment, param validator/producer parity, affinity concurrency safety, ESM injection ordering, cleanup on non-hint render paths). Focused Vitest green locally: app-route-tree-prefetch + app-prerender-endpoints + inject-pregenerated-paths = 36/36; app-router-next-config-codegen + prerender + request-pipeline = 236/236.

Verdict: no blocking issues. All three prior findings are correctly resolved and I found no new correctness defects.

Prior findings — confirmed resolved

  1. Double-measurement of children-slot pages — fixed. When pageValue === undefined, pageMeasurement reuses the children slot's Promise<number|null> rather than re-rendering (app-route-tree-prefetch.ts:356-379). One _prerender+gzip pass per children-slot page.
  2. Head-map cleanup ordering — fixed. __collectPrefetchHints reads + deletes the affinity head before the matchRoute/pattern-mismatch/route-handler early return (app-rsc-entry.ts:744-746), so cleanup is local to the collector and no longer relies solely on the finally DELETE.
  3. Hint-collection null → hard build failure — fixed. prerenderApp now soft-skips a 404 from the hints endpoint (prerender.ts: if (hintsResponse.status === 404) { await hintsResponse.body?.cancel(); }) while still throwing on non-404 non-ok (5xx collector errors). The successfully rendered route is preserved. tests/prerender.test.ts covers the 404 soft-skip path.

Fresh checks (no findings)

  • Affinity concurrency safety. Renders run under runWithConcurrency, so multiple renders can be in flight on the same worker (affinityIndex % poolSize). Because renderAffinity is a monotonic per-render counter used as the head-map key, concurrent renders on one worker never collide, and each render's finally issues its own keyed DELETE. HTML render, optional RSC render, hints POST, and DELETE cleanup all carry the same affinity, so they deterministically hit the worker that captured the head.
  • param validator ↔ producer parity. isTreePrefetch's param acceptance (type ∈ {d,c,oc}, key === null, siblings === null | string[]) exactly matches dynamicRouteTreeSegment output (app-route-tree-prefetch.ts:166-182). Static segments (param === null) are accepted. No legitimate producer output is rejected by the runtime re-validator.
  • route.pattern is present at runtime. getPrecomputedTree reads route.pattern; AppRscHandlerRoute declares pattern: string (app-rsc-handler.ts:173), and this PR adds the explicit optional pattern?: string to AppRouteTreePrefetchRoute, documenting the build↔runtime join key (prior finding #2).
  • Precomputed tree is served verbatim without stripMutableFields, correctly. The precomputed tree originates from build-time collector output that is already TreePrefetch-shaped (mutable fields stripped) and deep-frozen; the request path only JSON.stringifys it, so freezing is inert. The fallback (getPrecomputedTree → null) still runs buildTree + computePrefetchHints with HEAD_INLINE_SIZE, preserving pre-PR heuristic behavior.
  • ESM injection ordering. injectPregeneratedConcretePaths prepends the __VINEXT_PREFETCH_HINTS__ IIFE above the file's imports — same pattern as the existing pregenerated-paths block, which already ships in production. import hoisting makes a top-level statement before imports legal.
  • Security gating unchanged and sound. The prefetch-hints endpoint 403s without a matching prerender secret (prod-server.ts:1684-1689); the affinity header is read only when VINEXT_PRERENDER === "1" + secret matches (app-rsc-handler.ts:1691-1695, prod-server.ts:1141-1146), is in VINEXT_INTERNAL_HEADERS, and is stripped by filterInternalHeaders (regression test in request-pipeline.test.ts). Not externally forgeable.
  • No head-map leak on non-hint render paths. Fallback renders (isFallback) still capture a head but skip the hints POST; the config.prefetchInlining-gated finally DELETE always reclaims it. Errored renders that captured a head are likewise reclaimed.
  • Default-bundle regression avoided. generateRscEntry(..., prefetchInlining=false) omits app-route-tree-prefetch.js, createFromReadableStream, and __collectPrefetchHints (codegen assertions in app-router-next-config-codegen.test.ts), so the measurement runtime stays off the common path.

One low-priority note (non-blocking)

  • Layout-size positional alignment is an implicit contract. measureAppRouteTreePrefetchSizes builds layouts by iterating elements.__layoutIds in order, and buildTree reassociates them via modulesByTreePosition(measuredSizes.layouts, route.layoutTreePositions) — i.e. it assumes __layoutIds[i] corresponds to layoutTreePositions[i]. This holds today and the E2E parity suite exercises it, but the two orderings are produced by different modules with no shared assertion binding them. A regression here would silently mis-attribute a layout's measured size to the wrong tree position (a wrong inlining decision, not a crash). Worth a comment noting the ordering dependency, or a focused test that mismatches the two arrays. Low priority — not required for merge.

Overall: the PR is well-structured, well-tested, and the three review rounds have converged cleanly. Nothing here should block merge.

@ask-bonk

ask-bonk Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review posted to PR #2855.

Review outcome: no blocking issues

I did an independent pass on the final head (4862dcb), verifying the three prior Bonk findings are genuinely resolved and checking the seams they didn't cover. Focused Vitest green locally (36/36 + 236/236).

Prior findings confirmed resolved:

  1. Double-measurement of children-slot pages — reuses the slot's Promise for the page.
  2. Head-map cleanup ordering — delete now runs before the early return.
  3. Hint-collection null → hard build failure — prerenderApp soft-skips 404, throws only on 5xx; covered by a regression test.

Fresh checks (no findings): affinity concurrency safety (monotonic per-render key, no collision under runWithConcurrency), param validator↔producer parity, route.pattern present at runtime, precomputed tree served verbatim correctly (already stripped + frozen), ESM injection ordering (matches shipping pregenerated-paths pattern), security gating (403 without secret, header stripped), no head-map leak on fallback/errored renders, and default-bundle regression avoided via codegen gating.

One low-priority note (non-blocking): layout-size positional alignment (__layoutIds[i]layoutTreePositions[i]) is an implicit cross-module contract with no shared assertion binding it — a mismatch would silently mis-attribute a size rather than crash. Worth a comment or a focused test, but not required for merge.

The PR is well-structured, well-tested, and the review rounds have converged cleanly. Nothing should block merge.

github run

@james-elicx
james-elicx marked this pull request as ready for review August 10, 2026 07:28
@james-elicx

Copy link
Copy Markdown
Member Author

Backlog mapping for Actions run 31290819291: this PR appears to fix all three reported test/e2e/app-dir/segment-cache/prefetch-inlining/prefetch-inlining.test.ts failures (outlined chain, restart groups, and concrete dynamic-route hints). The exact targeted Next.js wrapper passes 7/7 with 7/7 snapshots at head 4862dcbc59f39251fea76d9c88652c9dd630ab04.

@james-elicx
james-elicx marked this pull request as draft August 10, 2026 10:07
@james-elicx
james-elicx marked this pull request as ready for review August 10, 2026 11:33
# Conflicts:
#	packages/vinext/src/build/prerender.ts
#	packages/vinext/src/server/app-prerender-endpoints.ts
#	packages/vinext/src/server/app-rsc-handler.ts
#	packages/vinext/src/server/headers.ts
#	packages/vinext/src/server/prod-server.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant