[GH-ISSUE #726] App Router layout persistence: Route Manifest and Navigation Planner architecture #155

Open
opened 2026-05-06 12:37:42 +02:00 by BreizhHardware · 2 comments

Originally created by @NathanDrake2406 on GitHub (Mar 31, 2026).
Original GitHub issue: https://github.com/cloudflare/vinext/issues/726

App Router layout persistence: Route Manifest and Navigation Planner architecture

What This Issue Is Really About

The flat keyed AppElements payload solved the first layout-persistence problem: the browser can merge layout, page, template, slot, and route entries instead of replacing the whole App Router tree.

That bridge should stay.

But the flat map must not become the router's brain.

Vinext now needs to move route meaning out of:

wire keys
missing payload entries
cache-key suffixes
mounted-slot headers
local activeNavigationId checks
runtime cache hits
hint-store reads
artifact-store object existence

The target architecture is not a giant rewrite. It is a controlled migration from implicit meaning to owned decisions:

flat payload transport
  + visible navigation lifecycle authority
  + minimal Route Manifest data
  + small pure NavigationPlanner
  + event-specific commit gate
  + semantic cache coherence, introduced narrowly
  + runtime profiles that execute approved work
  + React projection at the end

The discipline is:

No proof, no reuse.
No proof, no skip.
No proof, no visible commit.
No proof, use the safest event-specific fallback.

But that law has an important qualifier:

Safety fallback must be event-specific and measured.
It must not turn routine dynamic observations into universal cache misses.

Correctness is mandatory. Performance comes from narrow, proven reuse, not optimism.

Architecture Overview

flowchart TD
  subgraph Legend["Who owns what (one job per box)"]
    direction TB
    LG["<table cellpadding='8' cellspacing='0' style='border-collapse:collapse;width:640px'><tr><td align='left' style='width:240px;white-space:nowrap'><b>Route Manifest builder</b></td><td align='left' style='white-space:nowrap'>what can exist</td></tr><tr><td align='left' style='white-space:nowrap'><b>Visible route state + snapshot</b></td><td align='left' style='white-space:nowrap'>what is currently visible</td></tr><tr><td align='left' style='white-space:nowrap'><b>Event</b></td><td align='left' style='white-space:nowrap'>what happened</td></tr><tr><td align='left' style='white-space:nowrap'><b>NavigationPlanner</b></td><td align='left' style='white-space:nowrap'>what event means</td></tr><tr><td align='left' style='white-space:nowrap'><b>Navigation commit gate</b></td><td align='left' style='white-space:nowrap'>may become visible?</td></tr><tr><td align='left' style='white-space:nowrap'><b>Approved commit</b></td><td align='left' style='white-space:nowrap'>mutates browser / store</td></tr><tr><td align='left' style='white-space:nowrap'><b>Cache/reuse coordinator</b></td><td align='left' style='white-space:nowrap'>proves reuse</td></tr><tr><td align='left' style='white-space:nowrap'><b>Encode flat payload</b></td><td align='left' style='white-space:nowrap'>transport only</td></tr><tr><td align='left' style='white-space:nowrap'><b>Build React tree</b></td><td align='left' style='white-space:nowrap'>final projection only</td></tr></table>"]
  end

  subgraph Build["[1] Build time (once per deploy)"]
    direction TB
    A["app/ filesystem"] --> B["Route Manifest builder"]
    B --> C[("RouteManifest<br/>+ compiled route graph")]
    B --> G[("Resource dependency map")]
  end

  subgraph Decision["[2] Per-event decision (pure)"]
    direction TB
    EV["Event<br/>soft navigation · refresh ·<br/>back/forward · prefetch ·<br/>server action · render result"]
    S[("Visible route state<br/>+ route snapshot<br/>+ commit version")]
    K{{"NavigationPlanner"}}
    R["Candidate route decision<br/>(navigation kind · requested work ·<br/>visible proposal · trace)"]
    EV --> K
    S --> K
    K --> R
  end

  L{{"Navigation<br/>commit gate"}}
  Reject(["reject /<br/>cache-seed only"])
  Hard(["hard navigate"])

  subgraph Exec["[3] Approved execution"]
    direction TB
    AC["Approved commit"]
    BD["Update browser state<br/>URL · history · scroll · focus"]
    ES[("Payload store")]
    P["Build React tree"]
    UI(["Visible UI"])
    IO["Ask server/cache<br/>for missing payloads"]

    AC --> BD --> UI
    AC --> ES --> P --> UI
    AC --> IO
  end

  subgraph Server["[4] Server/cache work"]
    direction TB
    SR["Render or materialize<br/>RSC payloads"]
    WE["Encode flat payload"]
    SR --> WE
  end

  subgraph Runtime["[5] Runtime profile boundary"]
    direction TB
    CC["Cache/reuse coordinator"]
    RC[("Runtime cache<br/>hot local layer")]
    AS[("Artifact store<br/>immutable payloads")]
    CO[("Coherence coordinator<br/>epochs / invalidation floors")]
    BJ["Background jobs<br/>revalidate / cleanup"]
    HS[("Hints/config store<br/>not authority")]
    CC --> RC & AS & CO & BJ
    CC -.->|read-only hints| HS
  end

  C --> K
  G --> K
  G --> CC
  G --> L

  R --> L
  L -- approve --> AC
  L -- reject --> Reject
  L -- cross-root / incompatible graph --> Hard

  IO --> CC
  CC -- cache miss / render needed --> SR
  CC -- compatible cached payload --> WE
  WE ===|"network<br/>(flat payload boundary)"| ES

  classDef pure fill:#7eb3ec,stroke:#1e5fc4,stroke-width:2px,color:#0a2540
  classDef gate fill:#f5b876,stroke:#b8530a,stroke-width:3px,color:#3d1f00
  classDef visible fill:#7ed197,stroke:#1ea344,stroke-width:2px,color:#0a3d1c
  classDef storage fill:#e8d5f0,stroke:#7b3aa3,stroke-width:2px,color:#2d1040
  classDef terminal fill:#f0d4d4,stroke:#a33a3a,stroke-width:2px,color:#400a0a
  classDef legend fill:#fafafa,stroke:#888,stroke-width:1px,color:#222,text-align:left

  class K,R pure
  class L gate
  class AC,BD,P,UI,IO visible
  class C,G,S,ES,RC,AS,CO,HS storage
  class Reject,Hard terminal
  class LG legend

Current Status

The first layout-persistence milestone has landed.

Vinext currently has:

flat keyed AppElements payload
layout/page/template/slot/route entries
browser merge/replace behavior
SSR/browser deserialization symmetry
root-layout hard navigation behavior
mounted-slot headers for slot-aware RSC requests
bounded visited-response and prefetch caches
build-time layout classification groundwork

The current code also already contains the future architecture in scattered form:

activeNavigationId
pending browser-router promise
createPendingNavigationCommit()
resolvePendingNavigationCommitDisposition()
rootLayoutTreePath checks
pendingPathname ownership
mounted-slot cache variants
server-action same-URL race comments

The first implementation step is not to replace this system. It is to make these existing ownership seams explicit, typed, and testable.

Main Architectural Concerns From Adversarial Review

1. Scope Risk

This issue must not be treated as a single implementation epic that lands compiler facts, lifecycle, cache coherence, runtime storage, skip transport, streaming, and Activity preservation together.

The sane path is:

Build the thinnest end-to-end router spine that preserves today's behavior.
Then promote one semantic decision at a time through the full path.

Avoid PRs that only add large unused type systems. Early PRs must either:

freeze current behavior
introduce an enforceable boundary
move one existing behavior through the new ownership path
or delete an old semantic writer

2. NavigationPlanner Must Stay Small

navigationPlanner.plan() should be a small semantic planner, not the whole router.

It can decide:

navigation kind
root-layout transition intent
route/slot/topology intent from compiled facts
which work must be requested
which visible proposal is being considered
how an observed async result should be interpreted
why the decision was made

It must not do:

fetches
React state updates
URL/history mutation
cache reads/writes
server-action execution
runtime binding access
transition promise settlement
final stale-commit approval

A pure reducer is only realistic if async facts re-enter as explicit events.

The model is two phase:

Phase 1: user/runtime intent event
  navigate / refresh / traverse / prefetch / serverActionSubmitted
  -> planner proposes operation and requested work

Phase 2: observed result event
  flightResponseArrived / renderOutcomeObserved / actionReturned / streamChunkArrived
  -> planner interprets the result as commit proposal, noCommit, hardNavigate, cache-seed only, or terminal outcome
  -> lifecycle gate decides whether it is still allowed to become visible

The planner must not pretend to know render-time facts before render. Redirects, notFound, boundary errors, dynamic request API reads, cacheability downgrades, stream failures, and server-action revalidation effects are observed outcomes that feed back into the planner as events.

3. Lifecycle Authority Comes First

The most concrete current pressure is stale async work committing visible state.

activeNavigationId is not strong enough for same-URL refresh and server-action races. Same URL does not mean same visible world. Vinext needs a visibleCommitVersion and one lifecycle owner before broad compiler/cache work.

The lifecycle controller should wrap the existing candidate-commit seam first:

createPendingNavigationCommit()
resolvePendingNavigationCommitDisposition()
pending browser-router promise
pendingPathname ownership
pre-paint URL/history effect
server-action same-URL commit path
prefetch cache-only path

The first controller should consolidate existing behavior. It should not rewrite all router semantics.

4. Safe Fallbacks Can Become A Performance Trap

The law says uncertainty must not degrade to reuse. That is correct.

But a naive implementation can collapse cache hit rate by treating routine runtime observations as global uncertainty. For example, a route that reads a generic header or cookie should not automatically poison every cache class unless that input actually affects the output and can be modeled safely.

Rules:

runtime observations downgrade only the output they affect
public cache dimensions must be allowlisted, canonical, bounded, and redacted
unknown private/auth/draft/session inputs degrade to private, uncacheable, or fresh render
fresh render is one fallback, not the universal fallback
cache-hit rate and variant cardinality must be measured before broad rollout

Do not optimize with probabilistic reuse. Do optimize by keeping proof scopes narrow.

5. Skip Transport Can Backfire

ClientReuseManifest is an untrusted hint. It must not become a server CPU or storage-IO amplifier.

Skip transport must start with the cheapest proven class, likely static layout entries:

same graph/deployment compatibility
same root boundary
same route/topology identity
compatible params/search/interception/mounted-slot context
prior render observation says no dynamic request API usage
no incompatible boundary outcome
local metadata is enough to verify

Hard rule:

If verifying skip requires more work than rendering and sending the entry, do not skip.

Abuse limits are protocol requirements:

maximum manifest byte size
maximum entry count
canonical ordering
bounded hash algorithm
replay window / visibleCommitVersion compatibility
private-entry rejection
unknown-entry ignore path
trace reason for every rejected manifest entry

Skip transport is a later optimization, not a prerequisite for the router spine.

6. Deployment Compatibility Must Be Algebra, Not Strict Equality Everywhere

Strict graphVersion and deploymentVersion equality is too brittle for rolling deploys, multi-region edges, previews, canaries, and rollback windows.

The architecture needs a deployment compatibility protocol before hard-navigation decisions depend on version mismatches.

Required concepts:

graph version
asset/deployment version
compatibility map generated at build time
client/server handshake
old-client/new-server fallback
new-client/old-server fallback
hard-navigation loop prevention
asset pinning for in-flight RSC payloads
controlled cache miss or render-fresh path when compatibility is unknown

A version mismatch may require a hard navigation in some cases. It must not produce reload loops or break SPA behavior during normal rolling deploys.

7. Runtime Profile Must Not Own Semantics

Cloudflare Workers are the primary production target for vinext.

That matters. The semantic core must be binding-free. Cloudflare-specific storage, Durable Objects, KV, Cache API, Queues, or R2 integration belongs behind a runtime profile boundary.

Runtime profile may execute:

cache reads/writes
artifact reads/writes
coherence coordinator calls
background jobs
runtime trace fields
runtime hot-path metrics

Runtime profile must not decide:

route topology
slot preservation
layout reuse
interception meaning
cache equivalence
visible commit authority

Runtime storage hits are not semantic proof by themselves.

8. Streaming And Activity Are Follow-Up Specs

Streaming chunks and Activity/hidden-route preservation are real future needs, but they should not block the lifecycle spine.

Before streaming reveal is implemented, the architecture must specify:

chunk terminal settlement
stale chunk recovery
boundary ownership generation
cache-seed-only eligibility
no permanently hung Suspense boundary when a chunk is discarded

Before Activity preservation is implemented, it must specify:

owner
memory budget
eviction policy
auth/session invalidation
effect cleanup
style isolation
focus and accessibility behavior
cross-tab invalidation behavior

Until then, these remain out of scope for the first migration stage.

9. Correctness Oracle Without Process Theater

Every semantic PR must name its oracle:

Next public semantics conformance
Vinext internal invariant
Intentional documented divergence

This is required because vinext aims to match Next public behavior unless a divergence is deliberate.

But do not require full NavigationTrace, cache cardinality proof, and runtime coherence proof for every small PR. The process should scale with the semantic risk.

Minimal early requirement:

what old writer is being deleted
what new owner writes the decision
what oracle defines correctness
what hostile sequence or boundary case is covered
what fallback occurs on uncertainty

10. Lock Criteria From Final Review

Lock the direction, not every future type shape. The architecture is only useful if future PRs cannot rebuild the old implicit router under cleaner names.

Non-negotiable lock criteria:

visible state mutation happens only through an approved visible commit transaction
visibleCommitVersion increments in exactly one place
NavigationPlanner v0 stays narrow: requestWork, proposeCommit, noCommit, hardNavigate
raw AppElementsWire keys are fenced by constructors/parsers and import-boundary checks
unknown root-layout identity is traced as legacy fallback or uncertainty, not proof of safe merge
reusable output has positive and negative render-observation proof for the relevant scope
CacheVariant dimensions have hard budgets and a breaker path
every reusable/skippable artifact carries a compatibility envelope before cache/skip depend on it
NavigationTrace uses compact reason codes and structured fields, not narrative logs
semantic PRs delete the old writer for the promoted path in the same PR
v0 does not claim full Cache Components or Activity hidden-route preservation

Core Ownership Model

1. AppElementsWire

Implementation name: AppElementsWire.

Owns:

serialization
deserialization
transport compatibility
wire-key encoding
RSC/HTML payload boundary
merge buffer compatibility while legacy paths remain

Does not own:

topology
slot ownership
layout preservation
default/unmatched behavior
interception meaning
cache identity
visible commit permission

The flat payload is how data travels. It is not how the router thinks.

2. RouterState And RouteSnapshot

Implementation names: RouterState, RouteSnapshot.

Route state owns visible continuity.

It must keep together the facts that change together:

type RouterState = {
  visibleSnapshot: RouteSnapshot
  previousSnapshot: RouteSnapshot | null
  activeOperation: OperationRecord | null
  visibleCommitVersion: number
}

type RouteSnapshot = {
  id: RouteSnapshotId
  routeId: RouteId
  rootBoundaryId: RootBoundaryId | null
  displayUrl: string
  matchedUrl: string
  params: ParamFingerprint
  search: SearchFingerprint
  mountedSlots: MountedSlotState
  interceptionContext: InterceptionContext | null
  cacheContext: CacheContextFingerprint
}

State must not be reconstructed from wire keys.

3. Navigation Events

Implementation name: NavigationEvent.

Every router input becomes an event.

Minimum v0 event set:

type NavigationEvent =
  | { kind: "hydrate"; href: string; ssrSnapshot: HydrationSnapshot }
  | { kind: "navigate"; href: string; mode: "push" | "replace" }
  | { kind: "traverse"; direction: "back" | "forward"; historyState: unknown }
  | { kind: "refresh"; scope: RefreshScope }
  | { kind: "prefetch"; href: string }
  | { kind: "serverActionSubmitted"; actionId: ActionId }
  | { kind: "flightResponseArrived"; token: OperationToken; result: FlightResult }
  | { kind: "serverActionReturned"; token: OperationToken; result: ActionResult }
  | { kind: "operationFailed"; token: OperationToken; error: RuntimeError }
  | { kind: "operationAborted"; token: OperationToken; reason: AbortReason }

Async result events carry causal proof:

type OperationToken = {
  operationId: OperationId
  lane: OperationLane
  baseVisibleCommitVersion: number
  graphVersion: GraphVersion
  deploymentVersion: DeploymentVersion
  targetSnapshotFingerprint: string
  cacheVariantFingerprint?: string
}

A result is not allowed to commit because it finished. It may commit only if lifecycle still authorizes it.

4. NavigationPlanner

Implementation name: navigationPlanner.plan().

The planner owns route semantics, but only at the semantic planning and interpretation layer.

Shape:

function reduce(
  schema: RouteManifest,
  state: RouterState,
  event: NavigationEvent,
): NavigationDecisionV0

Minimum v0 output:

type NavigationDecisionV0 =
  | {
      kind: "requestWork"
      token: OperationToken
      work: RequestedWork
      trace: NavigationTrace
    }
  | {
      kind: "proposeCommit"
      token: OperationToken
      proposal: CommitProposal
      trace: NavigationTrace
    }
  | {
      kind: "noCommit"
      token: OperationToken
      reason: NoCommitReason
      trace: NavigationTrace
    }
  | {
      kind: "hardNavigate"
      token: OperationToken
      url: string
      reason: HardNavigationReason
      trace: NavigationTrace
    }

Promote SegmentOp, SlotOp, richer effect sets, and cache-specific decisions only when a real semantic slice needs them. Do not ship a fat reducer before one current path proves the spine.

NavigationTrace must be reason-code based: compact codes plus structured fields that explain the decision without becoming another router object graph.

The planner proposes. The lifecycle gate approves. The executor applies.

The planner must be boring at first. Boring is good here.

5. NavigationLifecycleController

Implementation name: NavigationLifecycleController.

Owns:

operation identity
operation lane
base visible commit version
operation token compatibility
terminal state
commit permission
transition promise settlement
visibleCommitVersion increment
RouterState update after successful commit

Operation lanes:

visible
traverse
refresh
action
prefetch
recovery

Terminal states:

committed
superseded
aborted
failed
hard-navigated
cache-seeded
refresh-scheduled

Rules:

newer visible work supersedes older visible work
prefetch never commits visible UI
refresh is a real operation and can be superseded
same-URL commits use visibleCommitVersion, not URL alone
stale candidate commits must not patch visible state
server-action results are ordered by OperationToken and visibleCommitVersion
older action results may return values, invalidate, schedule refresh, or cache-seed only; they may not overwrite newer visible state
RSC redirects stay inside one operation lifecycle when possible

6. CommitDecision, ApprovedVisibleCommit, And BrowserDelta

Visible browser mutation must go through one approved commit transaction.

An approved visible commit is the only path that may mutate:

browser URL/history
visible RouterState
payload store
mounted-slot state
scroll/focus restoration
transition promise settlement
visibleCommitVersion

There must be exactly one place where visibleCommitVersion++ happens. Everything else is either candidate work, rejected work, hard navigation, or non-visible cache seeding.

type ApprovedVisibleCommit = {
  token: OperationToken
  baseVisibleCommitVersion: number
  nextVisibleCommitVersion: number
  decision: CommitDecision
  browserDeltas: BrowserDelta[]
  storeDeltas: StoreDelta[]
  trace: NavigationTrace
}

type CommitDecision =
  | {
      kind: "apply"
      transitionMode: "sync" | "transition" | "viewTransition"
    }
  | { kind: "hardNavigate"; url: URL; reason: HardNavigationReason }
  | { kind: "noCommit"; reason: NoCommitReason }

Hard navigation is terminal. It should not sit beside normal browser deltas.

transitionMode is part of commit authority. Shell code may execute the mode, but must not reinterpret the semantic decision.

7. Route Facts Compiler

Implementation names: RouteManifestBuilder, RouteManifest, StaticSegmentGraph.

The compiler owns build-time topology and stable facts.

It compiles facts the current implementation already knows implicitly:

segments
layouts
pages
templates
parallel slots
implicit children slot
default entries
route groups
root-layout boundaries
interception rules
stable graph-minted IDs
canonical constructors
conservative static/dynamic hints

The compiler should not emit a full transition automaton. Runtime transition decisions remain in the planner.

Build-time classifications are hints. Runtime observations can always downgrade cacheability.

8. Render Observation Protocol

Implementation names: RenderOutcome, RenderObservation.

This is the missing bridge between pure planning and real execution.

A render must report what actually happened before cache write, reuse proof, streaming reveal, or visible commit approval can rely on it:

observed cookies()
observed headers()
observed draft/auth/session state
observed dynamic fetches
explicit no-request-api observation where reuse depends on absence
cache tags
path tags
redirect/notFound/forbidden/unauthorized/error/global-error outcomes
HTML/RSC renderEpoch metadata
privacy/cacheability downgrade
output scope: layout, page, slot, route wiring, HTML, RSC, boundary outcome
stream dependencies, later

Render observations are not side-channel decisions. They re-enter the planner/lifecycle/cache model as explicit events or result metadata.

Absence of a recorded dynamic read is not enough for reuse unless the render scope was observed completely enough to prove that absence. A reusable output needs a scoped bill of health: what it did observe, what it did not observe, what boundary outcome it produced, and which artifact envelope it belongs to.

9. CacheVariant And Resource Dependencies

Implementation names: CacheVariant, ResourceDependencyGraph.

Cache identity answers:

May these outputs be reused as equivalent?

Resource dependencies answer:

What inputs, resources, tags, actions, deployment facts, and runtime observations can invalidate that equivalence?

Start narrow. Do not attempt full cache algebra before the lifecycle spine exists.

Cache v1 should cover:

graph/deployment compatibility
root/route/render identity
params/search where required
mounted-slot fingerprint where required
interception context where required
renderEpoch pairing for HTML/RSC
privacy/cacheability downgrade from render observations
boundary outcome compatibility

Mandatory cache dimension rules:

no raw cookies as public dimensions
no raw headers as public dimensions
no bearer tokens, session cookies, or secrets as public dimensions
no unbounded raw user input as public dimensions
public dimensions must be allowlisted, canonicalized, bounded, classified, and redacted in traces

Mandatory cache budget rules:

maximum encoded dimension length
maximum dimension count
maximum value count per dimension
canonical ordering
redacted trace representation
per-route variant ceiling
breaker fallback when the ceiling is exceeded

If a route exceeds its variant ceiling, the fallback is private, uncacheable, or fresh render for the affected output. Measurement is not enough without an enforcement path.

10. Artifact Compatibility Envelope

Implementation name: ArtifactCompatibilityEnvelope.

Every payload or artifact that may later participate in cache reuse or skip transport must carry compatibility metadata before those systems rely on it:

graph version
asset/deployment/build ID
payload schema version
route schema version
RSC protocol version
renderEpoch
root boundary
compatibility set

Cache coherence and skip transport can remain disabled at first. The envelope should land early so old-client/new-server, new-client/old-server, rolling deploy, canary, and rollback behavior has a protocol instead of a boolean equality check.

11. Runtime Profile Boundary

Implementation names: RuntimeProfile, CacheCoordinator, ArtifactStore.

Runtime profiles execute approved work.

Cloudflare is the first production target, but the semantic core must not import raw runtime bindings.

Generic layers:

RequestMemo: per-request dedupe only
Process/Isolate microcache: opportunistic only
Response/artifact cache: hot local cache with semantic metadata
Artifact store: immutable HTML/RSC/payload artifacts
Coherence coordinator: invalidation floors, renderEpoch publication, pairing rules when needed
Background jobs: revalidation, prewarm, cleanup; idempotent by key
Hint/config store: read-mostly hints only; never correctness authority

Hot-path rule:

A static public hit must not require a distributed coordination round trip on every request.

Validity Rules

A reduction or runtime action is invalid if:

visible state mutates outside an ApprovedVisibleCommit

visibleCommitVersion increments from more than one owner

rootLayoutTransition = crossRoot and CommitDecision is not hardNavigate

BrowserDelta preserves a slot but RouterState cannot prove the slot exists or is retained

slot default is rendered but StaticSegmentGraph has no default for that SlotId

slot is marked unmatched but graph resolution found a matching slot route or default

route is intercepted but event/context is not interception-capable

layout is reused across a crossed root boundary

unknown root-layout identity is treated as proof of safe merge after root-layout transition is promoted

cache read occurs without CacheVariant compatibility

cache entry has compatible CacheVariant but incompatible dependency fingerprint or invalidation ownership

cache entry is reused without scoped proof that dynamic/private request APIs were not observed where that proof is required

CacheVariant exceeds the route budget and does not take the breaker fallback

artifact is cached, reused, or skipped without a compatible ArtifactCompatibilityEnvelope

HTML/RSC pair crosses renderEpoch incompatibly

successful RSC payload is paired with incompatible error/notFound/unauthorized HTML, or the reverse

cached notFound/forbidden/unauthorized boundary is reused as a successful route payload

private/auth/cookie/header/draft-sensitive output is cached as public

async result commits with stale visibleCommitVersion

older server-action result overwrites newer visible state

stream chunk mutates visible UI without lifecycle approval, once streaming exists

server accepts ClientReuseManifest entries without verifying graph, deployment, variant, epoch, payload hash, and invalidation compatibility, once skip exists

skip transport is enabled before verification cost and CacheVariant cardinality are measured and bounded

unbounded raw user input becomes a public CacheVariant dimension

runtime hint store or non-authoritative cache decides invalidation floor, current renderEpoch, HTML/RSC pairing, or private cache safety

generated code constructs NavigationDecisionV0, BrowserDelta, or CacheVariant directly instead of calling typed contracts

NavigationTrace relies on narrative logs instead of reason codes and structured fields for semantic decisions

wire-key absence is used as preserve/delete/default/skip proof after the relevant semantic decision has been promoted

uncertain cache/deployment/graph compatibility results in reuse instead of safe fallback

Navigation Kind Semantics

The planner must branch on explicit navigation kind, not infer it from payload shape.

hydrate:
  may use previous state: SSR snapshot only
  may preserve slots: only if SSR snapshot proves them
  commit: initialise or recovery

soft navigate:
  may use previous state: yes
  may preserve slots: yes
  may apply interception: yes
  commit: soft commit

hard navigate:
  may use previous state: no
  commit: full document navigation

refresh:
  may use previous state: yes
  may preserve slots: yes
  may apply interception: current-context only
  commit: refresh commit

traverse / popstate:
  may use previous state: yes, from history-derived state when available
  may preserve slots: yes, when history/current state proves it
  commit: traverse commit

prefetch:
  may seed cache only
  may not commit visible UI

server-action refresh:
  may use previous state: yes
  may preserve slots: yes
  may apply interception: current-context only
  commit: lifecycle-approved action refresh, redirect, noCommit, cache-seed only, or refresh-scheduled

Implementation Plan

This is not a fixed PR count. Split into small, reviewable PRs.

Governing sequence:

freeze today's flat-wire behavior with compatibility tests
fence AppElementsWire
add minimal compiled facts and compatibility envelope skeleton
add lifecycle transaction contract and reason-code trace shell
build the thin end-to-end spine for one current navigation path
promote root-layout hard navigation and delete the old writer for that path
only then promote slots, interception, actions, cache identity, and skip transport

Layer 0: Keep The Landed Foundation

Do not redo the flat payload milestone.

Keep:

flat keyed AppElements payload
layout/page/template/slot/route entries
browser merge/replace behavior
SSR/browser deserialization symmetry
root-layout hard navigation behavior, until promoted
absent-key soft-navigation preservation, until promoted
mounted-slot cache variants, until promoted

Layer 1: Lifecycle Spine First

Goal: make visible commit authority explicit while preserving behavior.

Deliverables:

OperationRecord
operation lanes
terminal states
visibleCommitVersion
ApprovedVisibleCommit transaction
one visibleCommitVersion increment owner
lifecycle approval barrier
pending promise settlement owned by lifecycle
same-URL/server-action stale commit rejection
prefetch cache-only lane
traverse/back-forward intent adapter where platform evidence exists

Acceptance:

newer navigation beats older RSC response
old RSC response can resolve late without committing visible state
prefetch can resolve late and seed cache only
server action resolving after newer visible commit cannot clobber the route
refresh can be superseded
RSC redirect chains keep one pending lifecycle
hard-navigation recovery only fires for the current operation
no-op back/forward cannot leave pending stuck

Layer 2: Fence AppElementsWire

Goal: stop semantic meaning from spreading through flat wire keys.

Deliverables:

AppElementsWire codec boundary
approved wire-key constructors/parsers
raw wire-key parsing restrictions
import-boundary checks
compatibility tests for current payload behavior

No semantic promotion yet.

Layer 3: Minimal Route Manifest

Goal: compile the facts needed for the first promotions.

Deliverables:

RouteManifestBuilder skeleton
RouteManifest skeleton
StaticSegmentGraph skeleton
graphVersion
ArtifactCompatibilityEnvelope skeleton
graph-minted IDs for routes/layouts/pages/templates/slots/defaults/root boundaries
minimal canonical constructors
fixture tests for graph output

Not yet:

full dependency graph
full cache algebra
transition automaton
skip transport
streaming protocol
Activity preservation

Layer 4: NavigationPlanner v0

Goal: route one existing navigation path through the new ownership boundaries while preserving behavior.

Deliverables:

navigationPlanner.plan() for navigate / refresh / traverse / flightResponseArrived
NavigationDecisionV0 only: requestWork / proposeCommit / noCommit / hardNavigate
CommitProposal
ApprovedVisibleCommit handoff
reason-code NavigationTrace
minimal invariant checker
current AppElementsWire emitted from the new path
old path remains only as compatibility fallback for unpromoted paths

The planner must remain small and pure. Async facts re-enter as events.

Layer 5: Promote First Semantic Decisions

Promote one decision area per PR and delete the old writer in the same PR.

Recommended order:

root-layout hard navigation
boundary outcomes: error/notFound/forbidden/unauthorized/global-error
mounted-slot preservation
default/unmatched slot behavior
interception transition rules
server-action refresh semantics
scroll/focus/hash restoration

Each PR must state:

old writer deleted
new owner
correctness oracle
hostile sequence covered
fallback on uncertainty

Layer 6: Cache Coherence v1

Goal: make existing cache reuse safer before making it more aggressive.

Deliverables:

RenderIdentity
ReuseIdentity
InvalidationScope
canonical CacheVariant serialization
RenderOutcome / RenderObservation integration
RouteCacheRecord v1
ArtifactCompatibilityEnvelope checks
negative RenderObservation proof where reuse depends on absence
renderEpoch
HTML/RSC pairing checks
privacy/cacheability downgrade rules
boundary outcome compatibility
cache variant cardinality measurement
cache variant budget enforcement and breaker fallback

Do not enable broad skip transport in this layer.

Layer 7: Proof-Backed Skip Transport v1

Goal: reduce server work and bytes only where proof is cheap.

Initial eligible class:

static layout entries only
same compatible graph/deployment/root
compatible route params/search/interception/mounted-slot context
no dynamic request API observation
no incompatible boundary outcome
local metadata verification only

Deliverables:

ClientReuseManifest as untrusted hint
manifest abuse limits
ServerRenderPlan
ReuseProof
verification-cost budget
trace reason for every accepted/rejected skip
fallback to render when proof is unavailable or expensive

Layer 8: Runtime Profile v1

Goal: keep runtime execution behind typed contracts after the semantic spine exists.

Deliverables:

runtime cache interface
artifact store interface
coherence coordinator interface, only where required
background job interface with idempotency keys
Cloudflare profile as first implementation
runtime hot-path metrics

The semantic core stays binding-free.

Later Layers: Streaming And Activity

Separate follow-up specs required for:

operation-tokened stream chunks
reveal boundary ownership
stale chunk recovery and settlement
hidden route model
Activity memory/eviction/auth/effect/focus policies

Do not implement these before lifecycle authority, Route Manifest data, and cache coherence v1 exist.

Test Strategy

The most important tests are sequence tests and hostile timelines, not giant snapshots.

Generate apps with:

nested layouts
templates
parallel slots
default slots
unmatched slots
route groups
interception routes
dynamic params
search params
root-layout boundaries
static layouts
cookie/header/auth/draft-sensitive layouts
server actions
concurrent server actions
prefetch races
back/forward traversals
cache invalidations
deployment changes

Generate event sequences like:

A -> B -> C, with B resolving last

prefetch A
navigate B
old A prefetch resolves

server action resolves after same-URL refresh

server actions A and B resolve out of order

back/back/forward while RSC responses are pending

cross-root navigation while old result arrives

slot mounted
slot unmatched
refresh
action redirect

HTML cache hit but RSC cache miss

RSC cache hit but HTML stale

deployment A client receives deployment B payload

Required invariants:

No stale operation commits visible state.
No visible state mutates outside ApprovedVisibleCommit.
No visibleCommitVersion increment exists outside the lifecycle owner.
No older server-action result overwrites newer visible state.
No cache entry is read without CacheVariant compatibility.
No cache entry is reused without required positive and negative RenderObservation proof.
No cache variant ceiling overflow reuses public cache.
No cached/reused/skipped artifact lacks a compatible ArtifactCompatibilityEnvelope.
No HTML/RSC pair crosses renderEpoch incompatibly.
No layout is reused across root-layout boundary.
No slot is preserved unless RouterState proves it existed, once promoted.
No interception applies without an interception-capable context, once promoted.
No AppElementsWire absence has semantic meaning after the relevant decision is promoted.
No private/cookie/auth-sensitive response is cached under a public variant.
No runtime hint-store value is treated as authoritative invalidation or renderEpoch state.
No ClientReuseManifest entry is trusted without server-side verification, once skip exists.
No uncertain graph/deployment/cache state results in reuse.
Every semantic change names its oracle.

Hot Path Budget

Track performance by route/cache class. Do not claim the architecture is faster merely because it is more formal.

At minimum, measure:

runtime cache reads/writes
coordinator calls
artifact store reads/writes
background job writes and duplicate/idempotent replays
runtime subrequests / external IO
server CPU time
RSC bytes sent
layouts rendered
client remount count
p95/p99 navigation latency
cache variant cardinality
ElementStore memory
stale commit rejection count
skip verification cost versus render cost
hard-navigation count caused by deployment mismatch
cache variant breaker count
compatibility-envelope mismatch count
trace reason-code distribution

Performance claims should be based on work avoided:

fewer layout renders
fewer RSC bytes
fewer remounts
lower p95/p99 latency
bounded runtime/coordinator calls on hot paths

Success Criteria

This migration succeeds when:

The flat payload wire format is transport, not semantic authority.

Visible commits go through one lifecycle-approved atomic transaction.

Same-URL/server-action stale commits are rejected, cache-seeded only, or translated into explicit refresh behavior.

NavigationPlanner is small, pure, event-driven, and v0 exposes only requestWork/proposeCommit/noCommit/hardNavigate.

Async render/server-action outcomes feed back as explicit events or result metadata.

Every promoted route-semantic decision has one owner and the old writer is deleted.

Every applied commit has explicit transitionMode and lifecycle-approved fallback path.

Every cache read uses CacheVariant compatibility.

Every cache write uses render observations to decide privacy/cacheability.

Every reused output has scoped positive and negative render-observation proof where reuse depends on absence.

Every CacheVariant has enforced budgets and a breaker fallback.

Every cached, reused, or skipped artifact has a compatible artifact envelope.

Every HTML/RSC pair has compatible renderEpoch.

Every skip is explained by cheap graph/cache/dependency proof and is bounded by abuse limits.

Deployment/version mismatch has a compatibility protocol and cannot produce hard-navigation loops.

Cloudflare runtime execution stays behind a profile boundary.

Generated code calls typed contracts and does not recreate semantic decisions.

NavigationTrace uses compact reason codes and structured fields.

Missing payload, missing wire key, missing branch, and cache miss do not imply semantic preservation after the relevant decision is promoted.

Out Of Scope For The First Migration Stage

Do not add yet:

full generated transition automaton
separate reference interpreter
global strong cache consistency for all content
complete Cloudflare storage profile before the semantic spine exists
new public router API surface
broad skip transport
custom stream chunk protocol
Activity / hidden route preservation
full Cache Components parity
stronger-than-Next performance claims before benchmarks prove them

Final Statement

This architecture is not trying to make a prettier flat map.

It is trying to make the router less magical without making it slower, more brittle, or impossible to ship.

The NavigationPlanner should be small. Lifecycle authority should land early. Cache proof should start narrow. Skip transport should prove that it saves work before it is trusted. Deployment compatibility must be designed for rolling edges. Runtime profiles execute approved work; they do not own route meaning.

The discipline remains:

No proof, no reuse.
No proof, no skip.
No proof, no visible commit.
No proof, use the safest event-specific fallback.

The implementation discipline is just as important:

Thin spine first.
One semantic promotion at a time.
Delete the old writer when the new owner lands.
Measure before claiming performance.
Originally created by @NathanDrake2406 on GitHub (Mar 31, 2026). Original GitHub issue: https://github.com/cloudflare/vinext/issues/726 # App Router layout persistence: Route Manifest and Navigation Planner architecture ## What This Issue Is Really About The flat keyed `AppElements` payload solved the first layout-persistence problem: the browser can merge layout, page, template, slot, and route entries instead of replacing the whole App Router tree. That bridge should stay. But the flat map must not become the router's brain. Vinext now needs to move route meaning out of: ```txt wire keys missing payload entries cache-key suffixes mounted-slot headers local activeNavigationId checks runtime cache hits hint-store reads artifact-store object existence ``` The target architecture is not a giant rewrite. It is a controlled migration from implicit meaning to owned decisions: ```txt flat payload transport + visible navigation lifecycle authority + minimal Route Manifest data + small pure NavigationPlanner + event-specific commit gate + semantic cache coherence, introduced narrowly + runtime profiles that execute approved work + React projection at the end ``` The discipline is: ```txt No proof, no reuse. No proof, no skip. No proof, no visible commit. No proof, use the safest event-specific fallback. ``` But that law has an important qualifier: ```txt Safety fallback must be event-specific and measured. It must not turn routine dynamic observations into universal cache misses. ``` Correctness is mandatory. Performance comes from narrow, proven reuse, not optimism. ## Architecture Overview ```mermaid flowchart TD subgraph Legend["Who owns what (one job per box)"] direction TB LG["<table cellpadding='8' cellspacing='0' style='border-collapse:collapse;width:640px'><tr><td align='left' style='width:240px;white-space:nowrap'><b>Route Manifest builder</b></td><td align='left' style='white-space:nowrap'>what can exist</td></tr><tr><td align='left' style='white-space:nowrap'><b>Visible route state + snapshot</b></td><td align='left' style='white-space:nowrap'>what is currently visible</td></tr><tr><td align='left' style='white-space:nowrap'><b>Event</b></td><td align='left' style='white-space:nowrap'>what happened</td></tr><tr><td align='left' style='white-space:nowrap'><b>NavigationPlanner</b></td><td align='left' style='white-space:nowrap'>what event means</td></tr><tr><td align='left' style='white-space:nowrap'><b>Navigation commit gate</b></td><td align='left' style='white-space:nowrap'>may become visible?</td></tr><tr><td align='left' style='white-space:nowrap'><b>Approved commit</b></td><td align='left' style='white-space:nowrap'>mutates browser / store</td></tr><tr><td align='left' style='white-space:nowrap'><b>Cache/reuse coordinator</b></td><td align='left' style='white-space:nowrap'>proves reuse</td></tr><tr><td align='left' style='white-space:nowrap'><b>Encode flat payload</b></td><td align='left' style='white-space:nowrap'>transport only</td></tr><tr><td align='left' style='white-space:nowrap'><b>Build React tree</b></td><td align='left' style='white-space:nowrap'>final projection only</td></tr></table>"] end subgraph Build["[1] Build time (once per deploy)"] direction TB A["app/ filesystem"] --> B["Route Manifest builder"] B --> C[("RouteManifest<br/>+ compiled route graph")] B --> G[("Resource dependency map")] end subgraph Decision["[2] Per-event decision (pure)"] direction TB EV["Event<br/>soft navigation · refresh ·<br/>back/forward · prefetch ·<br/>server action · render result"] S[("Visible route state<br/>+ route snapshot<br/>+ commit version")] K{{"NavigationPlanner"}} R["Candidate route decision<br/>(navigation kind · requested work ·<br/>visible proposal · trace)"] EV --> K S --> K K --> R end L{{"Navigation<br/>commit gate"}} Reject(["reject /<br/>cache-seed only"]) Hard(["hard navigate"]) subgraph Exec["[3] Approved execution"] direction TB AC["Approved commit"] BD["Update browser state<br/>URL · history · scroll · focus"] ES[("Payload store")] P["Build React tree"] UI(["Visible UI"]) IO["Ask server/cache<br/>for missing payloads"] AC --> BD --> UI AC --> ES --> P --> UI AC --> IO end subgraph Server["[4] Server/cache work"] direction TB SR["Render or materialize<br/>RSC payloads"] WE["Encode flat payload"] SR --> WE end subgraph Runtime["[5] Runtime profile boundary"] direction TB CC["Cache/reuse coordinator"] RC[("Runtime cache<br/>hot local layer")] AS[("Artifact store<br/>immutable payloads")] CO[("Coherence coordinator<br/>epochs / invalidation floors")] BJ["Background jobs<br/>revalidate / cleanup"] HS[("Hints/config store<br/>not authority")] CC --> RC & AS & CO & BJ CC -.->|read-only hints| HS end C --> K G --> K G --> CC G --> L R --> L L -- approve --> AC L -- reject --> Reject L -- cross-root / incompatible graph --> Hard IO --> CC CC -- cache miss / render needed --> SR CC -- compatible cached payload --> WE WE ===|"network<br/>(flat payload boundary)"| ES classDef pure fill:#7eb3ec,stroke:#1e5fc4,stroke-width:2px,color:#0a2540 classDef gate fill:#f5b876,stroke:#b8530a,stroke-width:3px,color:#3d1f00 classDef visible fill:#7ed197,stroke:#1ea344,stroke-width:2px,color:#0a3d1c classDef storage fill:#e8d5f0,stroke:#7b3aa3,stroke-width:2px,color:#2d1040 classDef terminal fill:#f0d4d4,stroke:#a33a3a,stroke-width:2px,color:#400a0a classDef legend fill:#fafafa,stroke:#888,stroke-width:1px,color:#222,text-align:left class K,R pure class L gate class AC,BD,P,UI,IO visible class C,G,S,ES,RC,AS,CO,HS storage class Reject,Hard terminal class LG legend ``` ## Current Status The first layout-persistence milestone has landed. Vinext currently has: ```txt flat keyed AppElements payload layout/page/template/slot/route entries browser merge/replace behavior SSR/browser deserialization symmetry root-layout hard navigation behavior mounted-slot headers for slot-aware RSC requests bounded visited-response and prefetch caches build-time layout classification groundwork ``` The current code also already contains the future architecture in scattered form: ```txt activeNavigationId pending browser-router promise createPendingNavigationCommit() resolvePendingNavigationCommitDisposition() rootLayoutTreePath checks pendingPathname ownership mounted-slot cache variants server-action same-URL race comments ``` The first implementation step is not to replace this system. It is to make these existing ownership seams explicit, typed, and testable. ## Main Architectural Concerns From Adversarial Review ### 1. Scope Risk This issue must not be treated as a single implementation epic that lands compiler facts, lifecycle, cache coherence, runtime storage, skip transport, streaming, and Activity preservation together. The sane path is: ```txt Build the thinnest end-to-end router spine that preserves today's behavior. Then promote one semantic decision at a time through the full path. ``` Avoid PRs that only add large unused type systems. Early PRs must either: ```txt freeze current behavior introduce an enforceable boundary move one existing behavior through the new ownership path or delete an old semantic writer ``` ### 2. NavigationPlanner Must Stay Small `navigationPlanner.plan()` should be a small semantic planner, not the whole router. It can decide: ```txt navigation kind root-layout transition intent route/slot/topology intent from compiled facts which work must be requested which visible proposal is being considered how an observed async result should be interpreted why the decision was made ``` It must not do: ```txt fetches React state updates URL/history mutation cache reads/writes server-action execution runtime binding access transition promise settlement final stale-commit approval ``` A pure reducer is only realistic if async facts re-enter as explicit events. The model is two phase: ```txt Phase 1: user/runtime intent event navigate / refresh / traverse / prefetch / serverActionSubmitted -> planner proposes operation and requested work Phase 2: observed result event flightResponseArrived / renderOutcomeObserved / actionReturned / streamChunkArrived -> planner interprets the result as commit proposal, noCommit, hardNavigate, cache-seed only, or terminal outcome -> lifecycle gate decides whether it is still allowed to become visible ``` The planner must not pretend to know render-time facts before render. Redirects, `notFound`, boundary errors, dynamic request API reads, cacheability downgrades, stream failures, and server-action revalidation effects are observed outcomes that feed back into the planner as events. ### 3. Lifecycle Authority Comes First The most concrete current pressure is stale async work committing visible state. `activeNavigationId` is not strong enough for same-URL refresh and server-action races. Same URL does not mean same visible world. Vinext needs a `visibleCommitVersion` and one lifecycle owner before broad compiler/cache work. The lifecycle controller should wrap the existing candidate-commit seam first: ```txt createPendingNavigationCommit() resolvePendingNavigationCommitDisposition() pending browser-router promise pendingPathname ownership pre-paint URL/history effect server-action same-URL commit path prefetch cache-only path ``` The first controller should consolidate existing behavior. It should not rewrite all router semantics. ### 4. Safe Fallbacks Can Become A Performance Trap The law says uncertainty must not degrade to reuse. That is correct. But a naive implementation can collapse cache hit rate by treating routine runtime observations as global uncertainty. For example, a route that reads a generic header or cookie should not automatically poison every cache class unless that input actually affects the output and can be modeled safely. Rules: ```txt runtime observations downgrade only the output they affect public cache dimensions must be allowlisted, canonical, bounded, and redacted unknown private/auth/draft/session inputs degrade to private, uncacheable, or fresh render fresh render is one fallback, not the universal fallback cache-hit rate and variant cardinality must be measured before broad rollout ``` Do not optimize with probabilistic reuse. Do optimize by keeping proof scopes narrow. ### 5. Skip Transport Can Backfire `ClientReuseManifest` is an untrusted hint. It must not become a server CPU or storage-IO amplifier. Skip transport must start with the cheapest proven class, likely static layout entries: ```txt same graph/deployment compatibility same root boundary same route/topology identity compatible params/search/interception/mounted-slot context prior render observation says no dynamic request API usage no incompatible boundary outcome local metadata is enough to verify ``` Hard rule: ```txt If verifying skip requires more work than rendering and sending the entry, do not skip. ``` Abuse limits are protocol requirements: ```txt maximum manifest byte size maximum entry count canonical ordering bounded hash algorithm replay window / visibleCommitVersion compatibility private-entry rejection unknown-entry ignore path trace reason for every rejected manifest entry ``` Skip transport is a later optimization, not a prerequisite for the router spine. ### 6. Deployment Compatibility Must Be Algebra, Not Strict Equality Everywhere Strict `graphVersion` and `deploymentVersion` equality is too brittle for rolling deploys, multi-region edges, previews, canaries, and rollback windows. The architecture needs a deployment compatibility protocol before hard-navigation decisions depend on version mismatches. Required concepts: ```txt graph version asset/deployment version compatibility map generated at build time client/server handshake old-client/new-server fallback new-client/old-server fallback hard-navigation loop prevention asset pinning for in-flight RSC payloads controlled cache miss or render-fresh path when compatibility is unknown ``` A version mismatch may require a hard navigation in some cases. It must not produce reload loops or break SPA behavior during normal rolling deploys. ### 7. Runtime Profile Must Not Own Semantics Cloudflare Workers are the primary production target for vinext. That matters. The semantic core must be binding-free. Cloudflare-specific storage, Durable Objects, KV, Cache API, Queues, or R2 integration belongs behind a runtime profile boundary. Runtime profile may execute: ```txt cache reads/writes artifact reads/writes coherence coordinator calls background jobs runtime trace fields runtime hot-path metrics ``` Runtime profile must not decide: ```txt route topology slot preservation layout reuse interception meaning cache equivalence visible commit authority ``` Runtime storage hits are not semantic proof by themselves. ### 8. Streaming And Activity Are Follow-Up Specs Streaming chunks and Activity/hidden-route preservation are real future needs, but they should not block the lifecycle spine. Before streaming reveal is implemented, the architecture must specify: ```txt chunk terminal settlement stale chunk recovery boundary ownership generation cache-seed-only eligibility no permanently hung Suspense boundary when a chunk is discarded ``` Before Activity preservation is implemented, it must specify: ```txt owner memory budget eviction policy auth/session invalidation effect cleanup style isolation focus and accessibility behavior cross-tab invalidation behavior ``` Until then, these remain out of scope for the first migration stage. ### 9. Correctness Oracle Without Process Theater Every semantic PR must name its oracle: ```txt Next public semantics conformance Vinext internal invariant Intentional documented divergence ``` This is required because vinext aims to match Next public behavior unless a divergence is deliberate. But do not require full `NavigationTrace`, cache cardinality proof, and runtime coherence proof for every small PR. The process should scale with the semantic risk. Minimal early requirement: ```txt what old writer is being deleted what new owner writes the decision what oracle defines correctness what hostile sequence or boundary case is covered what fallback occurs on uncertainty ``` ### 10. Lock Criteria From Final Review Lock the direction, not every future type shape. The architecture is only useful if future PRs cannot rebuild the old implicit router under cleaner names. Non-negotiable lock criteria: ```txt visible state mutation happens only through an approved visible commit transaction visibleCommitVersion increments in exactly one place NavigationPlanner v0 stays narrow: requestWork, proposeCommit, noCommit, hardNavigate raw AppElementsWire keys are fenced by constructors/parsers and import-boundary checks unknown root-layout identity is traced as legacy fallback or uncertainty, not proof of safe merge reusable output has positive and negative render-observation proof for the relevant scope CacheVariant dimensions have hard budgets and a breaker path every reusable/skippable artifact carries a compatibility envelope before cache/skip depend on it NavigationTrace uses compact reason codes and structured fields, not narrative logs semantic PRs delete the old writer for the promoted path in the same PR v0 does not claim full Cache Components or Activity hidden-route preservation ``` ## Core Ownership Model ### 1. AppElementsWire Implementation name: `AppElementsWire`. Owns: ```txt serialization deserialization transport compatibility wire-key encoding RSC/HTML payload boundary merge buffer compatibility while legacy paths remain ``` Does not own: ```txt topology slot ownership layout preservation default/unmatched behavior interception meaning cache identity visible commit permission ``` The flat payload is how data travels. It is not how the router thinks. ### 2. RouterState And RouteSnapshot Implementation names: `RouterState`, `RouteSnapshot`. Route state owns visible continuity. It must keep together the facts that change together: ```ts type RouterState = { visibleSnapshot: RouteSnapshot previousSnapshot: RouteSnapshot | null activeOperation: OperationRecord | null visibleCommitVersion: number } type RouteSnapshot = { id: RouteSnapshotId routeId: RouteId rootBoundaryId: RootBoundaryId | null displayUrl: string matchedUrl: string params: ParamFingerprint search: SearchFingerprint mountedSlots: MountedSlotState interceptionContext: InterceptionContext | null cacheContext: CacheContextFingerprint } ``` State must not be reconstructed from wire keys. ### 3. Navigation Events Implementation name: `NavigationEvent`. Every router input becomes an event. Minimum v0 event set: ```ts type NavigationEvent = | { kind: "hydrate"; href: string; ssrSnapshot: HydrationSnapshot } | { kind: "navigate"; href: string; mode: "push" | "replace" } | { kind: "traverse"; direction: "back" | "forward"; historyState: unknown } | { kind: "refresh"; scope: RefreshScope } | { kind: "prefetch"; href: string } | { kind: "serverActionSubmitted"; actionId: ActionId } | { kind: "flightResponseArrived"; token: OperationToken; result: FlightResult } | { kind: "serverActionReturned"; token: OperationToken; result: ActionResult } | { kind: "operationFailed"; token: OperationToken; error: RuntimeError } | { kind: "operationAborted"; token: OperationToken; reason: AbortReason } ``` Async result events carry causal proof: ```ts type OperationToken = { operationId: OperationId lane: OperationLane baseVisibleCommitVersion: number graphVersion: GraphVersion deploymentVersion: DeploymentVersion targetSnapshotFingerprint: string cacheVariantFingerprint?: string } ``` A result is not allowed to commit because it finished. It may commit only if lifecycle still authorizes it. ### 4. NavigationPlanner Implementation name: `navigationPlanner.plan()`. The planner owns route semantics, but only at the semantic planning and interpretation layer. Shape: ```ts function reduce( schema: RouteManifest, state: RouterState, event: NavigationEvent, ): NavigationDecisionV0 ``` Minimum v0 output: ```ts type NavigationDecisionV0 = | { kind: "requestWork" token: OperationToken work: RequestedWork trace: NavigationTrace } | { kind: "proposeCommit" token: OperationToken proposal: CommitProposal trace: NavigationTrace } | { kind: "noCommit" token: OperationToken reason: NoCommitReason trace: NavigationTrace } | { kind: "hardNavigate" token: OperationToken url: string reason: HardNavigationReason trace: NavigationTrace } ``` Promote `SegmentOp`, `SlotOp`, richer effect sets, and cache-specific decisions only when a real semantic slice needs them. Do not ship a fat reducer before one current path proves the spine. `NavigationTrace` must be reason-code based: compact codes plus structured fields that explain the decision without becoming another router object graph. The planner proposes. The lifecycle gate approves. The executor applies. The planner must be boring at first. Boring is good here. ### 5. NavigationLifecycleController Implementation name: `NavigationLifecycleController`. Owns: ```txt operation identity operation lane base visible commit version operation token compatibility terminal state commit permission transition promise settlement visibleCommitVersion increment RouterState update after successful commit ``` Operation lanes: ```txt visible traverse refresh action prefetch recovery ``` Terminal states: ```txt committed superseded aborted failed hard-navigated cache-seeded refresh-scheduled ``` Rules: ```txt newer visible work supersedes older visible work prefetch never commits visible UI refresh is a real operation and can be superseded same-URL commits use visibleCommitVersion, not URL alone stale candidate commits must not patch visible state server-action results are ordered by OperationToken and visibleCommitVersion older action results may return values, invalidate, schedule refresh, or cache-seed only; they may not overwrite newer visible state RSC redirects stay inside one operation lifecycle when possible ``` ### 6. CommitDecision, ApprovedVisibleCommit, And BrowserDelta Visible browser mutation must go through one approved commit transaction. An approved visible commit is the only path that may mutate: ```txt browser URL/history visible RouterState payload store mounted-slot state scroll/focus restoration transition promise settlement visibleCommitVersion ``` There must be exactly one place where `visibleCommitVersion++` happens. Everything else is either candidate work, rejected work, hard navigation, or non-visible cache seeding. ```ts type ApprovedVisibleCommit = { token: OperationToken baseVisibleCommitVersion: number nextVisibleCommitVersion: number decision: CommitDecision browserDeltas: BrowserDelta[] storeDeltas: StoreDelta[] trace: NavigationTrace } type CommitDecision = | { kind: "apply" transitionMode: "sync" | "transition" | "viewTransition" } | { kind: "hardNavigate"; url: URL; reason: HardNavigationReason } | { kind: "noCommit"; reason: NoCommitReason } ``` Hard navigation is terminal. It should not sit beside normal browser deltas. `transitionMode` is part of commit authority. Shell code may execute the mode, but must not reinterpret the semantic decision. ### 7. Route Facts Compiler Implementation names: `RouteManifestBuilder`, `RouteManifest`, `StaticSegmentGraph`. The compiler owns build-time topology and stable facts. It compiles facts the current implementation already knows implicitly: ```txt segments layouts pages templates parallel slots implicit children slot default entries route groups root-layout boundaries interception rules stable graph-minted IDs canonical constructors conservative static/dynamic hints ``` The compiler should not emit a full transition automaton. Runtime transition decisions remain in the planner. Build-time classifications are hints. Runtime observations can always downgrade cacheability. ### 8. Render Observation Protocol Implementation names: `RenderOutcome`, `RenderObservation`. This is the missing bridge between pure planning and real execution. A render must report what actually happened before cache write, reuse proof, streaming reveal, or visible commit approval can rely on it: ```txt observed cookies() observed headers() observed draft/auth/session state observed dynamic fetches explicit no-request-api observation where reuse depends on absence cache tags path tags redirect/notFound/forbidden/unauthorized/error/global-error outcomes HTML/RSC renderEpoch metadata privacy/cacheability downgrade output scope: layout, page, slot, route wiring, HTML, RSC, boundary outcome stream dependencies, later ``` Render observations are not side-channel decisions. They re-enter the planner/lifecycle/cache model as explicit events or result metadata. Absence of a recorded dynamic read is not enough for reuse unless the render scope was observed completely enough to prove that absence. A reusable output needs a scoped bill of health: what it did observe, what it did not observe, what boundary outcome it produced, and which artifact envelope it belongs to. ### 9. CacheVariant And Resource Dependencies Implementation names: `CacheVariant`, `ResourceDependencyGraph`. Cache identity answers: ```txt May these outputs be reused as equivalent? ``` Resource dependencies answer: ```txt What inputs, resources, tags, actions, deployment facts, and runtime observations can invalidate that equivalence? ``` Start narrow. Do not attempt full cache algebra before the lifecycle spine exists. Cache v1 should cover: ```txt graph/deployment compatibility root/route/render identity params/search where required mounted-slot fingerprint where required interception context where required renderEpoch pairing for HTML/RSC privacy/cacheability downgrade from render observations boundary outcome compatibility ``` Mandatory cache dimension rules: ```txt no raw cookies as public dimensions no raw headers as public dimensions no bearer tokens, session cookies, or secrets as public dimensions no unbounded raw user input as public dimensions public dimensions must be allowlisted, canonicalized, bounded, classified, and redacted in traces ``` Mandatory cache budget rules: ```txt maximum encoded dimension length maximum dimension count maximum value count per dimension canonical ordering redacted trace representation per-route variant ceiling breaker fallback when the ceiling is exceeded ``` If a route exceeds its variant ceiling, the fallback is private, uncacheable, or fresh render for the affected output. Measurement is not enough without an enforcement path. ### 10. Artifact Compatibility Envelope Implementation name: `ArtifactCompatibilityEnvelope`. Every payload or artifact that may later participate in cache reuse or skip transport must carry compatibility metadata before those systems rely on it: ```txt graph version asset/deployment/build ID payload schema version route schema version RSC protocol version renderEpoch root boundary compatibility set ``` Cache coherence and skip transport can remain disabled at first. The envelope should land early so old-client/new-server, new-client/old-server, rolling deploy, canary, and rollback behavior has a protocol instead of a boolean equality check. ### 11. Runtime Profile Boundary Implementation names: `RuntimeProfile`, `CacheCoordinator`, `ArtifactStore`. Runtime profiles execute approved work. Cloudflare is the first production target, but the semantic core must not import raw runtime bindings. Generic layers: ```txt RequestMemo: per-request dedupe only Process/Isolate microcache: opportunistic only Response/artifact cache: hot local cache with semantic metadata Artifact store: immutable HTML/RSC/payload artifacts Coherence coordinator: invalidation floors, renderEpoch publication, pairing rules when needed Background jobs: revalidation, prewarm, cleanup; idempotent by key Hint/config store: read-mostly hints only; never correctness authority ``` Hot-path rule: ```txt A static public hit must not require a distributed coordination round trip on every request. ``` ## Validity Rules A reduction or runtime action is invalid if: ```txt visible state mutates outside an ApprovedVisibleCommit visibleCommitVersion increments from more than one owner rootLayoutTransition = crossRoot and CommitDecision is not hardNavigate BrowserDelta preserves a slot but RouterState cannot prove the slot exists or is retained slot default is rendered but StaticSegmentGraph has no default for that SlotId slot is marked unmatched but graph resolution found a matching slot route or default route is intercepted but event/context is not interception-capable layout is reused across a crossed root boundary unknown root-layout identity is treated as proof of safe merge after root-layout transition is promoted cache read occurs without CacheVariant compatibility cache entry has compatible CacheVariant but incompatible dependency fingerprint or invalidation ownership cache entry is reused without scoped proof that dynamic/private request APIs were not observed where that proof is required CacheVariant exceeds the route budget and does not take the breaker fallback artifact is cached, reused, or skipped without a compatible ArtifactCompatibilityEnvelope HTML/RSC pair crosses renderEpoch incompatibly successful RSC payload is paired with incompatible error/notFound/unauthorized HTML, or the reverse cached notFound/forbidden/unauthorized boundary is reused as a successful route payload private/auth/cookie/header/draft-sensitive output is cached as public async result commits with stale visibleCommitVersion older server-action result overwrites newer visible state stream chunk mutates visible UI without lifecycle approval, once streaming exists server accepts ClientReuseManifest entries without verifying graph, deployment, variant, epoch, payload hash, and invalidation compatibility, once skip exists skip transport is enabled before verification cost and CacheVariant cardinality are measured and bounded unbounded raw user input becomes a public CacheVariant dimension runtime hint store or non-authoritative cache decides invalidation floor, current renderEpoch, HTML/RSC pairing, or private cache safety generated code constructs NavigationDecisionV0, BrowserDelta, or CacheVariant directly instead of calling typed contracts NavigationTrace relies on narrative logs instead of reason codes and structured fields for semantic decisions wire-key absence is used as preserve/delete/default/skip proof after the relevant semantic decision has been promoted uncertain cache/deployment/graph compatibility results in reuse instead of safe fallback ``` ## Navigation Kind Semantics The planner must branch on explicit navigation kind, not infer it from payload shape. ```txt hydrate: may use previous state: SSR snapshot only may preserve slots: only if SSR snapshot proves them commit: initialise or recovery soft navigate: may use previous state: yes may preserve slots: yes may apply interception: yes commit: soft commit hard navigate: may use previous state: no commit: full document navigation refresh: may use previous state: yes may preserve slots: yes may apply interception: current-context only commit: refresh commit traverse / popstate: may use previous state: yes, from history-derived state when available may preserve slots: yes, when history/current state proves it commit: traverse commit prefetch: may seed cache only may not commit visible UI server-action refresh: may use previous state: yes may preserve slots: yes may apply interception: current-context only commit: lifecycle-approved action refresh, redirect, noCommit, cache-seed only, or refresh-scheduled ``` ## Implementation Plan This is not a fixed PR count. Split into small, reviewable PRs. Governing sequence: ```txt freeze today's flat-wire behavior with compatibility tests fence AppElementsWire add minimal compiled facts and compatibility envelope skeleton add lifecycle transaction contract and reason-code trace shell build the thin end-to-end spine for one current navigation path promote root-layout hard navigation and delete the old writer for that path only then promote slots, interception, actions, cache identity, and skip transport ``` ### Layer 0: Keep The Landed Foundation Do not redo the flat payload milestone. Keep: ```txt flat keyed AppElements payload layout/page/template/slot/route entries browser merge/replace behavior SSR/browser deserialization symmetry root-layout hard navigation behavior, until promoted absent-key soft-navigation preservation, until promoted mounted-slot cache variants, until promoted ``` ### Layer 1: Lifecycle Spine First Goal: make visible commit authority explicit while preserving behavior. Deliverables: ```txt OperationRecord operation lanes terminal states visibleCommitVersion ApprovedVisibleCommit transaction one visibleCommitVersion increment owner lifecycle approval barrier pending promise settlement owned by lifecycle same-URL/server-action stale commit rejection prefetch cache-only lane traverse/back-forward intent adapter where platform evidence exists ``` Acceptance: ```txt newer navigation beats older RSC response old RSC response can resolve late without committing visible state prefetch can resolve late and seed cache only server action resolving after newer visible commit cannot clobber the route refresh can be superseded RSC redirect chains keep one pending lifecycle hard-navigation recovery only fires for the current operation no-op back/forward cannot leave pending stuck ``` ### Layer 2: Fence AppElementsWire Goal: stop semantic meaning from spreading through flat wire keys. Deliverables: ```txt AppElementsWire codec boundary approved wire-key constructors/parsers raw wire-key parsing restrictions import-boundary checks compatibility tests for current payload behavior ``` No semantic promotion yet. ### Layer 3: Minimal Route Manifest Goal: compile the facts needed for the first promotions. Deliverables: ```txt RouteManifestBuilder skeleton RouteManifest skeleton StaticSegmentGraph skeleton graphVersion ArtifactCompatibilityEnvelope skeleton graph-minted IDs for routes/layouts/pages/templates/slots/defaults/root boundaries minimal canonical constructors fixture tests for graph output ``` Not yet: ```txt full dependency graph full cache algebra transition automaton skip transport streaming protocol Activity preservation ``` ### Layer 4: NavigationPlanner v0 Goal: route one existing navigation path through the new ownership boundaries while preserving behavior. Deliverables: ```txt navigationPlanner.plan() for navigate / refresh / traverse / flightResponseArrived NavigationDecisionV0 only: requestWork / proposeCommit / noCommit / hardNavigate CommitProposal ApprovedVisibleCommit handoff reason-code NavigationTrace minimal invariant checker current AppElementsWire emitted from the new path old path remains only as compatibility fallback for unpromoted paths ``` The planner must remain small and pure. Async facts re-enter as events. ### Layer 5: Promote First Semantic Decisions Promote one decision area per PR and delete the old writer in the same PR. Recommended order: ```txt root-layout hard navigation boundary outcomes: error/notFound/forbidden/unauthorized/global-error mounted-slot preservation default/unmatched slot behavior interception transition rules server-action refresh semantics scroll/focus/hash restoration ``` Each PR must state: ```txt old writer deleted new owner correctness oracle hostile sequence covered fallback on uncertainty ``` ### Layer 6: Cache Coherence v1 Goal: make existing cache reuse safer before making it more aggressive. Deliverables: ```txt RenderIdentity ReuseIdentity InvalidationScope canonical CacheVariant serialization RenderOutcome / RenderObservation integration RouteCacheRecord v1 ArtifactCompatibilityEnvelope checks negative RenderObservation proof where reuse depends on absence renderEpoch HTML/RSC pairing checks privacy/cacheability downgrade rules boundary outcome compatibility cache variant cardinality measurement cache variant budget enforcement and breaker fallback ``` Do not enable broad skip transport in this layer. ### Layer 7: Proof-Backed Skip Transport v1 Goal: reduce server work and bytes only where proof is cheap. Initial eligible class: ```txt static layout entries only same compatible graph/deployment/root compatible route params/search/interception/mounted-slot context no dynamic request API observation no incompatible boundary outcome local metadata verification only ``` Deliverables: ```txt ClientReuseManifest as untrusted hint manifest abuse limits ServerRenderPlan ReuseProof verification-cost budget trace reason for every accepted/rejected skip fallback to render when proof is unavailable or expensive ``` ### Layer 8: Runtime Profile v1 Goal: keep runtime execution behind typed contracts after the semantic spine exists. Deliverables: ```txt runtime cache interface artifact store interface coherence coordinator interface, only where required background job interface with idempotency keys Cloudflare profile as first implementation runtime hot-path metrics ``` The semantic core stays binding-free. ### Later Layers: Streaming And Activity Separate follow-up specs required for: ```txt operation-tokened stream chunks reveal boundary ownership stale chunk recovery and settlement hidden route model Activity memory/eviction/auth/effect/focus policies ``` Do not implement these before lifecycle authority, Route Manifest data, and cache coherence v1 exist. ## Test Strategy The most important tests are sequence tests and hostile timelines, not giant snapshots. Generate apps with: ```txt nested layouts templates parallel slots default slots unmatched slots route groups interception routes dynamic params search params root-layout boundaries static layouts cookie/header/auth/draft-sensitive layouts server actions concurrent server actions prefetch races back/forward traversals cache invalidations deployment changes ``` Generate event sequences like: ```txt A -> B -> C, with B resolving last prefetch A navigate B old A prefetch resolves server action resolves after same-URL refresh server actions A and B resolve out of order back/back/forward while RSC responses are pending cross-root navigation while old result arrives slot mounted slot unmatched refresh action redirect HTML cache hit but RSC cache miss RSC cache hit but HTML stale deployment A client receives deployment B payload ``` Required invariants: ```txt No stale operation commits visible state. No visible state mutates outside ApprovedVisibleCommit. No visibleCommitVersion increment exists outside the lifecycle owner. No older server-action result overwrites newer visible state. No cache entry is read without CacheVariant compatibility. No cache entry is reused without required positive and negative RenderObservation proof. No cache variant ceiling overflow reuses public cache. No cached/reused/skipped artifact lacks a compatible ArtifactCompatibilityEnvelope. No HTML/RSC pair crosses renderEpoch incompatibly. No layout is reused across root-layout boundary. No slot is preserved unless RouterState proves it existed, once promoted. No interception applies without an interception-capable context, once promoted. No AppElementsWire absence has semantic meaning after the relevant decision is promoted. No private/cookie/auth-sensitive response is cached under a public variant. No runtime hint-store value is treated as authoritative invalidation or renderEpoch state. No ClientReuseManifest entry is trusted without server-side verification, once skip exists. No uncertain graph/deployment/cache state results in reuse. Every semantic change names its oracle. ``` ## Hot Path Budget Track performance by route/cache class. Do not claim the architecture is faster merely because it is more formal. At minimum, measure: ```txt runtime cache reads/writes coordinator calls artifact store reads/writes background job writes and duplicate/idempotent replays runtime subrequests / external IO server CPU time RSC bytes sent layouts rendered client remount count p95/p99 navigation latency cache variant cardinality ElementStore memory stale commit rejection count skip verification cost versus render cost hard-navigation count caused by deployment mismatch cache variant breaker count compatibility-envelope mismatch count trace reason-code distribution ``` Performance claims should be based on work avoided: ```txt fewer layout renders fewer RSC bytes fewer remounts lower p95/p99 latency bounded runtime/coordinator calls on hot paths ``` ## Success Criteria This migration succeeds when: ```txt The flat payload wire format is transport, not semantic authority. Visible commits go through one lifecycle-approved atomic transaction. Same-URL/server-action stale commits are rejected, cache-seeded only, or translated into explicit refresh behavior. NavigationPlanner is small, pure, event-driven, and v0 exposes only requestWork/proposeCommit/noCommit/hardNavigate. Async render/server-action outcomes feed back as explicit events or result metadata. Every promoted route-semantic decision has one owner and the old writer is deleted. Every applied commit has explicit transitionMode and lifecycle-approved fallback path. Every cache read uses CacheVariant compatibility. Every cache write uses render observations to decide privacy/cacheability. Every reused output has scoped positive and negative render-observation proof where reuse depends on absence. Every CacheVariant has enforced budgets and a breaker fallback. Every cached, reused, or skipped artifact has a compatible artifact envelope. Every HTML/RSC pair has compatible renderEpoch. Every skip is explained by cheap graph/cache/dependency proof and is bounded by abuse limits. Deployment/version mismatch has a compatibility protocol and cannot produce hard-navigation loops. Cloudflare runtime execution stays behind a profile boundary. Generated code calls typed contracts and does not recreate semantic decisions. NavigationTrace uses compact reason codes and structured fields. Missing payload, missing wire key, missing branch, and cache miss do not imply semantic preservation after the relevant decision is promoted. ``` ## Out Of Scope For The First Migration Stage Do not add yet: ```txt full generated transition automaton separate reference interpreter global strong cache consistency for all content complete Cloudflare storage profile before the semantic spine exists new public router API surface broad skip transport custom stream chunk protocol Activity / hidden route preservation full Cache Components parity stronger-than-Next performance claims before benchmarks prove them ``` ## Related Issues / PRs / References - #737: segment-map groundwork for parallel route segment selection - #750: flat keyed payload cutover - #811: mounted-slot cache variants showed why cache identity must be semantic - #842: layout classification should drive render-time omission, not Flight byte surgery - #863: generated-code lifecycle seam should move toward owned typed contracts - #918: navigation lifecycle controller / visible commit authority - Next App Router type reference: https://raw.githubusercontent.com/vercel/next.js/canary/packages/next/src/shared/lib/app-router-types.ts ## Final Statement This architecture is not trying to make a prettier flat map. It is trying to make the router less magical without making it slower, more brittle, or impossible to ship. The NavigationPlanner should be small. Lifecycle authority should land early. Cache proof should start narrow. Skip transport should prove that it saves work before it is trusted. Deployment compatibility must be designed for rolling edges. Runtime profiles execute approved work; they do not own route meaning. The discipline remains: ```txt No proof, no reuse. No proof, no skip. No proof, no visible commit. No proof, use the safest event-specific fallback. ``` The implementation discipline is just as important: ```txt Thin spine first. One semantic promotion at a time. Delete the old writer when the new owner lands. Measure before claiming performance. ```
Author
Owner

@NathanDrake2406 commented on GitHub (May 4, 2026):

Image
<!-- gh-comment-id:4368037501 --> @NathanDrake2406 commented on GitHub (May 4, 2026): <img width="1536" height="1024" alt="Image" src="https://github.com/user-attachments/assets/64aef008-600d-481b-b268-f3526dfa74e8" />
Author
Owner

@NathanDrake2406 commented on GitHub (May 4, 2026):

# #726 Practical Parallel Roadmap

Goal:
Keep PRs substantial enough to review as real architectural slices, not tiny ceremonial nouns.

Target PR size:
- Under ~1k changed lines is fine.
- Each PR should either move a real boundary, promote a semantic behaviour, or add a complete disabled proof model.
- Avoid PRs that only add unused types unless they are immediately consumed inside the same PR.

Rule:
Same wave means “can be worked once its own dependencies are landed”.
A PR does not wait for unrelated PRs in the same wave.
## WAVE 1 — Foundation shells already underway

#726-WIRE-01
Introduce AppElementsWire codec boundary
Depends on: landed flat AppElements payload

#726-GRAPH-01
Mint stable semantic IDs from existing route graph
Depends on: landed route graph extraction

#726-CORE-01
Add visibleCommitVersion + OperationRecord
Depends on: landed browser navigation lifecycle controller extraction

#726-COMPAT-01
Add ArtifactCompatibilityEnvelope skeleton
Depends on: WIRE-01 preferred, but can land standalone with rebase

#726-OPS-01
Add compact navigation reason-code trace shell
Depends on: CORE-01 preferred, but can land standalone with rebase
## WAVE 2 — Substantial boundary PRs

#726-CORE-02/03
Introduce ApprovedVisibleCommit and move browser state mutation behind it
Depends on: CORE-01

Includes:
- ApprovedVisibleCommit
- CommitDecision
- approved visible commit constructor/helper
- single visible mutation path for current browser commits
- current semantics preserved
- tests proving current navigate / replace / traverse / hard-nav behaviour still works

Does not include:
- stale same-URL server-action rejection
- NavigationPlanner
- cache semantics
#726-WIRE-02/03
Fence raw wire-key construction and add legacy wire compatibility coverage
Depends on: WIRE-01

Includes:
- ban or lint raw AppElements wire-key construction outside AppElementsWire
- approved constructors/parsers
- legacy metadata compatibility tests
- unmatched-slot sentinel compatibility tests
- payload metadata read/write coverage

Does not include:
- route topology semantics
- slot preservation semantics
- cache authority
#726-GRAPH-02/03
Expose RootBoundaryId and add minimal RouteManifest read model
Depends on: GRAPH-01

Includes:
- RootBoundaryId
- graphVersion
- minimal RouteManifest / StaticSegmentGraph shape
- route/layout/page/template/slot lookup by semantic ID
- fixture tests for stable graph output

Does not include:
- full transition automaton
- planner ownership
- cache dependency graph
#726-COMPAT-02/03
Attach concrete compatibility metadata and define unknown-compat fallback
Depends on: COMPAT-01, GRAPH-01, WIRE-01

Includes:
- graph/deployment/schema metadata attached to payloads
- rootBoundaryId when available
- renderEpoch field carried but still mostly null if not known
- unknown compatibility fallback helper
- tests proving unknown compatibility never becomes positive reuse proof

Does not include:
- cache reuse
- skip transport
- rolling deploy protocol completeness
#726-CACHE-01/04
Add disabled cache proof model
Depends on: none

Includes:
- RenderObservation v0
- CacheVariant v0
- BoundaryOutcomeCompatibility v0
- CacheVariantBudget
- breaker fallback shape
- redacted/canonical dimension representation
- no runtime reuse

Does not include:
- recording real observations
- enabling cache hits
- public/private cacheability enforcement
#726-OPS-02/03
Add visible commit invariants and transaction trace codes
Depends on: CORE-02/03, OPS-01

Includes:
- visibleCommitVersion single-owner invariant
- no visible mutation outside ApprovedVisibleCommit invariant
- structured trace codes for visible commit / noCommit / hardNavigate
- focused tests around the approved commit path

Does not include:
- broad runtime logging
- cache or skip metrics
## WAVE 3 — Parallel lifecycle behaviour PRs

#726-CORE-04
Prevent stale same-URL server-action commits
Depends on: CORE-02/03

Includes:
- use visibleCommitVersion instead of URL equality alone
- stale same-URL action result may return action value / schedule refresh / cache-seed only
- stale action result must not overwrite newer visible state
- hostile race tests

Does not include:
- discarded revalidation model
- redirect lifecycle
#726-CORE-05/06A
Lifecycle-gate router.refresh and prefetch outcomes
Depends on: CORE-02/03

Includes:
- refresh as lifecycle-gated operation
- refresh can be superseded
- prefetch resolves as cache-seed only
- prefetch never commits visible UI
- hostile refresh/prefetch tests

Does not include:
- full refresh lane model
- cache reuse authority
#726-CORE-06B
Lifecycle-gate traverse outcomes
Depends on: CORE-02/03

Includes:
- traverse/back-forward operation token
- stale traverse result cannot patch visible state
- no-op back/forward cannot leave pending state stuck
- tests for popstate/traverse ordering

Does not include:
- full back/forward intent model
- Activity preservation
## WAVE 4 — Planner and root-boundary ownership

#726-CORE-07/08
Add NavigationEvent / NavigationDecisionV0 and route root-boundary hard navigation through planner
Depends on: CORE-02/03, GRAPH-02/03, OPS-01

Includes:
- NavigationEvent v0
- NavigationDecisionV0: requestWork / proposeCommit / noCommit / hardNavigate
- planner path for root-boundary hard navigation
- current behaviour preserved
- root-boundary trace codes
- tests for same-root, cross-root, unknown-root fallback

Does not include:
- slot semantics
- template semantics
- cache decisions
#726-CORE-09
Delete legacy root-layout hard-navigation writer
Depends on: CORE-07/08

Includes:
- remove old root-layout decision writer
- planner/lifecycle path becomes the single authority for root-boundary decisions
- regression tests proving old fallback cases still work
- unknown root identity is fallback/uncertainty, not proof of safe merge

Does not include:
- layout persistence promotion beyond root-boundary decision
#726-COMPAT-04/05
Add old-client/new-server coverage and hard-navigation loop prevention
Depends on: COMPAT-02/03

Includes:
- old-client/new-server coverage
- hard-navigation loop prevention guard
- unknown compatibility fallback tests
- no cache/skip dependency yet

Does not include:
- full rolling deploy support
- skip transport
## WAVE 5 — RouteManifest facts for semantic promotion

#726-GRAPH-04/06
Expose slot/default/template/boundary facts from RouteManifest
Depends on: GRAPH-02/03

Includes:
- slot ownership facts
- default slot facts
- template/reset facts
- boundary outcome facts
- fixture tests for route groups, slots, templates, and boundaries

Does not include:
- planner promotion of those behaviours
#726-CACHE-05/07
Record render observations and attach observation metadata to payloads/artifacts
Depends on: CACHE-01/04, COMPAT-02/03

Includes:
- scoped dynamic-request observations
- render scope
- boundary outcome metadata
- observation metadata attached to produced payloads/artifacts
- no reuse yet

Does not include:
- cache hit authority
- private/dynamic downgrade enforcement
## WAVE 6 — Layout/template/boundary semantic promotions

#726-CORE-10
Promote same-layout ancestor persistence
Depends on: CORE-09, GRAPH-02/03

Includes:
- ancestor persistence decision through planner/lifecycle path
- delete old implicit writer for this promoted path
- visible tree persistence fixtures

Does not include:
- slot preservation
- template reset
#726-CORE-11/12
Promote template reset and error/loading/not-found reset semantics
Depends on: CORE-09, GRAPH-04/06, CACHE-01/04

Includes:
- template reset semantics
- error/loading/not-found reset semantics
- boundary outcome compatibility use, no cache reuse
- delete/fence old implicit writers for promoted paths
- template/boundary hostile fixtures

Does not include:
- slot preservation
- cache reuse
## WAVE 7 — Slot semantics

#726-CORE-13
Promote mounted parallel slot preservation
Depends on: CORE-10, GRAPH-04/06

Includes:
- mounted slot preservation through planner/lifecycle path
- hard-load vs soft-nav slot fixtures
- old wire-absence preservation path fenced for promoted cases

Does not include:
- default/unmatched slot semantics
- interception
#726-CORE-14
Promote default/unmatched slot semantics
Depends on: CORE-13, GRAPH-04/06

Includes:
- default slot rendering rules
- unmatched slot rules
- no wire-key absence as proof after promotion
- unmatched/default hostile fixtures

Does not include:
- intercepted route preservation
## WAVE 8 — Interception and advanced lifecycle lanes

#726-CORE-15
Promote intercepted route preservation
Depends on: CORE-13, CORE-14, GRAPH-04/06

Includes:
- intercepted route preservation through planner/lifecycle path
- current-context refresh/action handling
- traverse fixture coverage where possible
- old implicit interception writer fenced for promoted cases

Does not include:
- discarded action revalidation
- RSC redirect lifecycle
#726-CORE-16/17
Model refresh as its own operation lane and discarded server-action revalidation
Depends on: CORE-04, CORE-05/06A, CACHE-05/07

Includes:
- refresh operation lane
- discarded server-action revalidation
- older action result may invalidate/schedule refresh/cache-seed only
- must not overwrite newer visible state
- hostile refresh/action tests

Does not include:
- RSC redirects
#726-CORE-18/19
Model RSC redirect lifecycle and back/forward traversal intent
Depends on: CORE-06B, CORE-07/08

Includes:
- redirects stay inside one operation lifecycle where possible
- terminal redirect handling
- back/forward traversal intent model
- redirect/traverse hostile fixtures

Does not include:
- streaming chunk lifecycle
#726-CACHE-08
Add private/dynamic downgrade classification, still no public reuse
Depends on: CACHE-05/07

Includes:
- private/auth/draft/session downgrade classification
- dynamic request observation downgrade mapping
- downgrade tests
- no cache hit authority

Does not include:
- static layout artifact reuse
#726-COMPAT-06/08
Add rolling deploy and old/new client fallback coverage
Depends on: COMPAT-04/05

Includes:
- rolling deploy fallback coverage
- old-client/new-server fresh-render fallback
- new-client/old-server fallback
- canary/rollback compatibility tests

Does not include:
- reload-loop prevention for skip transport
## WAVE 9 — Cache proof hardening, still disabled

#726-CACHE-09/11
Add static layout reuse proof and private/dynamic downgrade coverage, disabled
Depends on: CACHE-05/07, CACHE-08, CORE-10

Includes:
- static layout reuse proof model
- positive and negative render-observation proof
- private/dynamic downgrade coverage
- cache proof trace codes
- no actual reuse

Does not include:
- serving from cache
#726-CACHE-10
Enforce variant budget + breaker fallback
Depends on: CACHE-01/04, CACHE-08

Includes:
- max encoded dimension length
- max dimension count
- max value count
- per-route variant ceiling
- breaker fallback path
- cache cardinality tests

Does not include:
- cache hit authority
## WAVE 10 — Enable narrow cache authority

#726-CACHE-12
Enable proven static layout artifact reuse
Depends on: CACHE-09/11, CACHE-10, COMPAT-06/08

Includes:
- static layout artifact reuse only
- compatibility envelope check
- render observation proof check
- variant budget check
- fallback to render on uncertainty
- hot-path metrics for cache outcomes

Does not include:
- skip transport
- broad dynamic reuse
#726-CACHE-13/14
Use cache/reuse proof in planner decisions and reject incompatible cache entries through lifecycle/planner path
Depends on: CACHE-12, CORE-07/08, COMPAT-06/08

Includes:
- planner-visible cache proof decisions
- incompatible cache entry rejection
- no runtime cache hit as semantic proof by itself
- cache rejection trace codes

Does not include:
- ClientReuseManifest
## WAVE 11 — Skip transport protocol, disabled first

#726-SKIP-01/03
Define ClientReuseManifest protocol with rejection codes and abuse limits, disabled by default
Depends on: CACHE-09/11, COMPAT-06/08

Includes:
- ClientReuseManifest shape
- max manifest byte size
- max entry count
- canonical ordering
- bounded hash algorithm
- replay/visibleCommitVersion compatibility fields
- private-entry rejection
- unknown-entry ignore path
- skip rejection reason codes

Does not include:
- enabling skip transport
#726-CACHE-15
Add skip/cache correctness cross-checks, no skip enablement yet
Depends on: SKIP-01/03, CACHE-12

Includes:
- skip/cache proof cross-check helpers
- tests for incompatible graph/deploy/variant/epoch/payload hash/invalidation cases

Does not include:
- actual skip response behaviour
## WAVE 12 — Enable skip transport

#726-SKIP-04/05
Enable skip transport for proven static layouts and add malicious/oversized manifest coverage
Depends on: SKIP-01/03, CACHE-15

Includes:
- enable skip transport for static layouts only
- malicious manifest tests
- oversized manifest tests
- rejection traces
- fallback to render/send when verification is too expensive or uncertain

Does not include:
- dynamic skip
#726-COMPAT-09
Prevent reload loops across canary/rollback
Depends on: SKIP-04/05, COMPAT-06/08

Includes:
- reload loop prevention
- canary/rollback skip compatibility coverage
- hard-navigation fallback tests
#726-OPS-11
Add hot-path metrics for skip outcomes
Depends on: SKIP-04/05

Includes:
- skip accepted/rejected metrics
- cache/skip outcome counters
- no semantic authority in metrics
## LATER — Separate specs

#726-STREAM-01/03
Streaming tree patch protocol, stale chunk discard, chunk terminal settlement
Depends on: lifecycle approval model, planner result events

#726-ACTIVITY-01/05
Activity owner model, memory budget, auth/session invalidation, focus/accessibility/style isolation, cross-tab invalidation
Depends on: slot/interception preservation semantics
<!-- gh-comment-id:4368951255 --> @NathanDrake2406 commented on GitHub (May 4, 2026): ```md id="x71lr8" # #726 Practical Parallel Roadmap Goal: Keep PRs substantial enough to review as real architectural slices, not tiny ceremonial nouns. Target PR size: - Under ~1k changed lines is fine. - Each PR should either move a real boundary, promote a semantic behaviour, or add a complete disabled proof model. - Avoid PRs that only add unused types unless they are immediately consumed inside the same PR. Rule: Same wave means “can be worked once its own dependencies are landed”. A PR does not wait for unrelated PRs in the same wave. ``` ```md id="sxvzwz" ## WAVE 1 — Foundation shells already underway #726-WIRE-01 Introduce AppElementsWire codec boundary Depends on: landed flat AppElements payload #726-GRAPH-01 Mint stable semantic IDs from existing route graph Depends on: landed route graph extraction #726-CORE-01 Add visibleCommitVersion + OperationRecord Depends on: landed browser navigation lifecycle controller extraction #726-COMPAT-01 Add ArtifactCompatibilityEnvelope skeleton Depends on: WIRE-01 preferred, but can land standalone with rebase #726-OPS-01 Add compact navigation reason-code trace shell Depends on: CORE-01 preferred, but can land standalone with rebase ``` ```md id="yycohh" ## WAVE 2 — Substantial boundary PRs #726-CORE-02/03 Introduce ApprovedVisibleCommit and move browser state mutation behind it Depends on: CORE-01 Includes: - ApprovedVisibleCommit - CommitDecision - approved visible commit constructor/helper - single visible mutation path for current browser commits - current semantics preserved - tests proving current navigate / replace / traverse / hard-nav behaviour still works Does not include: - stale same-URL server-action rejection - NavigationPlanner - cache semantics ``` ```md id="nnp9r6" #726-WIRE-02/03 Fence raw wire-key construction and add legacy wire compatibility coverage Depends on: WIRE-01 Includes: - ban or lint raw AppElements wire-key construction outside AppElementsWire - approved constructors/parsers - legacy metadata compatibility tests - unmatched-slot sentinel compatibility tests - payload metadata read/write coverage Does not include: - route topology semantics - slot preservation semantics - cache authority ``` ```md id="yal29f" #726-GRAPH-02/03 Expose RootBoundaryId and add minimal RouteManifest read model Depends on: GRAPH-01 Includes: - RootBoundaryId - graphVersion - minimal RouteManifest / StaticSegmentGraph shape - route/layout/page/template/slot lookup by semantic ID - fixture tests for stable graph output Does not include: - full transition automaton - planner ownership - cache dependency graph ``` ```md id="bn1x28" #726-COMPAT-02/03 Attach concrete compatibility metadata and define unknown-compat fallback Depends on: COMPAT-01, GRAPH-01, WIRE-01 Includes: - graph/deployment/schema metadata attached to payloads - rootBoundaryId when available - renderEpoch field carried but still mostly null if not known - unknown compatibility fallback helper - tests proving unknown compatibility never becomes positive reuse proof Does not include: - cache reuse - skip transport - rolling deploy protocol completeness ``` ```md id="558u9i" #726-CACHE-01/04 Add disabled cache proof model Depends on: none Includes: - RenderObservation v0 - CacheVariant v0 - BoundaryOutcomeCompatibility v0 - CacheVariantBudget - breaker fallback shape - redacted/canonical dimension representation - no runtime reuse Does not include: - recording real observations - enabling cache hits - public/private cacheability enforcement ``` ```md id="6tk6rj" #726-OPS-02/03 Add visible commit invariants and transaction trace codes Depends on: CORE-02/03, OPS-01 Includes: - visibleCommitVersion single-owner invariant - no visible mutation outside ApprovedVisibleCommit invariant - structured trace codes for visible commit / noCommit / hardNavigate - focused tests around the approved commit path Does not include: - broad runtime logging - cache or skip metrics ``` ```md id="4d5p1c" ## WAVE 3 — Parallel lifecycle behaviour PRs #726-CORE-04 Prevent stale same-URL server-action commits Depends on: CORE-02/03 Includes: - use visibleCommitVersion instead of URL equality alone - stale same-URL action result may return action value / schedule refresh / cache-seed only - stale action result must not overwrite newer visible state - hostile race tests Does not include: - discarded revalidation model - redirect lifecycle ``` ```md id="obq1jw" #726-CORE-05/06A Lifecycle-gate router.refresh and prefetch outcomes Depends on: CORE-02/03 Includes: - refresh as lifecycle-gated operation - refresh can be superseded - prefetch resolves as cache-seed only - prefetch never commits visible UI - hostile refresh/prefetch tests Does not include: - full refresh lane model - cache reuse authority ``` ```md id="1z8y2y" #726-CORE-06B Lifecycle-gate traverse outcomes Depends on: CORE-02/03 Includes: - traverse/back-forward operation token - stale traverse result cannot patch visible state - no-op back/forward cannot leave pending state stuck - tests for popstate/traverse ordering Does not include: - full back/forward intent model - Activity preservation ``` ```md id="uhpm70" ## WAVE 4 — Planner and root-boundary ownership #726-CORE-07/08 Add NavigationEvent / NavigationDecisionV0 and route root-boundary hard navigation through planner Depends on: CORE-02/03, GRAPH-02/03, OPS-01 Includes: - NavigationEvent v0 - NavigationDecisionV0: requestWork / proposeCommit / noCommit / hardNavigate - planner path for root-boundary hard navigation - current behaviour preserved - root-boundary trace codes - tests for same-root, cross-root, unknown-root fallback Does not include: - slot semantics - template semantics - cache decisions ``` ```md id="rg6txt" #726-CORE-09 Delete legacy root-layout hard-navigation writer Depends on: CORE-07/08 Includes: - remove old root-layout decision writer - planner/lifecycle path becomes the single authority for root-boundary decisions - regression tests proving old fallback cases still work - unknown root identity is fallback/uncertainty, not proof of safe merge Does not include: - layout persistence promotion beyond root-boundary decision ``` ```md id="4emx8i" #726-COMPAT-04/05 Add old-client/new-server coverage and hard-navigation loop prevention Depends on: COMPAT-02/03 Includes: - old-client/new-server coverage - hard-navigation loop prevention guard - unknown compatibility fallback tests - no cache/skip dependency yet Does not include: - full rolling deploy support - skip transport ``` ```md id="8bp4vl" ## WAVE 5 — RouteManifest facts for semantic promotion #726-GRAPH-04/06 Expose slot/default/template/boundary facts from RouteManifest Depends on: GRAPH-02/03 Includes: - slot ownership facts - default slot facts - template/reset facts - boundary outcome facts - fixture tests for route groups, slots, templates, and boundaries Does not include: - planner promotion of those behaviours ``` ```md id="klis5b" #726-CACHE-05/07 Record render observations and attach observation metadata to payloads/artifacts Depends on: CACHE-01/04, COMPAT-02/03 Includes: - scoped dynamic-request observations - render scope - boundary outcome metadata - observation metadata attached to produced payloads/artifacts - no reuse yet Does not include: - cache hit authority - private/dynamic downgrade enforcement ``` ```md id="is3y7m" ## WAVE 6 — Layout/template/boundary semantic promotions #726-CORE-10 Promote same-layout ancestor persistence Depends on: CORE-09, GRAPH-02/03 Includes: - ancestor persistence decision through planner/lifecycle path - delete old implicit writer for this promoted path - visible tree persistence fixtures Does not include: - slot preservation - template reset ``` ```md id="alh2xl" #726-CORE-11/12 Promote template reset and error/loading/not-found reset semantics Depends on: CORE-09, GRAPH-04/06, CACHE-01/04 Includes: - template reset semantics - error/loading/not-found reset semantics - boundary outcome compatibility use, no cache reuse - delete/fence old implicit writers for promoted paths - template/boundary hostile fixtures Does not include: - slot preservation - cache reuse ``` ```md id="axm7tl" ## WAVE 7 — Slot semantics #726-CORE-13 Promote mounted parallel slot preservation Depends on: CORE-10, GRAPH-04/06 Includes: - mounted slot preservation through planner/lifecycle path - hard-load vs soft-nav slot fixtures - old wire-absence preservation path fenced for promoted cases Does not include: - default/unmatched slot semantics - interception ``` ```md id="dmmpe4" #726-CORE-14 Promote default/unmatched slot semantics Depends on: CORE-13, GRAPH-04/06 Includes: - default slot rendering rules - unmatched slot rules - no wire-key absence as proof after promotion - unmatched/default hostile fixtures Does not include: - intercepted route preservation ``` ```md id="59qlfi" ## WAVE 8 — Interception and advanced lifecycle lanes #726-CORE-15 Promote intercepted route preservation Depends on: CORE-13, CORE-14, GRAPH-04/06 Includes: - intercepted route preservation through planner/lifecycle path - current-context refresh/action handling - traverse fixture coverage where possible - old implicit interception writer fenced for promoted cases Does not include: - discarded action revalidation - RSC redirect lifecycle ``` ```md id="oq4wz0" #726-CORE-16/17 Model refresh as its own operation lane and discarded server-action revalidation Depends on: CORE-04, CORE-05/06A, CACHE-05/07 Includes: - refresh operation lane - discarded server-action revalidation - older action result may invalidate/schedule refresh/cache-seed only - must not overwrite newer visible state - hostile refresh/action tests Does not include: - RSC redirects ``` ```md id="35e8ss" #726-CORE-18/19 Model RSC redirect lifecycle and back/forward traversal intent Depends on: CORE-06B, CORE-07/08 Includes: - redirects stay inside one operation lifecycle where possible - terminal redirect handling - back/forward traversal intent model - redirect/traverse hostile fixtures Does not include: - streaming chunk lifecycle ``` ```md id="0vtgy7" #726-CACHE-08 Add private/dynamic downgrade classification, still no public reuse Depends on: CACHE-05/07 Includes: - private/auth/draft/session downgrade classification - dynamic request observation downgrade mapping - downgrade tests - no cache hit authority Does not include: - static layout artifact reuse ``` ```md id="j17abj" #726-COMPAT-06/08 Add rolling deploy and old/new client fallback coverage Depends on: COMPAT-04/05 Includes: - rolling deploy fallback coverage - old-client/new-server fresh-render fallback - new-client/old-server fallback - canary/rollback compatibility tests Does not include: - reload-loop prevention for skip transport ``` ```md id="a6bswc" ## WAVE 9 — Cache proof hardening, still disabled #726-CACHE-09/11 Add static layout reuse proof and private/dynamic downgrade coverage, disabled Depends on: CACHE-05/07, CACHE-08, CORE-10 Includes: - static layout reuse proof model - positive and negative render-observation proof - private/dynamic downgrade coverage - cache proof trace codes - no actual reuse Does not include: - serving from cache ``` ```md id="dfe7rg" #726-CACHE-10 Enforce variant budget + breaker fallback Depends on: CACHE-01/04, CACHE-08 Includes: - max encoded dimension length - max dimension count - max value count - per-route variant ceiling - breaker fallback path - cache cardinality tests Does not include: - cache hit authority ``` ```md id="u89o2v" ## WAVE 10 — Enable narrow cache authority #726-CACHE-12 Enable proven static layout artifact reuse Depends on: CACHE-09/11, CACHE-10, COMPAT-06/08 Includes: - static layout artifact reuse only - compatibility envelope check - render observation proof check - variant budget check - fallback to render on uncertainty - hot-path metrics for cache outcomes Does not include: - skip transport - broad dynamic reuse ``` ```md id="mvzn8q" #726-CACHE-13/14 Use cache/reuse proof in planner decisions and reject incompatible cache entries through lifecycle/planner path Depends on: CACHE-12, CORE-07/08, COMPAT-06/08 Includes: - planner-visible cache proof decisions - incompatible cache entry rejection - no runtime cache hit as semantic proof by itself - cache rejection trace codes Does not include: - ClientReuseManifest ``` ```md id="jp2qkf" ## WAVE 11 — Skip transport protocol, disabled first #726-SKIP-01/03 Define ClientReuseManifest protocol with rejection codes and abuse limits, disabled by default Depends on: CACHE-09/11, COMPAT-06/08 Includes: - ClientReuseManifest shape - max manifest byte size - max entry count - canonical ordering - bounded hash algorithm - replay/visibleCommitVersion compatibility fields - private-entry rejection - unknown-entry ignore path - skip rejection reason codes Does not include: - enabling skip transport ``` ```md id="rwqj98" #726-CACHE-15 Add skip/cache correctness cross-checks, no skip enablement yet Depends on: SKIP-01/03, CACHE-12 Includes: - skip/cache proof cross-check helpers - tests for incompatible graph/deploy/variant/epoch/payload hash/invalidation cases Does not include: - actual skip response behaviour ``` ```md id="r94a7x" ## WAVE 12 — Enable skip transport #726-SKIP-04/05 Enable skip transport for proven static layouts and add malicious/oversized manifest coverage Depends on: SKIP-01/03, CACHE-15 Includes: - enable skip transport for static layouts only - malicious manifest tests - oversized manifest tests - rejection traces - fallback to render/send when verification is too expensive or uncertain Does not include: - dynamic skip ``` ```md id="hh2yjs" #726-COMPAT-09 Prevent reload loops across canary/rollback Depends on: SKIP-04/05, COMPAT-06/08 Includes: - reload loop prevention - canary/rollback skip compatibility coverage - hard-navigation fallback tests ``` ```md id="xxixdp" #726-OPS-11 Add hot-path metrics for skip outcomes Depends on: SKIP-04/05 Includes: - skip accepted/rejected metrics - cache/skip outcome counters - no semantic authority in metrics ``` ```md id="mi0uyj" ## LATER — Separate specs #726-STREAM-01/03 Streaming tree patch protocol, stale chunk discard, chunk terminal settlement Depends on: lifecycle approval model, planner result events #726-ACTIVITY-01/05 Activity owner model, memory budget, auth/session invalidation, focus/accessibility/style isolation, cross-tab invalidation Depends on: slot/interception preservation semantics ```
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/vinext#155
No description provided.