Repository navigation
Description
Problem (measured 2026-07-06, hec homepage)
On a shell HIT, the fast path replays the snapshot segments but still runs a full Flight serialization of the entire tree to build the inline hydration payload (__FLIGHT_DATA), and the SSR side then consumes that same stream again for the resume render. Measured on a 3.77MB storefront homepage: 1.09MB of __FLIGHT_DATA per request (245 chunks), verified to duplicate content already served from the stored prelude ("SPF pays off" hero et al. present in both). At the measured ~5MB/s workerd stream-generation throughput, this is roughly 200–300ms of per-request CPU whose output bytes are constant for the lifetime of the entry — the tax scales with page weight, not dynamism, inverting the fast-path promise. It also burns isolate CPU that competes with concurrent renders (the 2.5s PDP-prefetch starvation class).
Fix shape (the design doc's deferred "fragment splice")
The entry's snapshot already stores per-segment RSC-encoded fragments — the serialization work is already done at capture time (a waitUntil job, off the request path). The HIT payload assembly should:
- Emit the stored fragment strings verbatim (string copy) for replayed segments instead of deserialize → re-serialize;
- Serialize fresh only: loader rows, per-request metadata rows (nonce, pathname, params), handle records;
- Expand fragments on the consumer side through the same
deserializeResultpath the client cache seed already uses (per-record fragments have an in-tree precedent —segment-codecserializeResult/deserializeResult).
Segment-boundary fragments avoid the known raw Flight-row-surgery hazard (row-id collisions / shared-row dedupe) — each fragment is its own row space, deserialized independently, exactly like the seed.
Constraints / notes
- The SSR resume render itself is NOT removed (React re-renders above the holes and discards — inherent to the resume model). This issue is about the serialization half only; hole-free pages skipping resume entirely is PPR producer B: emit the shell entry at BUILD time for prerendered routes (Prerender + ppr = zero runtime capture) #699's territory.
- Coordinate the entry/storage layout with PPR producer B: emit the shell entry at BUILD time for prerendered routes (Prerender + ppr = zero runtime capture) #699 (producer B) — both shape
ShellCacheEntry; producer B's build-time entries should produce the same fragment layout so the serve path has one consumer. - Hydration-heavy test surface: exec-matrix + hydration-clean e2e (test-app + cloudflare-basic, dev + production), semantic matrix, seed/deserialize unit coverage.
References
- Measurements in the 2026-07-06 session (hec homepage decomposition: prelude 1MB stored, resume 2.74MB, flight 1.09MB, continuous ~5MB/s generation profile).
docs/design/shell-fast-path.md— fragment splice as the original design direction; v1 deliberately deferred it.- feat(ppr): shell fast path — HIT tails replay the handler layer; only loaders are dynamic #695 (fast path v1), fix(vite): prerender collection ran trieless — catch-all shadowed nested routes; Prerender + ppr composition coverage #697 (composition + capture hardening), PPR producer B: emit the shell entry at BUILD time for prerendered routes (Prerender + ppr = zero runtime capture) #699 (producer B).
Activity
perf(ppr): extend fragment passthrough to partial navigation replay (#…
docs(router): trim render-pass.ts comments to the invariants