Skip to main content
The PR comment is the point. Every comment is read by every reviewer, in context, at the moment it matters.
npx @moumensoliman/crust init generates the configuration below for the detected provider, filled in with your package manager, app directory, and default branch - and pinned to the crust version that wrote it. The rest of this page is what it writes, and why.

The action

.github/workflows/crust.yml
The action lives on master. baseline is a branch in your repository and is unrelated to it - set it to whatever your pull requests target.
It fetches snapshot history, runs the check, records and publishes the new snapshot, and posts or updates one comment on pull requests. Run the workflow on the base branch as well as pull requests so future branches have a baseline at their merge base. The Next production build happens before the action because crust reads its output; the action never changes how the application is bundled. Even when the verdict fails, the action publishes the snapshot and posts the comment before returning a failed check.

What fails without configuration

Once a comparable baseline exists, no budget file is required for strict regressions:
  • a route became less static
  • a new uncached read appeared
  • a route stopped emitting a static shell
Those are statements about direction against the project’s own previous build, not guessed thresholds. crust ci exits non-zero when one is detected.
New routes are not regressions. A transition involving unknown is reported but not assigned a direction. Different bundlers, Next.js majors, or snapshot schemas are not compared.

Add project-specific ceilings

Create .perf/budgets.json at the workspace root for limits crust cannot choose for you:
.perf/budgets.json
allowRegression exempts only automatic rendering-mode, cache, and shell-disappearance checks for the named route. Explicit byte, growth, and shell-ratio ceilings still apply.
See the regression model for the certainty rules and the budgets reference for every field.

The comment

The comment carries a hidden <!-- crust-report --> marker so the action updates the existing one instead of stacking a new comment on every push. It leads with the decision and attribution coverage. Improvements get their own visible section, and packages, client boundaries, barrels, and shared call sites are grouped once with their route blast radius. Additional neutral route movement stays behind a disclosure. If the builds are incomparable, no route deltas are printed.

Measure whether authors agree

Every blocking breach is appended to .perf/findings.jsonl before the action publishes history. Reviewers can mark the occurrence locally after fetching history:
rate reports disputed / (agreed + disputed). Open findings are unfinished measurement and do not enter the denominator; with no reviewed findings the rate remains unmeasured rather than appearing as 0%. The pre-ownership-routing target is fewer than 1 in 10 reviewed findings disputed.

The import chain, one click away

Cause and Introduced by answer what broke and where. The chain behind them answers how it got into this route, which is a different question, so it is folded rather than inlined - on a deep import path it would push the verdict off the screen:
The chain is the one the analyzer stored, matched to the finding by call site. When no stored chain matches, none is shown - an import path under the wrong finding sends a reviewer somewhere real that is not where the problem is. A chain that is not verified states its evidence level and the segment crust could not follow.

Configuration is reported apart from code

Build configuration moves for reasons no pull request caused, so it is never mixed into the route deltas:
A change that stops the comparison entirely gets the warning block instead, and each reason says what it explains - so “no deltas were reported” never reads as “nothing happened”. A route that regressed because it declared dynamic, revalidate, runtime, or fetchCache still fails, with the declaration named beside the mode drop:
crust ci exits non-zero on a breach.

Snapshot history

A fresh CI checkout has no .perf/ directory, so the baseline lives on an orphan branch:
An orphan branch shares no history with your code, so it can grow and be truncated freely without polluting git log or complicating rebases.
Fork pull requests get the comment but skip the push. A fork’s GITHUB_TOKEN is read-only on the base repository, so history push reports and exits cleanly rather than failing the check. Diffs still work from whatever snapshots the branch already holds.

Run the check without the action

This is useful on other CI providers. --comment writes Markdown only; posting it is the CI provider’s responsibility.

Retention

Full fidelity for the newest 50 builds, then one snapshot per commit, then module detail dropped after 90 days. Route totals and shell ratios are kept forever - they are tiny, and they are what anyone looks at a year later.