Commit Graph

1 Commits

Author SHA1 Message Date
Nathan Nguyen 5b3ea11153 fix(navigation): promote OperationToken to the eligibility authority (#2009)
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.
2026-06-15 23:21:38 +01:00