> ## Documentation Index
> Fetch the complete documentation index at: https://docs.crust.moumen.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# CI and pull requests

> Automatic regression checks, optional ceilings, an updating PR comment, and durable history.

The PR comment is the point. Every comment is read by every reviewer, in context, at the moment it
matters.

## The action

```yaml .github/workflows/crust.yml theme={null}
name: crust

on: pull_request

permissions:
  contents: write        # push snapshots to perf-history
  pull-requests: write   # post the comment

jobs:
  crust:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 22 }
      - run: npm ci
      - run: npx next build
      - uses: moumen-soliman/crust/action@main
        with:
          working-directory: .
          baseline: main
```

It fetches snapshot history, runs the check, posts or updates one comment, and publishes the new
snapshot. The Next production build happens before the action because crust reads its output; the
action never changes how the application is bundled.

## 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.

<Note>
  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.
</Note>

## Add project-specific ceilings

Create `.perf/budgets.json` at the workspace root for limits crust cannot choose for you:

```json .perf/budgets.json theme={null}
{
  "defaultFirstLoadBytes": 250000,
  "firstLoadBytes": {
    "/products/[slug]": 180000
  },
  "maxGrowth": 0.05,
  "defaultMinShellRatio": 0.6,
  "minShellRatio": {
    "/checkout": 0.8
  },
  "allowRegression": ["/admin/[...slug]"]
}
```

<Note>
  `allowRegression` exempts only automatic rendering-mode, cache, and shell-disappearance checks for
  the named route. Explicit byte, growth, and shell-ratio ceilings still apply.
</Note>

See the [regression model](/docs/concepts/regressions) for the certainty rules and the
[budgets reference](/docs/reference/budgets) for every field.

## The comment

```text theme={null}
### crust: `/products/[slug]` is no longer static

**`/products/[slug]`**

- rendering: **static → partial**
- static shell: **100% → 45%**
- Cause: `uncached fetch at lib/http.ts:3`
- Introduced by: `<ProductGallery>`

**Failing check**

- `/products/[slug]` - rendering mode dropped static -> partial
```

The comment carries a hidden `<!-- crust-report -->` marker so the action updates the existing one
instead of stacking a new comment on every push. Regressions receive prose and source blame;
improvements and neutral changes stay behind a disclosure. If the builds are incomparable, no
route deltas are printed.

`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:

```bash theme={null}
npx @moumensoliman/crust history fetch   # pull snapshots into .perf/
npx @moumensoliman/crust history push    # publish this build's snapshot
```

An orphan branch shares no history with your code, so it can grow and be truncated freely without
polluting `git log` or complicating rebases.

<Warning>
  **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.
</Warning>

## Run the check without the action

```bash theme={null}
npx @moumensoliman/crust history fetch
npx @moumensoliman/crust ci main --comment crust-comment.md
npx @moumensoliman/crust history push
```

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

## Retention

```bash theme={null}
npx @moumensoliman/crust prune
```

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.
