This adds `bench/dev-validation/`, wired as `pnpm bench:dev-validation`,
which measures how much dev-mode Cache Components validation contends
for the dev server's event loop during rapid navigation, and how much
running it on a worker thread relieves that. It toggles
`experimental.devValidationWorker` (added in the previous commit) to A/B
the two configurations on the same build. Until the worker
implementation lands the flag is inert and the A/B shows no delta.
The fixture generates one route per family (`client`, `server`,
`sprite`), each nested several layout segments deep under a `(routes)`
route group. Validation renders a combined payload at every URL depth,
so a deeper route means more validation work per navigation, which
mirrors a realistically deep app rather than a single flat segment. The
runner clicks a family's `<Link>` repeatedly, since navigating to the
current route re-renders and re-validates it on every click. The routes
carry no `instant` config because dev validation applies to page
segments by default at the warning level. The three families isolate the
client prerender, the Flight re-encode plus owner-stack work, and the
Flight payload size, respectively.
The signal is browser-observed TTFB taken from Playwright's own network
timing, because it includes the time a request waits for the event loop
while validation monopolizes it. We deliberately do not use the CLI's
logged request durations: the dev server starts that clock inside the
request handler, after the loop has already yielded to the request, so
the queue wait is invisible to it.
The runner prints each configuration's absolute TTFB (p50/p95/max) side
by side rather than a ratio. The time the worker frees is the validation
render's CPU, which is bounded, route-dependent, and does no IO, so a
ratio would overstate a win that does not scale with total request time.
Because the clicks are back-to-back the numbers are a worst case —
navigations that land inside the validation window — and the `max` tail
is the honest headline: it is the main-thread stall the worker removes.