---
name: perf_budgets
description: Set and enforce performance budgets for a web app: load-time and bundle-size caps, CI gates that fail on regression, triage for overruns, and documenting which features are worth their weight. Triggers when asked to "set performance budgets", "stop the site getting slower", "add bundle size limits", "gate PRs on performance", or "handle a budget overrun".
metadata: { "includeInPrompt": true }
---

# Performance Budgets

## Purpose

Turn "the site got slower" into numbers with names, owners, and CI gates. A budget is a cap on something the user feels (load time, bundle weight, input lag) that blocks a merge when crossed. This skill produces the budget doc, the CI enforcement, and the triage playbook for when a budget breaks.

Budgets only work when set from measured reality, not wishes. Step 1 always comes before the caps.

## Workflow

### 1. Measure first

Before writing any cap, collect the current numbers:

- **Bundle weight**: build the app, record initial JS (gzipped), CSS (gzipped), fonts, images. `du` plus gzip is enough for a baseline.
- **Lab numbers**: one Lighthouse run on the production build against a local server. Record performance score, LCP, INP-equivalent (TBT), CLS.
- **Field numbers (if available)**: p75 LCP/INP/CLS from CrUX or your RUM. Lab passing does not mean users are fast.

See `references/cwv-thresholds.md` for the official "good" targets.

### 2. Set the caps

Rules:

- Set each budget at **current measured value plus a small headroom** (10-15%), not at an aspirational target. A budget nobody can pass is a budget nobody reads.
- Cover what the user feels: initial bundle, one metric for load (LCP), one for responsiveness (INP/TBT), one for stability (CLS). Four to eight budgets is enough; more than that and nobody maintains them.
- Assign an **owner** to each budget: the person who reviews regression alerts and argues for raises. No owner means the budget drifts.
- Write them into a repo doc (template in `assets/budgets-doc-template.md`). The doc is the contract: violations block, raises need a written reason (an ADR or a PR note), and the team reviews quarterly.

### 3. Wire the CI gates

Two layers, each catching what the other misses:

- **Build-time bundle check**: fails when a build ships more bytes than allowed. Run `bin/budget_check.py` (checked in with this skill) or a size-limit/bundlesize setup against the production build. See `references/ci-gates.md` for config examples.
- **Lighthouse CI assertions**: fails the PR when a page gets slower. Use `lhci autorun` with assertions on the score and the timing metrics, or a `budget.json` resource-size file. Repeat noisy runs and assert on the median so normal variance does not make the gate flaky.

Add both as required CI checks, not optional ones. Optional gates get skipped the week they would have mattered.

### 4. Triage an overrun

When a gate fails or a field alert fires:

1. **Re-run before believing it.** Lab numbers are noisy; confirm the regression reproduces. For field alerts, check whether the CrUX rolling window agrees or if it is one bad day.
2. **Find what changed.** `git log` on the build output diff, the Lighthouse trace, or the RUM breakdown. Name the commit or the dependency that moved the number.
3. **Pick a fix path**: remove bytes (tree-shake, code-split, lazy-load), remove work (debounce, virtualize, move to a worker), or remove the feature if it is not worth its weight.
4. **If nothing can come out**, raise the budget with a written reason and a new owner review date. A budget raised quietly is a budget deleted.
5. Record the outcome: what regressed, what fixed it, what was tried and reverted. Keep a short ledger so a discarded idea stays discarded.

### 5. Review quarterly

Budgets tighten when the app gets faster, never loosen without a reason on record. Read the trend: if every metric sits well under budget for a quarter, lower the caps.

## Output Contract

Given a web app (or a description of one), deliver:

1. **A budget doc** in the repo (from the assets template): metrics, caps, owners, violation policy, raise process, review date.
2. **CI enforcement**: a bundle-size gate config plus a Lighthouse CI config, added as required checks, both green on the current code.
3. **A baseline snapshot**: the measured numbers the caps were set from, committed next to the config.
4. Optionally, a triage log for any overrun already in flight.

Every number in the doc must trace to a real measurement or a cited primary source (web.dev, CrUX, the repo's own build output). No invented targets.

## Operating Rules

1. Measure before capping. A budget set without a baseline is a guess with a CI gate attached.
2. Budgets block. If the gate can be ignored, it will be, exactly when it matters.
3. Gate on what the user feels (load, input response, layout stability), not on every metric available.
4. Repeat noisy lab measurements and assert on a median or trend; never let one flaky run block a team.
5. Raising a budget is a documented decision with a reason and a review date, never a drive-by commit.
6. Field data is the verdict, lab data is the gate. When they disagree, trust the field and fix the gate.
7. Never claim a performance improvement you did not measure. Before-and-after numbers or it did not happen.
8. Keep the style out of the budgets: no em dashes, no filler phrasing, plain direct language in the doc.
