Repository navigation
fix(router): the segment walker meters, spans and warns the same on a full match and a navigation (rango-t187) - #1285
Merged
Merged
Conversation
…and warns about a streamed slot Response, as the document does (rango-t187)
The one segment walker kept four reporting branches of the old full-match
walker. A navigation or an action revalidation gave a parallel slot's
handler no handler:<id> Server-Timing row and no rango.handler span, and an
orphan layout's handler no row; it metered a route's static-store hit as a
handler run; and only it warned in development when a streamed slot
handler returned a Response.
Both passes now meter and span every handler they run, meter no static hit
(docs/telemetry.md: a route's or slot's static hit records neither), and
warn on a streamed slot Response. What renders is unchanged.
walker-differential drops its five recorded exceptions ("slot handler
metered", "slot handler span", "slot Response warning", "orphan handler
metered", "static hit metered"). walker-observability-parity.rsc-test.tsx
pins the same rows, spans and warning through serveShellRequest.
…nly, after the ppr latch (rango-t187) withCacheLookup filtered the entries an intercept's fill does not hold before it called resolveLoadersOnly, so the ppr header latch there read a chain that could lack the route, its leaf. resolveAllSegments skips those entries itself, after its latch. resolveLoadersOnly now does the same (interceptFillSkips in its entry loop) and withCacheLookup passes the whole chain below the boundary. The same entries are skipped; nothing a consumer sees changes today.
49 of 50 tasks
…ker-parity # Conflicts: # packages/rangojs-router/CHANGELOG.md
…yout always is, on both passes (rango-t187) walker-differential's static-hit and orphan cases now list the segments each walk meters (`metered`), not only that the two walks agree: the static-hit route is absent on both, the orphan layout and its slot present on both. walker-observability-parity's orphan case asserts the orphan layout's d2-handler row on the document and on the navigation. The CHANGELOG entry says a Static() route's navigation row disappears.
This was referenced Oct 24, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
devcorner: rango-t187 · session rango-s397
Problem
A client navigation reported a page's handlers differently from the document request for the same page. The one segment walker (
resolveAllSegments) still carried four reporting branches from the old full-match walker:handler:<id>Server-Timing row (debugPerformance) and norango.handlerspan (tracing).cache()) had nohandler:<id>row.Static()) got ahandler:<id>row for a handler that did not run. The document request sent no row, asdocs/telemetry.mddocuments.loading()that returned aResponse(for exampleredirect()) printed the "returned a Response … NOT sent as an HTTP redirect" warning on navigations only, not on document loads.These are reporting and dev-warning gaps, not rendering defects.
walker-differential.test.ts(#1279) pinned them as five named exceptions. The review of #1279 judged them in rango-t162-c2.One more item (rango-t184-c4): on a
cache()or prerender HIT,withCacheLookupdropped the entries that an intercept's fill does not hold before it calledresolveLoadersOnly. The ppr header latch insideresolveLoadersOnlycould then read a chain without its route leaf. Nothing visible depends on this today.Before / after (consumer view)
Same page,
/slots(a layout withparallel({ "@side": … })), withcreateRouter({ debugPerformance: true, tracing }):Response(dev)What renders is unchanged. No change in your code. A dashboard that reads the per-segment rows of navigations now sees the slot and orphan rows.
End-to-end example: a consumer test that holds the document and the navigation to the same report:
Code change
src/router/segment-resolution/revalidation.tsresolveParallelSlotComponent: alwaystrack("handler:<parallel id>.<slot>"), always run the handler throughobserveSegmentHandler, which keeps the slot's tag scope and adds the span. AlwayswarnOnStreamedResponseon a streamed result. TherunInSegmentTagScopeimport goes.resolveOrphanLayout: always meter the orphan's handler.resolveRouteComponent: meter after the static lookup on every pass, so a static hit records nothing. Thefreshparameter goes.resolveLoadersOnly: skip an entry an intercept's fill does not hold (interceptFillSkips) in its entry loop, afterlatchPprHeaderScopeForEntries, asresolveAllSegmentsdoes.src/router/match-middleware/cache-lookup.ts: pass the whole chain below the boundary toresolveLoadersOnly, with no fill filter.docs/telemetry.md: thehandler:{id}row says slot and orphan handlers are recorded on every pass.CHANGELOG.md: an Unreleased "Fixed" entry.Net non-test source: -15 lines (revalidation.ts +27/-40, cache-lookup.ts +1/-3).
Each branch now follows the full match's behaviour, except the static hit. There the full match matches the documented contract (
docs/telemetry.md: a route's or slot's static hit records neither row nor span), so the partial pass moves to it. Every other branch moves the partial pass to what the document already reported.Test plan
walker-differential.test.ts: the five recorded exceptions are removed (slot handler metered,slot handler span,slot Response warning,orphan handler metered,static hit metered). Red before the fix: 12 failed / 36 passed of 48. After the slot change: 8 failed. After the orphan change: 2 failed. After the static-hit change: 48 passed.src/testing/__tests__/walker-observability-parity.rsc-test.tsx, throughserveShellRequest. It pins that a navigation reports a slot handler's Server-Timing row andrango.handlerspan, that an orphan layout and its slot have the same rows and spans on a navigation as on the document, and that a streamed slotResponseis warned about on the document as on a navigation (dev). Red on the base walker: 3 of 3 failed. With the fix: 3 of 3 passed.cache-hit-parallel-loaders.test.ts, new case: on an intercept's fill,resolveLoadersOnlyskips an unheld entry and still latchespprfrom the route leaf. Red on the base walker (L1D0.unheld-loaderresolved). With the fix: 9 of 9 passed.vitest run src/router src/cache: 187 files, 3057 passed, 5 skipped, 3 todo.playwright test semantic-matrix): 111 passed (dev 43, production 46, plus the smoke, build and warmup setup projects).test:unitandtest:unit:rsc(every package), oxlint--deny-warnings, oxfmt--check, build, and the CI lint checks (check:e2e-bucketing,check:e2e-parity --strict,check:e2e-console-baseline,check:suspense-contract,check:no-external-store, tools tests,check:doc-links,check:docs-api,check:skills,check:skills-catalog,check:route-gen,check:internals,check:bundle-guards): all pass.Practices
--deny-warningsand oxfmt--checkran on the whole repo and are clean. Thedocs/telemetry.mdtable was reflowed by the formatter.runInSegmentTagScopeunused in revalidation.ts (removed; instrument.ts, intercept-resolution.ts, cache-scope.ts and cache-tag.ts still use it).resolveRouteComponent'sfreshparameter and the slot function'sfreshlocal are removed.interceptFillSkipsstays imported in cache-lookup.ts, which still uses it for stored segments. The differential'sKindowner map stays, because it still names a difference by its segment id.observeSegmentHandler, which isrunInSegmentTagScopeplus the span; with tracing off it is the same direct call as before. The extratrack()per slot and orphan is a no-op withoutdebugPerformance. Nothing was left open.docs/telemetry.md(thehandler:{id}row) and the CHANGELOG are updated. execution-model.md already describes the streamed-Response warning for every streamed handler, and the observability skill makes no claim about slot rows. Neither needs a change.Review follow-ups
The review found two of the CHANGELOG's reporting claims pinned only as "the two passes agree". A change that broke both passes the same way would have passed every suite. One test commit (e245dd2) pins each claim on its own, and a merge brings the branch up to
reorg/stack.metered: ["M0L0", "M0L0L0"], both walks), so the route (M0L0L0R9) must be absent from both. Mutation,track()moved abovetryStaticHandlerinresolveRouteComponentso both passes meter the hit: 2 failed / 46 passed (both partitions). Without it: 48 passed.d2-handler-…orphanlayout<n>row on the document and on the navigation. walker-differential's orphan case lists its metered segments (the orphan layoutM0L0L0R7L0and its slot included). Mutation, the orphan'strack()removed from both passes: the parity test fails 1 / passes 2, and walker-differential fails 2 / passes 46. Without it: 3 of 3 and 48 of 48 pass.Static()route's row disappears from navigations; documents never had it.Notes
undefineddifference stays in the differential as an exception. It is not a defect (rango-t162-c2).