The App Router client carried an OperationToken on every navigation
decision but only read its `lane` field. The authority the token was
meant to own lived inline elsewhere: the commit-staleness gate was a raw
boolean (startedNavigationId !== activeNavigationId or a visible-commit
version mismatch) evaluated before the token was even built, and cache
reuse was gated separately. The token also lacked the lifecycle
navigation id, so it could not answer "does this result belong to the
active navigation?" at all — its operationId is a per-render counter.
Promote OperationToken to the single proof-of-eligibility object. A new
operation-token.ts module owns the token type and verifyOperationToken,
a pure verifier over four dimensions (active-navigation, visible-commit,
graph-version, cache-variant) that returns a branded VerifiedOperationToken
on success. The token verifies; ApprovedVisibleCommit still mutates — the
two stay separate.
- Add navigationId to the token (sourced from startedNavigationId) so it
can carry the active-navigation authority.
- Route the commit-staleness gate through verifyOperationTokenForCommit
and delete the inline boolean. The staleOperation skip + trace are
byte-identical; planPendingRootBoundaryFlightResponse now requires the
VerifiedOperationToken brand, making "verify before plan" a compile-time
guarantee.
- Share the authority with cache reuse: planFlightResponseArrived gates an
accepted cache-entry reuse decision through verifyOperationTokenForCacheReuse,
hard-navigating (cacheReuseTokenRejected) when the proof's graph version no
longer matches the installed route graph. Behavior-preserving today (the
token's graphVersion is minted from the same manifest); a real guard once
cross-document and segment reuse can diverge (PR 6/7).
Absence is not permission: a checked dimension tolerates an absent
authority fact, but a *required* one fails closed, and require implies
evaluation — the contract segment-cache/BFCache writes need in PR 6/7.
Tests: operation-token.test.ts covers every verdict dimension, the brand,
the absent/mismatch split, and require⇒evaluate; navigation-planner and
app-browser-entry suites stay green, proving the gate refactor preserved
behavior; two new planner tests cover the cache-reuse graph-version seam.