Skip to content
Beta Multi-model support (OpenAI, Gemini, and more) launching soon.

perf-budget-enforcement

Installable Ships in performance-auditor — or copy the raw skill below.
Open Claude.ai

Paste into Claude Code, Claude.ai, the desktop app, or the browser extension — ⌘/Ctrl+V.

ExperimentalNo security review

Performance Budget Enforcement

Wire Lighthouse CI and bundle-size checks into the project's CI pipeline so that performance regressions are caught at the PR stage, not in production.

Usage

/perf-budget-enforcement <project-root> [--ci github|ado] [--format lhci|bundlesize|both] [--target-score <0-100>]

Examples:

  • /perf-budget-enforcement . --ci github --format both — full setup for GitHub Actions
  • /perf-budget-enforcement . --ci ado --format lhci — Azure DevOps YAML task
  • /perf-budget-enforcement . --format bundlesize — bundle-size budget only

Phase 1 — Inventory Existing Configuration

Read the project root to understand what already exists:

ls -la .github/workflows/ 2>/dev/null
ls -la .lighthouserc* lighthouserc.* 2>/dev/null
cat package.json | node -e "
  const p = JSON.parse(require('fs').readFileSync('/dev/stdin','utf8'));
  console.log('scripts:', JSON.stringify(p.scripts, null, 2));
  console.log('devDeps:', JSON.stringify(p.devDependencies, null, 2));
"

Detect:

  • CI system (GitHub Actions, Azure DevOps, or none)
  • Framework (Next.js, Vite, Webpack, .NET)
  • Existing performance tooling

Phase 2 — Generate Lighthouse CI Configuration

Create .lighthouserc.json (or lighthouserc.js for dynamic config):

{
  "ci": {
    "collect": {
      "numberOfRuns": 3,
      "startServerCommand": "npm start",
      "startServerReadyPattern": "ready on|listening on|started on",
      "startServerReadyTimeout": 30000,
      "url": [
        "http://localhost:3000",
        "http://localhost:3000/products",
        "http://localhost:3000/about"
      ],
      "settings": {
        "formFactor": "mobile",
        "throttlingMethod": "simulate",
        "screenEmulation": {
          "mobile": true,
          "width": 412,
          "height": 823,
          "deviceScaleFactor": 1.75,
          "disabled": false
        }
      }
    },
    "assert": {
      "preset": "lighthouse:no-pwa",
      "assertions": {
        "categories:performance": ["error", { "minScore": 0.9 }],
        "largest-contentful-paint": ["error", { "maxNumericValue": 2500 }],
        "interaction-to-next-paint": ["warn", { "maxNumericValue": 200 }],
        "cumulative-layout-shift": ["error", { "maxNumericValue": 0.1 }],
        "first-contentful-paint": ["warn", { "maxNumericValue": 1800 }],
        "total-blocking-time": ["warn", { "maxNumericValue": 300 }],
        "server-response-time": ["warn", { "maxNumericValue": 600 }],
        "render-blocking-resources": ["warn", { "maxLength": 0 }],
        "unused-javascript": ["warn", { "maxNumericValue": 0 }],
        "uses-optimized-images": ["warn", { "maxLength": 0 }],
        "uses-webp-images": ["warn", { "maxLength": 0 }]
      }
    },
    "upload": {
      "target": "temporary-public-storage"
    }
  }
}

Threshold guidance:

  • Use "error" for hard blockers (fail the PR): LCP, CLS, overall score
  • Use "warn" for soft warnings (notify but don't block): FCP, TBT, individual audits
  • temporary-public-storage: stores reports publicly for 7 days (no auth needed). For private repos, use LHCI server or "no-upload".

Phase 3 — GitHub Actions Workflow

Create .github/workflows/lighthouse-ci.yml:

name: Performance Audit (Lighthouse CI)

on:
  pull_request:
    branches: [main, develop]
  push:
    branches: [main]

jobs:
  lighthouse:
    name: Lighthouse CI
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Build
        run: npm run build
        env:
          NODE_ENV: production

      - name: Run Lighthouse CI
        run: npx lhci autorun
        env:
          LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}

      - name: Upload Lighthouse Reports
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: lighthouse-reports
          path: .lighthouseci/
          retention-days: 14

For Next.js projects, replace the Build step with:

      - name: Build Next.js
        run: npx next build
        env:
          NEXT_TELEMETRY_DISABLED: 1

      - name: Start Next.js server
        run: npx next start &
        env:
          PORT: 3000

Phase 4 — Azure DevOps Pipeline Task

For projects using Azure DevOps instead of GitHub Actions, create an equivalent stage in azure-pipelines.yml:

- stage: PerformanceAudit
  displayName: 'Performance Audit (Lighthouse CI)'
  dependsOn: Build
  condition: and(succeeded(), eq(variables['Build.Reason'], 'PullRequest'))
  jobs:
    - job: LighthouseCI
      displayName: 'Run Lighthouse CI'
      pool:
        vmImage: 'ubuntu-latest'
      steps:
        - task: NodeTool@0
          inputs:
            versionSpec: '20.x'

        - script: npm ci
          displayName: 'Install dependencies'

        - script: npm run build
          displayName: 'Build'
          env:
            NODE_ENV: production

        - script: npx lhci autorun
          displayName: 'Run Lighthouse CI'
          continueOnError: false

        - task: PublishPipelineArtifact@1
          displayName: 'Upload Lighthouse Reports'
          condition: always()
          inputs:
            targetPath: '$(System.DefaultWorkingDirectory)/.lighthouseci'
            artifact: 'lighthouse-reports'

Phase 5 — Bundle Size Budget (JavaScript)

Using bundlesize

Add to package.json:

{
  "bundlesize": [
    {
      "path": "./dist/static/js/*.js",
      "maxSize": "200 kB",
      "compression": "gzip"
    },
    {
      "path": "./dist/static/css/*.css",
      "maxSize": "50 kB",
      "compression": "gzip"
    }
  ]
}

Add to CI:

npx bundlesize

Using size-limit (preferred for Next.js / React projects)

Install: npm install --save-dev @size-limit/preset-app

Add to package.json:

{
  "size-limit": [
    {
      "path": ".next/static/chunks/pages/index-*.js",
      "limit": "75 KB"
    },
    {
      "path": ".next/static/chunks/framework-*.js",
      "limit": "45 KB"
    }
  ],
  "scripts": {
    "size": "size-limit",
    "analyze": "size-limit --why"
  }
}

Add npm run size to the CI workflow after build.


Adapt these to project requirements:

Budget Conservative (existing site) Ambitious (new build) Green-field best-practice
Performance score ≥ 75 ≥ 85 ≥ 90
LCP ≤ 3.5 s ≤ 2.5 s ≤ 1.8 s
INP ≤ 400 ms ≤ 200 ms ≤ 150 ms
CLS ≤ 0.15 ≤ 0.1 ≤ 0.05
JS bundle (gzip) ≤ 400 KB ≤ 200 KB ≤ 100 KB
Total page weight ≤ 3 MB ≤ 1.5 MB ≤ 800 KB

Phase 7 — Verification Checklist

After generating all files, verify:

  • .lighthouserc.json asserts at least LCP, CLS, and performance score
  • CI workflow runs on pull_request events (not just push)
  • numberOfRuns: 3 to reduce variance
  • Appropriate startServerCommand set for the project type
  • Bundle budget covers at least the main JS chunk
  • Azure DevOps pipeline has continueOnError: false so failures block the PR
  • LHCI_GITHUB_APP_TOKEN documented in the project's CI secrets setup guide

Reference files

  • reference.md — LHCI assertion syntax, assertion levels, and audit ID list
  • templates.md — ready-to-use config file templates for Next.js, Sitecore JSS, .NET MVC, Umbraco
Technical details
Plugin
performance-auditor
Author
Dakota Smith
Skill name
perf-budget-enforcement
Maturity
experimental
Security review
No security review
Owners
  • engineering
Argument hint
<project-root> [--ci github|ado] [--format lhci|bundlesize|both] [--target-score <0-100>]
Allowed tools
  • Read
  • Write
  • Glob
  • Grep
  • Bash(cat *)
  • Bash(ls *)
  • Bash(node *)
  • WebFetch(domain:web.dev)
  • WebFetch(domain:github.com)
Source
plugins/performance-auditor/skills/perf-budget-enforcement/SKILL.md

Edit this skill on GitHub — opens GitHub's web editor. No git knowledge needed.