What crust answers
For a single build:- Is this route static, partially static, ISR, dynamic, or a route handler?
- Which dynamic API, uncached read, or route configuration produced that mode?
- How much of the rendered route is in the static shell?
- Which first-party modules and packages make up its first-load JavaScript?
- Which three findings are most worth investigating first?
- Did a route become less static?
- Did a cached read become uncached?
- Did a component leave the static shell?
- Did first-load JavaScript grow, and which module caused it?
- Is the regression certain enough to fail CI?
The failure it exists to catch
Under Cache Components, removing a cache directive deep in a utility silently shrinks the static shell. No build error and no bundle growth:Under the older
experimental_ppr model a dynamic API outside a Suspense boundary threw a build
error, so you found out immediately. Cache Components inverts the default: absence of caching is
what postpones, and it fails quietly.What it does
Analyze a build
Prioritized findings, route modes, first-load JS, shell composition, and source-level reasons.
Understand regressions
What fails automatically, what needs a budget, and when crust refuses to compare.
Shell engine
Predicted versus actual static shell, and the call site that broke it.
Add CI
Zero-config regression checks, optional ceilings, and a PR comment that updates in place.
What is enforced
With a comparable baseline, crust fails CI without a budget file when:- a rendering mode moves down the staticness scale
- an uncached read appears where the baseline had none
- a route that emitted a static shell stops emitting one
.perf/budgets.json.
An added route is not a regression,
unknown is never assigned a direction, and builds made with
different bundlers, Next majors, or snapshot schemas are not compared. Absolute ceilings can still
apply because they describe the current build rather than a delta.How it differs
Route-level regression blame and shell composition, together, is the gap.