Compare two Next.js App Router production builds. crust leads with the decision, groups every affected route by cause, and traces regressions to the component, import and source line that introduced them.
1$ npx @moumensoliman/crust diff main feature
2
3BLOCK · attribution 94%
4
5/products/[slug] is no longer static
6 rendering static → partial · shell 100% → 45%
7 at lib/http.ts:3 in <ProductGallery>
8 → cache that read and the route can prerender again
9
10CAUSES
11 date-fns added +48.2 kB on /checkout, +8 more
12 <AnalyticsProvider> became client +82.0 kB on 3 routes
1crust 4a802397 · next 16.2 · turbopack
292% confidence · 14/14 routes classified
3
4Fix first
5
61. /products/[slug]
7 only 45% is in the static shell
8 ↳ uncached fetch at lib/http.ts:3
9 → add `use cache` above that read
10
11Route First load Shell Mode
12/ 143.2 kB 100% static
13/products/[slug] 168.4 kB 45% partial
1{
2 "defaultFirstLoadBytes": 250000,
3 "maxGrowth": 0.05,
4 "defaultMinShellRatio": 0.6,
5 "allowRegression": [
6 "/admin/[...slug]"
7 ]
8}
9
10// strict regressions need no config
11// ceilings stay yours to choose
No build error. No byte growth. A route still fell from static to partial.
A use cache directive was removed three call frames below the page. Conventional bundle checks passed because the JavaScript was identical. crust compared the emitted shells, followed the source chain, and named the edit that changed the merge decision.
Production artifacts prove what changed. Source explains why. History makes it comparable.
Rendering, caching, shell composition and client cost support the same verdict.
Missing evidence lowers confidence; it never becomes confident-looking blame.
Useful secondary surfaces. None are required for analyze, diff or CI.
The boundary keeps the core small enough to trust.
Initialize once, keep the snapshots in your repository, and let every later build answer what changed, why, and whether it should ship.