Skip to content

feat(skip): enable proven static layout transport skips#1437

Open
NathanDrake2406 wants to merge 20 commits into
cloudflare:mainfrom
NathanDrake2406:nathan/726-skip-04-05-static-layout-skip-transport
Open

feat(skip): enable proven static layout transport skips#1437
NathanDrake2406 wants to merge 20 commits into
cloudflare:mainfrom
NathanDrake2406:nathan/726-skip-04-05-static-layout-skip-transport

Conversation

@NathanDrake2406
Copy link
Copy Markdown
Contributor

@NathanDrake2406 NathanDrake2406 commented May 22, 2026

Overview

Implements #726-SKIP-04/05 from #726.

Bonk: please read issue #726 for the architectural big picture before reviewing this slice.

Item Summary
Goal Enable RSC skip transport for layouts only when the server can prove the client already has the exact public static layout artifact.
Core change Client navigation sends a bounded reuse manifest, dispatch verifies it against current render proof, and RSC encoding omits only verified static layout records.
Safety boundary The manifest is a hint. Server verification, layout classification, cache proof, compatibility, payload hash, invalidation, structural per-layout param scope, request API observation, finite layout revalidate, and per-layout data observations decide whether bytes may be skipped.
Primary review path client-reuse-manifest, skip-cache-proof, app-page-render, app-page-dispatch, app-layout-param-observation, app-page-probe, app-page-route-wiring, app-page-params, app-browser-client-reuse-manifest, generated app-rsc-entry wiring.
Expected impact Proven static shared layouts can be retained across soft navigations. Param-scoped, data-dependent, finite-revalidate, dynamic, malformed, oversized, cache-unsafe, or unproven cases render and send normally.

Why

Skip transport is only correct when the retained client layout is indistinguishable from the target server layout for this request. That means the client manifest cannot authorize omission by itself, partial RSC responses cannot become shared cache artifacts, and a layout whose output can depend on params, data invalidation, or finite revalidation cannot be reused without stronger proof. This PR keeps those responsibilities at the server render boundary and treats uncertain layouts conservatively.

Area Principle / invariant What this PR changes
Client manifest Hints are untrusted input, and browser-produced hints must stay within the server verification budget. The browser sends a bounded manifest only for normal soft navigation, caps produced entries to the skip planner budget, and parsing still returns render/send by default.
Server proof Omission requires current target proof. Dispatch threads the parsed manifest into render lifecycle, where current layout flags, compatibility, variant, payload hash, invalidation, and static layout proof are checked.
Layout params Static classification is not enough if layout output can depend on params. The tracker records each layout's structural segment-param scope from route segment names, including optional catch-all names when the runtime value is empty. Any non-empty scope blocks skip for that layout.
Layout data and request APIs Static classification is not enough if layout output can depend on request APIs or cached/dynamic data. Each layout probe observes request APIs, cache life, fetch tags, cacheable fetches, dynamic fetches, and unstable_cache() usage in an isolated child request scope before skip verification. Any observed dependency blocks skip for that layout without leaking into sibling or parent probe observations.
Layout revalidate A finite ISR policy is not a permanent retained artifact proof. Per-layout observation records finite positive revalidate segment config, and skip rejects those layouts with SKIP_LAYOUT_REVALIDATE_PRESENT.
Probe shape and depth Dependencies can live below the shallow layout function body, and some layouts render chrome only when children is present. Probes must not mutate single-use children. Generated layout probes delegate into a typed helper that passes a terminal non-null child, evaluates returned server-component children including memo()/forwardRef()/React.lazy wrappers, and enforces bounded depth/node limits. Array branches count against those limits. Non-array iterables are rejected without iteration so skip falls back instead of consuming stateful children.
Param observation Direct reads still provide traceability and future proofing. Layout params observation records sync reads, await params, destructuring/enumeration, and in checks, while skip authorization now requires both complete observation and empty structural param scope.
Caching Skipped payloads are per-client transport artifacts. Enabled skip bypasses RSC capture and ISR writes, returns Cache-Control: no-store, must-revalidate, and avoids visited-response replay.

What changed

Scenario / surface Before After
Browser soft navigation No client reuse manifest was attached, and later producer limits could exceed the server verification budget. navigateRsc() attaches X-Vinext-Client-Reuse-Manifest for retained public static layouts, capped to the skip planner entry budget before byte-size trimming.
Server request path Parsed manifests could be dropped before render. The parsed manifest flows through handler, generated entry, dispatch, and render lifecycle.
Verified static layout RSC payload Full layout record was always sent. Proven layout records may be omitted from RSC payloads only. HTML still sends the full tree.
Shared caches A skipped RSC stream could be captured under the normal cache key. Skip-enabled responses are no-store and are not captured for ISR/page cache writes.
Dynamic segment layouts A layout classified static could pass params to children and be incorrectly retained across sibling values. Empty optional catch-all targets could look param-free because the runtime params object was {}. Resolved structural per-layout param scope gates skip. Layouts under dynamic, catch-all, or optional catch-all segment names render and send, even when the current value is empty or the shallow probe saw no param read.
Layout request/data dependencies Skip proof manufactured request-api-clean and cache-clean observations for static layouts, and client-facing layout flags could still advertise those layouts as static. Skip proof now requires complete per-layout observation, rejects layouts whose probe observed request APIs, cache life, fetch tags, cacheable fetches, dynamic fetches, or unstable_cache() usage, and publishes those unsafe observations as dynamic layout metadata.
Finite layout revalidate Static layout skip could still be considered for finite revalidate segment config. Finite positive layout revalidate is tracked per layout and blocks skip.
Layout metadata Runtime and build-time-static probes could publish "s" layout flags even after per-layout observation proved skip would be rejected. Layout flags now consult the same unsafe-observation predicate used by skip authorization, so request APIs, params, finite revalidate, cacheLife, cache tags, cacheable fetches, dynamic fetches, unstable_cache() usage, and incomplete probes publish "d".
Layout probe child shape Layouts that branch on children could hide chrome dependencies because probes rendered with children={null}. The probe renders layouts with an inert non-null React child, so children-truthy chrome branches are observed without descending into page content.
Wrapped layout children memo(), forwardRef(), and React.lazy returned below a layout could hide data dependencies from the probe, while arbitrary iterable children could be single-use. The bounded subtree probe renders through wrappers before skip verification, arrays consume the same depth and node budgets as element branches, and non-array iterables are treated as unprobeable without advancing them.
Browser visited cache Partial payloads could be restorable without matching retained layouts. Skip-pruned payload metadata is non-restorable, and approved commits preserve explicitly skipped retained layouts.
Maintainer review path
File What to review
packages/vinext/src/server/client-reuse-manifest.ts Parser limits, rejection codes, and the untrusted parse-time disposition contract.
packages/vinext/src/server/skip-cache-proof.ts Static-layout skip planning, exact compatibility requirements, mixed rejection traces, and over-budget fallback.
packages/vinext/src/server/app-page-render.ts Runtime upgrade from manifest hint to skip disposition, layout-only omission boundary, cache bypass, and param/request-api/data/revalidate observation rejection.
packages/vinext/src/server/app-page-dispatch.ts Request dispatch wiring, layout observation tracker ownership, and propagation into intercept and render paths.
packages/vinext/src/server/app-layout-param-observation.ts Per-layout completeness, observed param keys, structural param scope, finite revalidate, and isolated request API, cache life, cache tag, cacheable fetch, and dynamic fetch observations.
packages/vinext/src/server/app-page-probe.ts Bounded returned-subtree probing for layout server-component children, including memo()/forwardRef()/React.lazy wrappers, array traversal limits, and non-array iterable fail-closed behavior.
packages/vinext/src/server/app-page-route-wiring.tsx Route-specific layout probe helper, structural scope recording, finite revalidate recording, and generated-entry delegation boundary.
packages/vinext/src/server/app-page-params.ts Difference between runtime params values and structural segment-param scope names.
packages/vinext/src/entries/app-rsc-entry.ts Generated App Router wiring for parsed manifests, build-page callbacks, layout probes, and helper delegation.
packages/vinext/src/shims/fetch-cache.ts Cacheable fetch observation alongside existing dynamic fetch and tag collection.
packages/vinext/src/shims/thenable-params.ts Sync and async params observation, including await params, destructuring, enumeration, and promise continuations.
packages/vinext/src/server/app-browser-client-reuse-manifest.ts and browser entry tests Bounded manifest production from visible AppElements state, producer-side verification-budget capping, and navigation-only attachment.
Validation
Command Coverage
vp check Formatting, lint, and typecheck. Passed: all 1406 files formatted, no warnings, lint errors, or type errors in 649 files.
vp test run tests/app-page-probe.test.ts tests/app-layout-param-observation.test.ts tests/app-page-dispatch.test.ts Layout subtree probing, including memo()/forwardRef()/React.lazy wrappers and array traversal limits, isolated per-layout dependency observation, plus dispatch skip rejection for wrapped cache-tagged layout children. Passed: 3 files, 43 tests.
`vp test run tests/app-page-probe.test.ts -t "single-use iterables subtree .*limits"`
vp test run tests/app-page-probe.test.ts tests/app-page-dispatch.test.ts tests/app-page-render.test.ts tests/app-layout-param-observation.test.ts Probe, dispatch, render lifecycle, and tracker coverage after non-array iterables became fail-closed and after unsafe layout observations were bridged into client-facing layout flags. Passed: 4 files, 80 tests.
`vp test run tests/app-page-dispatch.test.ts -t "request-api layouts dynamic probe observes request APIs"`
vp test run tests/app-page-dispatch.test.ts tests/app-layout-param-observation.test.ts tests/app-page-render.test.ts tests/skip-cache-proof.test.ts Dispatch, tracker, render lifecycle, and skip planner coverage after request API observations were added to the layout skip gate. Passed: 4 files, 82 tests.
vp test run tests/app-layout-param-observation.test.ts tests/app-page-dispatch.test.ts -t "unstable_cache" Focused regressions that layout probes record unstable_cache() dependencies on cache miss and hit, and that verified static-layout skip falls back to render/send for retained layouts backed by unstable_cache() data. Passed: 2 tests.
vp test run tests/app-layout-param-observation.test.ts tests/app-page-dispatch.test.ts tests/app-page-render.test.ts tests/shims.test.ts tests/unified-request-context.test.ts tests/skip-cache-proof.test.ts Cache shim, unified request context, layout observation, dispatch, render lifecycle, and skip planner coverage after adding the unstable_cache() observation channel. Passed: 6 files, 1094 tests.
vp test run tests/app-browser-client-reuse-manifest.test.ts tests/client-reuse-manifest.test.ts tests/skip-cache-proof.test.ts Browser producer cap, manifest parser limits, and skip planner budget behaviour. Passed: 3 files, 42 tests.
vp test run tests/app-page-render.test.ts tests/app-router.test.ts Render lifecycle plus App Router integration after the wrapped-subtree probe change. Passed: 2 files, 364 tests.
`vp test run tests/app-page-dispatch.test.ts -t "finite revalidate cache-tagged data"`
`vp test run tests/app-page-route-wiring.test.ts -t "children are present returned layout children"`
vp test run tests/app-page-route-wiring.test.ts tests/app-page-execution.test.ts tests/app-page-probe.test.ts tests/app-page-dispatch.test.ts tests/app-page-render.test.ts tests/app-layout-param-observation.test.ts Route wiring, lifecycle probing, subtree probing, dispatch, render lifecycle, and tracker coverage after child-shape-equivalent probing and the build-time special-error flag regression fix. Passed: 6 files, 136 tests.
vp test run tests/app-router.test.ts tests/entry-templates.test.ts tests/app-page-route-wiring.test.ts Generated entry wiring, route-wiring helper coverage, and App Router integration after probe child-shape changes. Passed: 3 files, 380 tests.
vp test run tests/app-page-dispatch.test.ts tests/app-page-route-wiring.test.ts tests/app-page-probe.test.ts tests/app-page-render.test.ts tests/app-router.test.ts Dispatch, route wiring, layout subtree probing, render lifecycle, and App Router integration. Passed: 5 files, 428 tests.
vp test run tests/app-page-probe.test.ts tests/app-page-dispatch.test.ts tests/app-page-render.test.ts tests/app-browser-client-reuse-manifest.test.ts tests/client-reuse-manifest.test.ts tests/skip-cache-proof.test.ts Skip planner, manifest parser/producer, render lifecycle, request dispatch, subtree probing, and cache/data dependency rejection. Passed: 6 files, 109 tests.
vp test run tests/app-router.test.ts tests/entry-templates.test.ts tests/app-page-request.test.ts tests/app-elements.test.ts tests/app-page-route-wiring.test.ts tests/thenable-params.test.ts tests/app-page-params.test.ts Generated entry wiring, App Router integration, request helper regression, payload encoding, route wiring, thenable params, and structural param scope. Passed: 7 files, 494 tests.
vp run vinext#build Package build and generated type output. Passed with the existing unresolved virtual/private import warnings for private-next-instrumentation-client, virtual:vinext-rsc-entry, and virtual:vite-rsc/client-references.
PLAYWRIGHT_PROJECT=app-router pnpm exec playwright test tests/e2e/app-router/layout-persistence.spec.ts --project=app-router -g "dynamic segment layout counter survives param navigation while content updates" Regression for sibling dynamic values preserving stale layout params. Passed: 1 test.
vp test run tests/app-page-execution.test.ts -t "build-time classified layout throws" Local reproduction for the failed CI unit test. Passed: 1 test.
git diff --check Whitespace sanity check. Passed.
Commit hook Checked-file formatting, lint/typecheck, and knip. Passed.

Previously on this PR, the navigation manifest and visited-cache e2e slice also passed:

CI=true PLAYWRIGHT_PROJECT=app-router npx playwright test tests/e2e/app-router/build-id-navigation.spec.ts tests/e2e/app-router/nextjs-compat/actions-revalidate.spec.ts tests/e2e/app-router/nextjs-compat/search-params-key.spec.ts tests/e2e/app-router/advanced.spec.ts -g "client reuse manifest|visited RSC|discarded server action|router.push|refresh on direct target"
Risk / compatibility
Surface Risk handling
Public API No public API change. The new header is an internal vinext client/server transport hint.
RSC transport Omission is restricted to layout element keys on RSC responses. HTML, pages, templates, routes, slots, dynamic layouts, structurally param-scoped layouts, finite-revalidate layouts, and data-dependent layouts still send full payloads.
Cache correctness Skip-enabled responses are no-store, bypass RSC capture, and cannot be written to ISR under a normal RSC key.
Param-dependent layouts Conservative by design. Any non-empty structural layout param scope blocks skip for that layout, including optional catch-all segments whose runtime value is empty and cases where params are only passed to child components.
Request/data-dependent layouts Conservative by design. Request APIs, cache life, fetch tags, cacheable fetches, dynamic fetches, and unstable_cache() usage observed during isolated layout probing block skip rather than relying on synthetic clean proof. Wrapped memo()/forwardRef()/React.lazy children are probed too. unstable_cache() trace metadata uses hashed keys and tag metadata, not raw keys or values.
Finite revalidate Conservative by design. Finite positive layout revalidate blocks skip because the retained layout has a revalidation policy, not a permanent artifact proof.
Manifest cardinality The client manifest remains bounded, browser-produced manifests stay within the server verification budget, and skipped payloads are not made shared cache variants.
Existing apps Unproven cases fall back to current render/send behavior. The main risk is missed optimization, not stale UI.
Non-goals

References

Reference Why it matters
#726 Architectural context for the skip transport, compatibility, and cache-proof model.
#726-SKIP-04/05 Task slice implemented by this PR.
tests/app-page-dispatch.test.ts Lowest honest dispatch-boundary coverage for verified skip, dynamic fallback, cache bypass, param-scoped layout rejection, request-API rejection, unstable_cache() rejection, optional catch-all empty-target rejection, data-dependent layout rejection including wrapped children and React.lazy children, finite-revalidate rejection, and hostile param-dependent layouts.
tests/app-browser-client-reuse-manifest.test.ts Focused coverage for browser manifest production, including server verification-budget capping.
tests/app-page-route-wiring.test.ts Focused coverage for layout probe helper delegation, returned subtree probing, and revalidate tracking.
tests/app-layout-param-observation.test.ts Focused coverage for probe-local request API, fetch/cacheLife, and unstable_cache() dependency observation isolation across child, parent, and sibling layouts.
tests/app-page-probe.test.ts Focused coverage that layout probing invokes returned server-component children, including memo()/forwardRef()/React.lazy wrappers, enforces array depth/node limits, and does not consume single-use iterable children.
tests/app-page-params.test.ts Focused coverage for runtime params value scoping versus structural layout param scope.
tests/thenable-params.test.ts Focused coverage for sync, async, and destructured params observation.

Authorize skip transport only after a parsed client reuse manifest entry cross-checks against a public static layout cache proof. Add a skip transport planner that preserves rejection traces, falls back to render/send for absent, rejected, oversized, or over-budget manifests, and uses canonical parsed entry ids rather than verifier-provided ids.

Wire enabled static-layout skip disposition into the RSC payload encoder so only proven layout records are omitted. HTML responses and non-layout entries still render and send the full tree.
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 22, 2026

Open in StackBlitz

npm i https://pkg.pr.new/vinext@1437

commit: fcaac5d

@NathanDrake2406 NathanDrake2406 marked this pull request as draft May 22, 2026 10:58
Canonical RSC requests could parse X-Vinext-Client-Reuse-Manifest but still send full AppElements payloads. The parsed manifest stopped at the App RSC handler, so dispatch and render never had the proof input needed to plan static layout omission.

Thread the parsed manifest through generated App RSC dispatch into dispatchAppPage and renderAppPageLifecycle, then build the skip disposition from current static layout classifications and layout-scoped proof metadata before encoding the RSC payload. HTML renders still ignore skip dispositions.

Tests cover header parsing into dispatch, generated entry wiring, static-only dispatch omission, dynamic fallback, and HTML full-payload behaviour.
Verified skip transport can omit layout records for a specific client. Caching that partial RSC stream under the normal route key is unsafe because later clients may not have the omitted layout locally.

Bypass RSC capture and ISR cache scheduling when skip transport is enabled, force the client response to no-store, and emit a debug trace for the skipped cache write. Tests cover the render lifecycle cache boundary and the manifest-driven dispatch path.
@james-elicx
Copy link
Copy Markdown
Member

/bigbonk review

Copy link
Copy Markdown
Contributor

@ask-bonk ask-bonk Bot left a comment

Choose a reason for hiding this comment

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

Review: feat(skip): enable proven static layout transport skips

The architecture is sound. The planner stays untrusted-at-parse / verified-at-runtime, the cache bypass is correctly wired end-to-end, and the test coverage is thorough. The inline comments below are refinements, not blockers.

What's good

  • Trust boundary is clean. Parser disposition stays renderAndSend; only the runtime planner can upgrade to SKIP_STATIC_LAYOUT_VERIFIED after server-side verification. The hardening from #726 is preserved.
  • Cache bypass is correct. shouldCaptureRscForCacheMetadata is gated off, capturedRscDataPromise is null, scheduleAppPageRscCacheWrite returns false, finalizeAppPageRscCacheResponse returns the response unchanged, and no-store, must-revalidate is set. No shared cache poisoning path.
  • Layout-only omission guard is correct. createSkippedLayoutIds re-parses each entry ID through parseAppElementsWireElementKey and only admits kind: "layout", so a malicious manifest that smuggles a page/template/route entry ID into skippedEntryIds cannot omit non-layout content.
  • Budget enforcement — the maxEntriesToVerify check rejects over-budget manifests before the verifier loop runs. The test for this is explicit.
  • Entry ID hardeningskippedEntryIds is populated from entry.id (the canonical parsed entry), not from verifier-returned IDs. The "uses the verified manifest entry id instead of trusting verifier-provided skipped ids" test proves this.
  • HTML path is untouched — skip disposition is only passed to encodeOutgoingPayload for RSC requests, and the dispatch test proves HTML responses still contain the full tree.

Nits and suggestions below.

Comment thread packages/vinext/src/server/app-page-render.ts
Comment thread packages/vinext/src/server/app-page-render.ts
Comment thread packages/vinext/src/server/skip-cache-proof.ts Outdated
Comment thread packages/vinext/src/server/skip-cache-proof.ts
Comment thread packages/vinext/src/server/app-elements-wire.ts Outdated
Comment thread packages/vinext/src/server/app-page-render.ts
Comment thread tests/app-page-dispatch.test.ts
Comment thread packages/vinext/src/server/app-page-render.ts Outdated
@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk Bot commented May 22, 2026

Review submitted. Here's a summary of what I found:

Overall: The PR is solid. The trust boundary design from #726 is correctly preserved — parse-time disposition stays renderAndSend, and only the runtime planner can upgrade to SKIP_STATIC_LAYOUT_VERIFIED after server-side verification. The cache bypass is correctly wired end-to-end (capture disabled, ISR write skipped, no-store set, no shared cache poisoning path).

8 inline comments posted, all non-blocking refinements:

  1. Dead verifyEntry callback in the non-record/no-compatibility fallback path — the maxEntriesToVerify: 0 budget means the callback can never execute. Suggested simplification.
  2. Synthetic render observation for static layout verification assumes probe-time classification is authoritative — suggested documenting this assumption.
  3. Duplicate type aliases (StaticLayoutClientReusePayloadHashInput and StaticLayoutClientReuseArtifactCompatibilityInput) have identical fields — could be unified.
  4. Budget check conflates invalid vs exceeded — both produce SKIP_ENTRY_COUNT_EXCEEDED with misleading maxEntryCount: 0 for invalid budgets.
  5. Empty Set allocation on hot pathcreateSkippedLayoutIds allocates a new empty Set on every response when skip is disabled. Suggested module-level constant.
  6. Missing cacheState is intentional but non-obvious — the skip-bypass policy omits cacheState which has a downstream effect on preserveClientResponseHeaders. Suggested documenting intent.
  7. Dispatch test missing waitUntil assertion — render-level test covers it but dispatch boundary test doesn't verify no ISR write was scheduled.
  8. Budget constant placementSTATIC_LAYOUT_SKIP_VERIFICATION_ENTRY_BUDGET is policy, could live in skip-cache-proof.ts.

github run

Browser navigation could only benefit from skip transport when an external caller manually supplied X-Vinext-Client-Reuse-Manifest. That left normal App Router soft navigations rendering full layout payloads even when the current visible state retained a proven public static layout.

The missing boundary was the browser request path. Build a bounded manifest from visible AppElements metadata, reuse the same static-layout compatibility and payload-hash proof contract as server verification, and attach it through createRscRequestHeaders only for navigation fetches.

Also folds in Bonk review refinements around skip planner policy ownership, invalid verification budgets, synthetic static-layout observations, no-store cache semantics, and dispatch-level waitUntil coverage.
@NathanDrake2406 NathanDrake2406 marked this pull request as ready for review May 22, 2026 12:31
@james-elicx james-elicx marked this pull request as draft May 22, 2026 13:17
@chatgpt-codex-connector
Copy link
Copy Markdown

To use Codex here, create an environment for this repo.

@NathanDrake2406
Copy link
Copy Markdown
Contributor Author

@codex review

@NathanDrake2406 NathanDrake2406 marked this pull request as ready for review May 22, 2026 14:04
Copilot AI review requested due to automatic review settings May 22, 2026 14:04
@NathanDrake2406 NathanDrake2406 marked this pull request as draft May 22, 2026 14:04
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR enables verified static-layout skip transport for App Router RSC navigations by introducing an untrusted client reuse manifest header, parsing it at the canonical RSC boundary, and upgrading to an enabled skip disposition only after server-side proof cross-checks at the render lifecycle boundary. It also ensures skipped (client-specific) RSC payloads bypass shared caching and are returned with Cache-Control: no-store, must-revalidate.

Changes:

  • Add a browser-side producer for a bounded X-Vinext-Client-Reuse-Manifest derived from visible retained static layout entries, and attach it to real RSC navigation requests.
  • Add server-side skip transport planning/proofing to upgrade from parsed (disabled) disposition to SKIP_STATIC_LAYOUT_VERIFIED, and omit only proven layout records from RSC payload encoding.
  • Add extensive wiring + boundary tests (planner behavior, request normalization/dispatch plumbing, cache bypass/no-store behavior, and e2e header emission).

Reviewed changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/skip-cache-proof.test.ts Expands unit coverage to assert verified entries produce enabled skip plans and that budgets/oversize/malicious cases fall back safely.
tests/e2e/app-router/build-id-navigation.spec.ts Adds Playwright coverage proving RSC soft navigations send a bounded client reuse manifest header.
tests/app-rsc-handler.test.ts Verifies parsed client reuse manifest hints are threaded from canonical RSC requests into page dispatch.
tests/app-rsc-cache-busting.test.ts Ensures manifest header is attached without becoming a shared cache-variant input.
tests/app-router.test.ts Asserts generated dispatchMatchedPage wiring includes clientReuseManifest threading.
tests/app-page-render.test.ts Adds render lifecycle coverage for skip omission, no-store cache policy, and HTML non-omission behavior.
tests/app-page-dispatch.test.ts Adds dispatch-boundary integration coverage for verified static-layout omission (RSC only) and cache bypass.
tests/app-elements.test.ts Validates outgoing payload builder omits only layout keys when skip is enabled and preserves metadata/layout flags.
tests/app-browser-client-reuse-manifest.test.ts New tests for browser manifest construction (static-only, retained-only) and byte-budget trimming.
packages/vinext/src/server/static-layout-client-reuse-proof.ts Introduces shared proof helpers for static-layout compatibility shaping + payload hash computation.
packages/vinext/src/server/skip-cache-proof.ts Adds skip transport planner (createClientReuseSkipTransportPlan) and upgrades verified cross-checks to enabled skip disposition.
packages/vinext/src/server/client-reuse-manifest.ts Extends skip disposition type to include SKIP_STATIC_LAYOUT_VERIFIED (enabled) alongside disabled mode.
packages/vinext/src/server/app-rsc-handler.ts Threads clientReuseManifest through handleAppRscRequest to dispatch boundary.
packages/vinext/src/server/app-rsc-cache-busting.ts Allows createRscRequestHeaders() to attach the manifest header when provided.
packages/vinext/src/server/app-page-render.ts Builds runtime skip disposition at render lifecycle boundary; omits proven layouts from RSC payload and bypasses RSC cache writes (no-store).
packages/vinext/src/server/app-page-dispatch.ts Threads clientReuseManifest through dispatch into render lifecycle.
packages/vinext/src/server/app-elements-wire.ts Applies skip disposition during outgoing payload encoding by filtering proven layout keys only.
packages/vinext/src/server/app-browser-entry.ts Attaches the client reuse manifest header to real navigation RSC fetches (prefetch unchanged).
packages/vinext/src/server/app-browser-client-reuse-manifest.ts New browser helper to construct a bounded manifest from visible AppElements metadata.
packages/vinext/src/entries/app-rsc-entry.ts Updates generated entry wiring to pass clientReuseManifest into app page dispatch/render.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/vinext/src/server/app-page-render.ts
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 58dfd28128

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/vinext/src/server/app-browser-entry.ts
Skip transport sends partial RSC payloads only for clients that already hold retained static layouts. Treating those payloads as restorable visited-cache entries, sending manifests on refresh, or skipping layouts marked dynamic by runtime segment config breaks that invariant.

The previous wiring relied on generic payload metadata and build-time classifications, while hard-navigation recovery had drifted around the navigation controller guard.

Record skipped layout ids in AppElements metadata, deny visited-cache restoration for skip-pruned payloads, preserve skipped layouts from current state on approved commits, restrict browser manifests to navigate requests, honor runtime dynamic segment config, and route hard navigations through the controller.
Compatibility-map acceptance lets a retained layout hint verify across compatible canary and rollback deployments, but it does not prove byte-for-byte transport identity. Treating that accepted compatibility as permission to omit layout bytes breaks the cache proof boundary when deployments only share a declared compatibility set.

Keep the cross-check verified in that case, but return the disabled skip disposition unless the manifest entry compatibility exactly matches the current artifact compatibility. Add regression coverage for shared and stale canary/rollback compatibility maps.
…05-static-layout-skip-transport

# Conflicts:
#	tests/skip-cache-proof.test.ts
@NathanDrake2406 NathanDrake2406 marked this pull request as ready for review May 22, 2026 16:00
@NathanDrake2406
Copy link
Copy Markdown
Contributor Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b04d9b7273

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/vinext/src/server/app-page-render.ts
@james-elicx
Copy link
Copy Markdown
Member

/bigbonk review

Copy link
Copy Markdown
Contributor

@ask-bonk ask-bonk Bot left a comment

Choose a reason for hiding this comment

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

Review: feat(skip): enable proven static layout transport skips

I read the full diff, the #726 architectural spec, and the prior reviews from Bonk, Copilot, and Codex. The architecture is well-designed and the implementation is disciplined. I'm focusing on areas the other reviews didn't cover or where I disagree with their findings.

Correctness assessment

The end-to-end trust boundary is sound:

  1. Parse-time disposition stays disabled. parseClientReuseManifestHeader always returns enabled: false. Only createClientReuseSkipTransportPlan at render time can upgrade to SKIP_STATIC_LAYOUT_VERIFIED.
  2. Cache bypass is complete. shouldCaptureRscForCacheMetadata is false, capturedRscDataPromise is null, waitUntil is never called, and no-store, must-revalidate is set.
  3. Visited-cache poisoning is prevented. isCacheRestorableAppPayloadMetadata returns false when skippedLayoutIds.length > 0 (line 157 of app-browser-state.ts), so skip-pruned payloads are never stored. The Codex P1 comment about visited-route cache keying is a false positive — the store path is gated off entirely.
  4. Layout-only omission guard is structurally correct. createSkippedLayoutIds re-parses through parseAppElementsWireElementKey and only admits kind: "layout".
  5. Render dependency release on skip is correct. releaseAppElementRenderDependency is called for skipped entries so downstream renderAfterAppDependencies barriers don't hang.
  6. CDN cache coherence is correct. The manifest header is intentionally excluded from createCacheBustingInput and VINEXT_RSC_VARY_HEADER, so CDN caches are not manifest-dimensioned. A CDN-cached full response is harmlessly served to manifest-bearing clients.

Param scope design is the strongest part

The structural param scope tracking (via resolveAppPageSegmentParamScopeKeys) is the right conservative approach. It blocks skip for any layout whose ancestor segments include dynamic, catch-all, or optional catch-all params — regardless of whether the layout component actually reads them. This is correct because:

  • A layout under [slug] could receive params and pass them to children without reading them itself
  • The probe-time observation only catches direct reads, not pass-through
  • Optional catch-all segments whose runtime value is []/{} still have non-empty structural scope

The three-gate rejection order in rejectParamDependentStaticLayout (incomplete observation → non-empty scope → observed reads) is clean and each gate has a distinct rejection code for traceability.

On the Codex P2 concern about synthetic observations

The second Codex comment about cacheTags: [] and dynamicFetches: [] in synthetic observations raises a real concern worth tracking. A layout that does await fetch(url, { next: { tags: ['config'] } }) without calling request APIs would be classified "s" at probe time, but revalidateTag('config') would change its output. The synthetic observation with cacheTags: [] means the skip proof doesn't know about the config tag. This is a known scope limitation for this PR (no tag-based invalidation tracking for skip proof), but it should be tracked as a future correctness gap.

Items below — all non-blocking refinements

See inline comments.

Comment thread packages/vinext/src/server/app-layout-param-observation.ts
Comment thread packages/vinext/src/server/app-page-render.ts
Comment thread packages/vinext/src/server/app-browser-client-reuse-manifest.ts
Comment thread packages/vinext/src/server/app-page-dispatch.ts
Comment thread packages/vinext/src/server/app-page-render.ts
Comment thread packages/vinext/src/entries/app-rsc-entry.ts Outdated
Comment thread packages/vinext/src/server/app-browser-state.ts
Static layout skip could still be authorised from a synthetic cache-clean observation. That was wrong when the retained layout output depended on cached fetches, dynamic fetches, fetch tags, or use-cache cache life discovered during layout probing.

The violated invariant was that transport omission must be proven from the target layout's actual request observation, not only from static classification. Record per-layout data dependency observations during layout probes, evaluate returned server-component children under bounded probe limits, and reject skip when any data dependency is observed.

Tests cover a nested layout child that records cache-tagged data and prove the RSC payload preserves that layout instead of omitting it.
Static-layout skip still treated finite layout revalidate segment config as eligible when build-time classification marked the layout static. That is too strong because a finite ISR policy means the retained layout is not a permanent artifact.

The root assumption was that static classification alone described transport reusability. Record finite positive layout revalidate values in the per-layout observation, reject skip with SKIP_LAYOUT_REVALIDATE_PRESENT, and move generated layout probing into a typed helper that records skip observation scope.

Tests cover finite-revalidate layouts rendering and sending normally, plus the helper's subtree probing and revalidate tracking.
@NathanDrake2406
Copy link
Copy Markdown
Contributor Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7bbf954625

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/vinext/src/server/app-page-probe.ts Outdated
Static-layout skip could still approve a layout whose returned child component was wrapped in React.memo() or forwardRef(). The subtree probe treated object element types as opaque and visited only props.children, so cache tags or params observed inside those wrapped children were invisible.

Render through memo and forwardRef wrappers during the bounded layout subtree probe, preserving the existing render-and-send fallback on probe failures.

Tests cover pure probe invocation for memo, forwardRef, and memo(forwardRef), plus dispatch rejection for wrapped cache-tagged layout children.
@NathanDrake2406
Copy link
Copy Markdown
Contributor Author

@codex review

@NathanDrake2406 NathanDrake2406 requested a review from Copilot May 22, 2026 17:16
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7702c682cb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/vinext/src/server/app-layout-param-observation.ts
Comment thread packages/vinext/src/server/app-page-probe.ts
Comment thread packages/vinext/src/server/app-page-probe.ts
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 40 out of 40 changed files in this pull request and generated 1 comment.

Comment thread packages/vinext/src/server/app-layout-param-observation.ts
Static-layout skip could over-share or under-observe probe dependency state. Fetch tags, cacheable fetch URLs, dynamic fetch URLs, and cacheLife were read from request-wide collections, so dependencies from one layout probe could be attributed to later probes.

The violated invariant was that skip eligibility must be proven from the layout subtree being considered, not from neighbouring probes or from wrapper shapes the probe cannot see. Isolate each layout probe's dependency observation in a child request scope, render through React.lazy wrappers, and count array and iterable branches against the existing subtree depth and node budgets.

Tests cover per-layout dependency isolation, lazy cache-tagged layout children preserving the RSC layout record, and array depth and node limit enforcement.
@NathanDrake2406
Copy link
Copy Markdown
Contributor Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6de623645a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/vinext/src/server/app-browser-client-reuse-manifest.ts
Browser soft navigation could emit more retained layout entries than the server skip planner will verify. When a valid manifest exceeded that verification budget, the server rejected the whole hint with SKIP_ENTRY_COUNT_EXCEEDED and skip transport stayed disabled for routes with many retained static layouts.

The mistaken invariant was treating the parser's hostile-input entry limit as the producer budget. Keep the parser envelope larger, share the server verification budget through the manifest protocol module, and cap browser-produced manifests to that budget before byte-size trimming.

The regression test builds a route with more retained static layouts than the verification budget and asserts the browser manifest only contains the verifiable prefix.
@NathanDrake2406
Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Static-layout skip could still authorize omission after a layout probe observed request APIs. Build-time or segment-config static classification can skip isolated dynamic detection, so a retained layout that reads headers(), cookies(), draftMode(), connection(), params, or search params needed an independent runtime negative proof before bytes could be omitted.

The violated invariant was that skip proof must be based on evidence owned by the per-layout observation tracker, not a fabricated empty request API observation. Record request API usage inside each isolated layout probe, reject observed request APIs before static-layout proof construction, and feed the completed per-layout request API observation into the synthetic cache proof.

Tests cover isolated request API tracking across layout probes and a dispatch regression where a static-classified layout probe observes headers usage and the RSC payload still includes the layout.
@NathanDrake2406
Copy link
Copy Markdown
Contributor Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1863f07ab5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/vinext/src/server/app-page-probe.ts Outdated
Layout subtree probing consumed arbitrary iterable children before the real RSC render. If an app returned a single-use iterator or shared generator from a layout subtree, the probe could advance it and leave the real render with missing children.

The mistaken invariant was treating all iterables like arrays. Arrays are stable values to traverse, but arbitrary iterables can be stateful, so the probe now rejects non-array iterables without advancing them. That keeps the real render authoritative and makes skip fall back instead of mutating output.

The regression verifies a shared generator is still unconsumed after the subtree probe rejects it.
@NathanDrake2406
Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Layout probes could record unsafe static-reuse observations while still publishing an "s" layout flag. That was safe for server skip authorization, but it let the browser keep proposing manifest entries for layouts that could never be accepted.

The violated invariant was that client-facing layout flags should be derived from the same per-layout evidence used by skip authorization. Treat incomplete observations, structural params, observed params, request APIs, finite revalidate, cacheLife, cache tags, cacheable fetches, and dynamic fetches as dynamic for layout metadata.

Tests cover both build-time-static false positives and runtime-probed request API layouts publishing "d" while still rendering and sending the layout.
@NathanDrake2406
Copy link
Copy Markdown
Contributor Author

@codex review

Layout skip probes rendered layouts with children set to null. That could hide layout chrome dependencies behind children truthiness checks, producing a clean static observation for a layout whose real render would observe request or data dependencies.

The violated invariant was that per-layout skip evidence must be gathered under a child shape equivalent to the real App Router render. Probe layouts now receive a terminal non-null React child, while the subtree probe treats it as inert instead of descending into page content.

This also restores build-time layout flags before special-error probes so short-circuit responses keep the same metadata contract covered by CI. Tests cover children-dependent chrome observations and the special-error flag regression.
@NathanDrake2406
Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Static layout skip could treat layouts that read unstable_cache() data as clean. That was unsafe because cached data can be tagged, revalidated, and invalidated without passing through the fetch, cacheLife, request API, or params observation channels.

The violated invariant was that skip proof must observe every layout data dependency source before synthesizing a static-layout proof. unstable_cache() now records a bounded observation for every invocation, including cache hits, using hashed keys and tag metadata rather than raw keys or values.

Layout observation carries those dependencies into skip rejection and client-visible layout flags. Tests cover cache-miss and cache-hit observation plus dispatch fallback for a retained static layout whose probe reads unstable_cache() data.
@NathanDrake2406
Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

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.

3 participants