* feat(skip): add layout safety observation foundations
Introduce the per-layout observation primitives that later slices need
to decide whether retained static layouts can be reused. This slice
adds the machinery only; nothing reads the observations yet, so
behavior is unchanged.
- app-layout-param-observation: per-layout completeness, param keys,
structural scope, finite revalidate, request API, cacheLife, cache
tag, cacheable fetch, dynamic fetch, and unstable_cache observation
tracker.
- app-page-probe: bounded React server subtree probe that walks
layout-returned children including memo, forwardRef, and React.lazy
wrappers, enforces depth and node limits, and refuses single-use
iterable children.
- shims: thenable params observer for sync, await, destructure, and
enumeration. Cache, fetch, and unified request context record
unstable_cache, cacheable fetch, and request-scoped observation
state.
- app-page-params: structural segment-param scope so empty optional
catch-all targets are not treated as param-free downstream.
No skip transport behavior. Later slices wire the planner, manifest,
and encoder onto these observations.
* fix(skip): address review feedback on observation foundations
- Add currentFetchSoftTags reset in runWithIsolatedProbeDependencies to prevent soft tag leak from parent scope (matches runWithFetchCache isolation)
- Replace unnecessary JSON.stringify in cacheLife check with direct null comparison
- Add comment documenting intentional omission of markProbeComplete on probe errors (keeps completeness as unknown, safe fallback to render-and-send)
* fix(skip): record cacheable-fetch observation synchronously before first await
The patched fetch deferred observation and tag collection past
await buildFetchCacheKey(). If a layout probe started a cacheable
fetch and returned synchronously without awaiting it, the probe
snapshot would see cacheableFetchCount: 0 -- a false proof that
could let the skip planner reuse a stale retained layout.
Move observation and tag recording before the first await in the
cacheable branch. If key generation later fails and the fetch falls
back to dynamic, recording both is conservative (false unsafe costs
performance, not correctness).
Add an integration test that exercises the real patched fetch shim
without mocks to verify synchronous observation capture.
* fix(test): remove polluting fetch restore and catch background promise
Do not restore globalThis.fetch in test cleanup. ensureFetchPatch
sets a global _PATCH_KEY guard; restoring fetch without clearing
that marker makes later calls skip reinstallation silently.
Catch the background fetch promise inside the probe so the patched
fetch continuation (cache lookup, network fetch) cannot produce an
unhandled rejection after the synchronous assertions complete.
* chore: empty commit to trigger CI
* chore: empty commit to trigger CI
* fix(app-router): scope layout params and propagate layout head errors
Layouts received full route params, layout generateMetadata failures were swallowed, and layout-thrown forbidden()/unauthorized() fell back through the not-found boundary path. Those diverge from Next.js when nested layouts depend on segment params, when head generation fails before page render, or when HTTP access APIs are thrown from a layout.
The generated RSC entry now delegates param slicing, head resolution, and parent access-boundary selection to typed runtime helpers. The helpers keep route-specific imports in the entry while moving the behavioral core into unit-tested modules.
Tests port Next.js layout params, global-error, forbidden, and unauthorized coverage into focused helper and integration regressions.
* test(app-router): cover layout viewport error boundaries
Layout generateViewport() failures now intentionally share the same boundary path as layout generateMetadata() failures. Copilot flagged that this behavior change needed direct coverage.
Add dev and production-preview compat assertions for co-located error.tsx handling and global-error escalation, with fixtures that throw from layout generateViewport().
* chore(app-router): address head review nits