Archilyzer · Source

archilyzer

Archilyzer
git clone https://archilyzer.pages.dev/source/archilyzer.git
Log | Files | Refs | README | LICENSE

commit 7c12455751a62e4ca5d1e8e62082060c87634b01
parent 2db970950e401e0605ecd2e4ae139a248a60acaf
Author: I Mean I'm Just Saying <imeanimjustsaying@kiwifarms.st>
Date:   Tue,  4 Aug 2026 22:38:47 -0400

Record the cacheComponents spike: measured, and the answer is no

The spike's question was whether uncached node:fs I/O gets captured into the
prerendered shell under cacheComponents. The build never gets far enough to
ask: `next build` fails with 49 errors, one per file, because "Route segment
config dynamic is not compatible with nextConfig.cacheComponents" — and all 49
files in editor/app declare force-dynamic. That is every page and every API
route; force-dynamic IS this app's rendering model.

So adoption is not the "~48 x await connection()" the plan estimated. It is
deleting the declaration that currently guarantees dynamic rendering, and THEN
proving per-route that the fs reads are excluded from the prerender — the
original question asked with the safety net removed. For a gain we already
have: sibling navigation is instant from the router cache today.

Recommendation in plans/cache-components-spike.md: do not adopt. Branch
deleted, next.config.ts reverted, nothing merged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Diffstat:
Aplans/cache-components-spike.md | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 55 insertions(+), 0 deletions(-)

diff --git a/plans/cache-components-spike.md b/plans/cache-components-spike.md @@ -0,0 +1,55 @@ +# Stage 6 spike: `cacheComponents` — measured, and the answer is no + +Ran 2026-08-04 on branch `spike/cache-components` (deleted; nothing merged). + +## The question the spike was meant to answer + +Next instruments `fetch`, `cookies()`, `headers()`, `params`, `searchParams` — +**not `node:fs`**. Every loader in this editor is a raw `readFile`/`readdir`. Under +`cacheComponents`, does uncached fs I/O outside a Suspense boundary get captured into +the prerendered static shell at build time (→ a frozen admin UI), or is it correctly +excluded? + +## The answer: the build never gets far enough to ask + +``` +Route segment config "dynamic" is not compatible with `nextConfig.cacheComponents`. +Please remove it. +``` + +**49 errors, one per file — exactly the 49 files in `editor/app/` that declare +`export const dynamic = "force-dynamic"`.** `next build` fails outright. + +That is every page and every API route in the editor. `force-dynamic` is not incidental +here; it *is* the app's rendering model. An admin UI over a live corpus with running +job runners has nothing meaningful to prerender. + +## What adoption would actually cost + +Not the "~48 × `await connection()`" the plan estimated. It is: + +1. Delete `force-dynamic` from all 49 files — removing the one declaration that + currently *guarantees* dynamic rendering; then +2. Prove, per route, that its uncached `node:fs` reads are excluded from the prerender — + i.e. the original question, now asked with the safety net removed rather than in + place. Getting it wrong ships a frozen admin page that looks fine in dev. +3. Plus the costs already known: `use cache` has no correct invalidation key here + (writes happen in `common/` job runners that cannot import `next/cache`), and + `Activity`-based navigation keeps routes mounted and re-runs effects, needing + re-verification of the ~20 `router.refresh()`-on-mount components and much of the + 407-spec suite. + +## What it would buy: nothing we don't already have + +Sibling navigation is already instant — measured: with the destination prefetched, the +click commits from the router cache with no pending state at all. The one thing +`cacheComponents` would add is `unstable_instant`, which *guarantees* a loading fallback +when the destination is NOT prefetched (see `editor/e2e/navigation.spec.ts` for why +`loading.tsx` alone does not). That is a narrow gain for a migration that starts by +deleting the app's rendering model. + +**Recommendation: do not adopt.** Revisit only if the editor grows nested layouts with +their own data (there is exactly one layout today and zero nesting), which is where +`unstable_instant`'s cross-layout validation earns its keep. + +Taken from those docs regardless, and already shipped: `loading.tsx` and `staleTimes`.