Skip to content

refactor(router): S9 follow-ups: plain walker names, differential coverage, dead RouterContext fields (rango-t162) - #1279

Merged
ivogt merged 3 commits into
reorg/stackfrom
reorg/t162-s9-followups
Oct 24, 2026
Merged

ivogt merged 3 commits into
reorg/stackfrom
reorg/t162-s9-followups

Conversation

@ivogt

@ivogt ivogt commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator

devcorner: rango-t162 · session rango-s375

Summary

Three S9 follow-ups, one commit each, in this order, no behaviour change:

  1. refactor(router): four dead RouterContext fields are removed (ticket item 3, first so the rename has no collision).
  2. refactor(router): the segment walker's functions lose the WithRevalidation suffix (item 1).
  3. test(router): the walker differential pins the FRESH branches it could not see: handler metering and spans for slots and orphans, the slot streamed-Response warning, the static-hit metering and the void-layout undefined (item 2).

Problem statement

S9 (#1244) made the revalidating walker the only segment walker, so the suffix on resolveAllSegmentsWithRevalidation and its siblings now reads as one of two variants that no longer exist. S9 also kept several full-match behaviours as explicit pass.client === FRESH branches, but walker-differential.test.ts compared segments only: deleting any of the reporting branches, or the void-layout branch, left the suite green. RouterContext still declared generator-based resolution fields from before the single walker that nothing sets or reads.

Nothing consumer-visible changes. RouterContext is type-exported from the @internal @rangojs/router/__internal subpath; its fields resolveAllSegmentsWithRevalidation and resolveLoadersOnlyWithRevalidation become resolveAllSegments and resolveLoadersOnly, and the optional fields resolveAllSegmentsGenerator, collectSegmentsFromGenerator, resolveAllSegmentsWithRevalidationGenerator and collectWithMarkers are removed. No repo app imports them.

Before / after (internal shape)

Before After
resolveAllSegmentsWithRevalidation resolveAllSegments
resolveSegmentWithRevalidation resolveSegment
resolveEntryHandlerWithRevalidation resolveEntryHandler
resolveOrphanLayoutWithRevalidation resolveOrphanLayout
resolveParallelSegmentsWithRevalidation resolveParallelSegments
resolveLoadersOnlyWithRevalidation resolveLoadersOnly
RouterContext.resolveAllSegmentsWithRevalidation / .resolveLoadersOnlyWithRevalidation RouterContext.resolveAllSegments / .resolveLoadersOnly
RouterContext.resolveAllSegmentsGenerator, .collectSegmentsFromGenerator, .resolveAllSegmentsWithRevalidationGenerator, .collectWithMarkers removed

No plain name was in use before the rename (git grep -w over the repo matched only docs/archive/cf-cache-store.md, which describes the deleted fresh walker under the same names; the archive keeps the names it was written against). None of the six functions is exported from a public entry point or @rangojs/router/testing.

The differential now records, per walk, what the walk reports beside its segments, and pins how the two passes differ there:

interface Recorded {
  metered: string[]; // handler:<id> perf metrics (track())
  spans: string[]; // rango.handler spans (observeSegmentHandler)
  warnedResponse: string[]; // warnOnStreamedResponse
  warnedOther: string[]; // every other console.warn
}
// per case, beside `expected`:
recorded: {
  "slot handler metered": ["metered by fresh only: M0L0L0L0.@side"],
  "slot handler span": ["span by fresh only: M0L0L0L0.@side"],
},

Code-change summary

  • src/router/router-context.ts: four dead optional fields removed (commit 1). resolveAllSegmentsWithRevalidationGenerator goes with them because its plain name would be resolveAllSegmentsGenerator, the other dead field.
  • Rename (commit 2, archived docs left as written): src/router/segment-resolution/revalidation.ts, the barrel src/router/segment-resolution.ts, segment-wrappers.ts, router.ts, router-context.ts, prerender-match.ts, match-middleware/{segment-resolution,cache-lookup,background-revalidation}.ts, comments in eight more source files (one an e2e test-app route file), 34 unit, RSC and bench test files, comments in 2 e2e suites, 11 docs and 2 skills. The segment-resolution middleware's flow diagram now shows one walker with a FRESH or a partial pass, and the three docstrings that said "with revalidation" describe what each function resolves.
  • src/router/segment-resolution/__tests__/walker-differential.test.ts (commit 3): each walk runs with the DSL store's metrics on, a span runner on the request context and console.warn captured. New named exceptions: void layout (segments) and, under Case.recorded, slot handler metered, slot handler span, slot Response warning, orphan handler metered, static hit metered. A route whose handler the partial pass never runs (under an intercept, a build-time loader error) is filed under the existing skipped under intercept / build-time loaders. Three fixture routes are appended after the existing ones, so no short code moves: a void layout, a Static() route and a slot returning a Response under loading(). The Static() route hits the real static store through a __STATIC_MANIFEST entry; only the Flight runtime, which does not run in the unit config, is stubbed (the shared pluginRscMock, decoding JSON). Every console.warn a walk prints is compared between the passes, the Response warning under its own name.

Test plan

  • walker-differential.test.ts: 48/48 (was 42; 3 new cases on both manifest partitions).

  • Red against each mutation (the FRESH branch forced to the partial pass's behaviour in revalidation.ts, reverted after), green after:

    Mutation New suite Suite before this PR
    slot handler not metered on FRESH 6 failed 42 passed
    slot handler not in its span on FRESH 6 failed 42 passed
    FRESH warns on a streamed slot Response 2 failed 42 passed
    orphan handler not metered on FRESH 6 failed 42 passed
    FRESH meters a static hit 2 failed 42 passed
    FRESH normalizes a void layout to null 2 failed 42 passed
  • Touched unit files after the rename: 28 files / 672 tests; RSC 2 files / 11 tests.

  • Gate on the final head (lint, format, typecheck, build, the CI lint check:* scripts including check:internals, check:docs-api, check:doc-links, check:skills, the skills catalog, route-gen, node --test tools, bundle guards): all pass. Unit: router 567 files / 9374 passed (5 skipped, 6 todo), mini 25, vite-rsc-demo 27, cloudflare-basic 90, shared-e2e 20. RSC: router 95 files / 1150 passed (3 expected fail), cloudflare-basic 39, mini 3, vite-rsc-demo 1.

  • e2e semantic-matrix (AGENTS.md segment-resolution row): dev 42/42, production 45/45. The dependency projects ran too: smoke 20/20, with smoke (production) › SSR renders home page flaky (its first attempt failed in the fixture on a pnpm deps-status error, e2e/fixture.ts:89, and it passed on retry).

  • No other e2e: the change renames, removes unused types and adds unit assertions.

Practices

  • Lint: oxlint --deny-warnings and oxfmt clean; oxfmt reflowed four files after the rename shortened lines (one docs table among them).
  • Dead code: the repo search for the two named RouterContext fields found two more never set or read (resolveAllSegmentsWithRevalidationGenerator, collectWithMarkers); all four removed. The rename leaves no WithRevalidation identifier outside docs/archive.
  • Tests: the differential gains the six assertions above, each shown red against its mutation; touched unit and RSC files, the full unit suites and the semantic matrix ran (see Test plan).
  • Code review: reviewed the diff against reorg/stack. Fixed: the differential stubbed console.warn but compared only the Response warning (every warning is compared now); the rename had rewritten an archived design doc into call shapes that never existed (reverted). Left: five findings that are the pinned FRESH/partial divergences themselves (slot metering and spans, the slot Response warning, orphan metering, static-hit metering, a void layout's null on a partial pass, which the client reads as "keep the cached copy"). Each is a behaviour change outside this ticket, recorded on rango-t162 as a finding for follow-up.
  • Docs: every doc and skill that named the old functions now names the new ones (docs/design/*, packages/rangojs-router/docs/**, skills/cache-guide, skills/ppr); feature-map.md follows the rename; feature-file-map.md names none of them. check-docs-api, check-doc-links, check-skills and the skills catalog pass.

Notes

  • git merge-tree against the open branches: reorg/t182-intercept-record-target merges clean. reorg/t176-late-next-holder conflicts in packages/rangojs-router/docs/internal/feature-map.md (the "Derived render passes" line), the same conflict it has with reorg/stack itself; this branch's one line there (the resolveOrphanLayout rename) merges clean. Neither branch adds a use of an old name.
  • The differential now covers c2 items 1-4 of the S9 builder finding. Items 5 (an evicted slot whose static lookup misses) and 6 (an orphan slot's loader order) stay unpinned; neither is in this ticket.

ivogt added 3 commits October 24, 2026 10:38
…elds (rango-t162)

RouterContext declared four optional fields that nothing sets or reads:
resolveAllSegmentsGenerator, collectSegmentsFromGenerator,
resolveAllSegmentsWithRevalidationGenerator and collectWithMarkers. They
are leftovers of a generator-based pipeline that predates the single
segment walker. A search of the whole repo (source, tests, docs, skills,
e2e apps, examples) finds them only in their own declarations.

resolveAllSegmentsWithRevalidationGenerator goes too: its plain name
after the walker rename would be resolveAllSegmentsGenerator.

Type-only change on an @internal type; no behaviour change.
…alker's functions (rango-t162)

The revalidating walker is the only segment walker since S9 deleted the
fresh one, so the suffix no longer names one of two variants. Plain names,
one mechanical rename:

  resolveAllSegmentsWithRevalidation      -> resolveAllSegments
  resolveSegmentWithRevalidation          -> resolveSegment
  resolveEntryHandlerWithRevalidation     -> resolveEntryHandler
  resolveOrphanLayoutWithRevalidation     -> resolveOrphanLayout
  resolveParallelSegmentsWithRevalidation -> resolveParallelSegments
  resolveLoadersOnlyWithRevalidation      -> resolveLoadersOnly

The RouterContext fields resolveAllSegmentsWithRevalidation and
resolveLoadersOnlyWithRevalidation (an @internal type) follow, with every
caller, test, bench, doc and skill; docs/archive keeps the names it was
written against. No plain name was in use; none of the names is exported
from a public entry point or @rangojs/router/testing. The
segment-resolution middleware's flow diagram now shows one walker with a
FRESH or a partial pass, and the three docstrings that said "with
revalidation" describe what each function resolves.

No behaviour change.
…the walker differential (rango-t162)

S9 kept several full-match behaviours as explicit FRESH branches of the
walker, but the differential compared segments only, so dropping any of
them passed. Each walk now records what it reports beside its segments:
the handler perf metrics (track()), the rango.handler spans
(observeSegmentHandler), the streamed-Response warnings
(warnOnStreamedResponse) and every other console.warn. Each case pins how
the FRESH and the partial pass differ there, under named exceptions:

- slot handler metered, slot handler span: a FRESH slot handler is
  metered and runs in its span; a partial pass does neither.
- slot Response warning: a partial pass warns on a streamed slot's
  Response; FRESH does not.
- orphan handler metered: FRESH meters an orphan layout's handler.
- static hit metered: a partial pass meters a route's static hit as a
  handler run; FRESH does not.
- void layout: FRESH keeps a void layout handler's undefined component;
  a partial pass normalizes it to null.

A route whose handler the partial pass never runs (under an intercept, a
build-time loader error) is metered and spanned by FRESH only, filed under
the existing exception. Three routes join the fixture, after the existing
ones so no short code moves: a void layout, a Static() route and a slot
that returns a Response under loading(). The Static() route hits the real
static store through a __STATIC_MANIFEST entry; only the Flight runtime,
which does not run in this config, is stubbed (the shared pluginRscMock,
decoding JSON).

Each branch, forced to the partial pass's behaviour, fails the suite (6, 6,
2, 6, 2 and 2 tests); the suite before this change passes all six.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant