performance-testing
<!-- Loaded by performance-optimization-engineer when task involves Lighthouse CI, performance budgets, regression testing, synthetic monitoring, or CI/CD performance gates -->
$ npx -y skills add notque/vexjoy-agent --agent claude-codeHow it fires
How this agent gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
Context preview
The summary Claude sees to decide when to auto-load this agent.
<!-- Loaded by performance-optimization-engineer when task involves Lighthouse CI, performance budgets, regression testing, synthetic monitoring, or CI/CD performance gates -->
Agent definition
performance-testing.mdPerformance Testing Reference
<!-- Loaded by performance-optimization-engineer when task involves Lighthouse CI, performance budgets, regression testing, synthetic monitoring, or CI/CD performance gates -->
> **Scope**: Automated performance testing in CI/CD, Lighthouse CI setup, performance budgets, and regression detection. Does NOT cover RUM/real-user monitoring (see `metrics-and-monitoring.md`). > **Version range**: Lighthouse CI 0.12+, @lhci/cli 0.12+ > **Generated**: 2026-04-09
---
Overview
Synthetic performance testing in CI/CD catches regressions before they reach production. The most common failure mode is running Lighthouse manually during development and never catching performance regressions in CI — so bundle size grows 20% over 6 months with no alerts. Performance budgets with automated gates prevent this category of problem entirely.
---
Pattern Table
| Tool | Purpose | When to Use | |------|---------|-------------| | `@lhci/cli` | Lighthouse CI — runs Lighthouse in CI, stores results, enforces assertions | Primary CI performance gate | | `bundlesize` / `size-limit` | Bundle size gates — fail CI if JS bundle exceeds limit | Add alongside @lhci when bundle size is the primary concern | | `playwright` + tracing | Real browser performance measurement with network control | Integration tests that need timing precision | | `web-vitals` + Vitest | Unit-level performance assertions | Catching regressions in specific components |
---
Correct Patterns
Lighthouse CI Configuration with Assertions
`.lighthouserc.js` at repo root is the standard config location.
// .lighthouserc.js
module.exports = {
ci: {
collect: {
// Run against the built app served locally
url: ['http://localhost:3000/', 'http://localhost:3000/products'],
numberOfRuns: 3, // Average over 3 runs to reduce variance
startServerCommand: 'npm run start', // Production build
startServerReadyPattern: 'ready on',
},
assert: {
// Fail CI if these thresholds aren't met
assertions: {
'categories:performance': ['error', { minScore: 0.9 }], // 90+ score
'categories:accessibility': ['warn', { minScore: 0.9 }],
'first-contentful-paint': ['error', { maxNumericValue: 2000 }], // 2s
'largest-contentful-paint': ['error', { maxNumericValue: 2500 }], // 2.5s
'total-blocking-time': ['error', { maxNumericValue: 300 }],
'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
// Bundle size gates via Lighthouse
'total-byte-weight': ['error', { maxNumericValue: 1_600_000 }], // 1.6MB
'uses-optimized-images': ['warn', {}],
'unused-javascript': ['warn', { maxLength: 2 }], // Max 2 unused JS chunks
},
},
upload: {
target: 'temporary-public-storage', // Free LHCI storage for 30 days
// Or: target: 'lhci', serverBaseUrl: 'https://lhci.yourcompany.com'
},
},
}**Why**: `numberOfRuns: 3` is essential — single-run Lighthouse scores have 10-15 point variance on CI machines. Assertions use `'error'` to fail CI (not just warn). The upload step stores historical data so you can see score trends over time.
---
GitHub Actions Workflow for Lighthouse CI
# .github/workflows/lighthouse.yml
name: Lighthouse CI
on: [push, pull_request]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run build
- name: Run Lighthouse CI
run: |
npm install -g @lhci/cli@0.14
lhci autorun
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}**Why**: Running `lhci autorun` reads `.lighthouserc.js` automatically. The `LHCI_GITHUB_APP_TOKEN` enables PR status checks and comments. Without it, Lighthouse results don't appear inline in PRs.
---
size-limit for Bundle Size Gates
// package.json
{
"size-limit": [
{
"path": ".next/static/chunks/main-*.js",
"limit": "80 kB",
"gzip": true
},
{
"path": ".next/static/chunks/pages/**/*.js",
"limit": "50 kB",
"gzip": true,
"ignore": ["node_modules"]
}
],
"scripts": {
"size": "size-limit",
"analyze": "ANALYZE=true next build"
}
}# Add to CI workflow
- name: Check bundle size
run: npx size-limit --json > size-report.json
- name: Report size
uses: andresz1/size-limit-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}**Why**: `size-limit` measures gzipped output, which is what the browser actually downloads. Failing CI on bundle size regressions prevents the incremental growth pattern where no single PR is "bad" but 6 months of PRs add 150KB.
---
Pattern Catalog
Average Multiple Lighthouse Runs
**Detection**:
grep -rn "numberOfRuns\|number-of-runs" .lighthouserc.* .lhci* 2>/dev/null
# If no output: numberOfRuns is not configured (defaults to 1)
**Signal**:
// .lighthouserc.js
module.exports = {
ci: {
collect: {
url: ['http://localhost:3000'],
// numberOfRuns missing — defaults to 1
},
},
}**Why this matters**: A single Lighthouse run has 10-15 point score variance on CI machines (shared CPU, GC pauses, network jitter). A score of 85 one run, 72 the next — both from the same code. Single-run CI gates either false-positive (blocking good PRs) or miss real regressions.
**Preferred action**:
collect: {
url: ['http://localhost:3000'],
numberOfRuns: 3, // Minimum for stable averages
},---
Use error Level for Core Performance Assertions
**Detection**:
grep -rn "largest-contentful-paint\|first-contentful-paint\|total-blocking-time" .lighthouserc.*
# Check if it uses 'warn' instead of 'error' for core metrics
**Signal**:
assertions:
Read more
Performance Testing Reference
<!-- Loaded by performance-optimization-engineer when task involves Lighthouse CI, performance budgets, regression testing, synthetic monitoring, or CI/CD performance gates -->
> **Scope**: Automated performance testing in CI/CD, Lighthouse CI setup, performance budgets, and regression detection. Does NOT cover RUM/real-user monitoring (see `metrics-and-monitoring.md`). > **Version range**: Lighthouse CI 0.12+, @lhci/cli 0.12+ > **Generated**: 2026-04-09
---
Overview
Synthetic performance testing in CI/CD catches regressions before they reach production. The most common failure mode is running Lighthouse manually during development and never catching performance regressions in CI — so bundle size grows 20% over 6 months with no alerts. Performance budgets with automated gates prevent this category of problem entirely.
---
Pattern Table
| Tool | Purpose | When to Use | |------|---------|-------------| | `@lhci/cli` | Lighthouse CI — runs Lighthouse in CI, stores results, enforces assertions | Primary CI performance gate | | `bundlesize` / `size-limit` | Bundle size gates — fail CI if JS bundle exceeds limit | Add alongside @lhci when bundle size is the primary concern | | `playwright` + tracing | Real browser performance measurement with network control | Integration tests that need timing precision | | `web-vitals` + Vitest | Unit-level performance assertions | Catching regressions in specific components |
---
Correct Patterns
Lighthouse CI Configuration with Assertions
`.lighthouserc.js` at repo root is the standard config location.
// .lighthouserc.js
module.exports = {
ci: {
collect: {
// Run against the built app served locally
url: ['http://localhost:3000/', 'http://localhost:3000/products'],
numberOfRuns: 3, // Average over 3 runs to reduce variance
startServerCommand: 'npm run start', // Production build
startServerReadyPattern: 'ready on',
},
assert: {
// Fail CI if these thresholds aren't met
assertions: {
'categories:performance': ['error', { minScore: 0.9 }], // 90+ score
'categories:accessibility': ['warn', { minScore: 0.9 }],
'first-contentful-paint': ['error', { maxNumericValue: 2000 }], // 2s
'largest-contentful-paint': ['error', { maxNumericValue: 2500 }], // 2.5s
'total-blocking-time': ['error', { maxNumericValue: 300 }],
'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
// Bundle size gates via Lighthouse
'total-byte-weight': ['error', { maxNumericValue: 1_600_000 }], // 1.6MB
'uses-optimized-images': ['warn', {}],
'unused-javascript': ['warn', { maxLength: 2 }], // Max 2 unused JS chunks
},
},
upload: {
target: 'temporary-public-storage', // Free LHCI storage for 30 days
// Or: target: 'lhci', serverBaseUrl: 'https://lhci.yourcompany.com'
},
},
}**Why**: `numberOfRuns: 3` is essential — single-run Lighthouse scores have 10-15 point variance on CI machines. Assertions use `'error'` to fail CI (not just warn). The upload step stores historical data so you can see score trends over time.
---
GitHub Actions Workflow for Lighthouse CI
# .github/workflows/lighthouse.yml
name: Lighthouse CI
on: [push, pull_request]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run build
- name: Run Lighthouse CI
run: |
npm install -g @lhci/cli@0.14
lhci autorun
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}**Why**: Running `lhci autorun` reads `.lighthouserc.js` automatically. The `LHCI_GITHUB_APP_TOKEN` enables PR status checks and comments. Without it, Lighthouse results don't appear inline in PRs.
---
size-limit for Bundle Size Gates
// package.json
{
"size-limit": [
{
"path": ".next/static/chunks/main-*.js",
"limit": "80 kB",
"gzip": true
},
{
"path": ".next/static/chunks/pages/**/*.js",
"limit": "50 kB",
"gzip": true,
"ignore": ["node_modules"]
}
],
"scripts": {
"size": "size-limit",
"analyze": "ANALYZE=true next build"
}
}# Add to CI workflow
- name: Check bundle size
run: npx size-limit --json > size-report.json
- name: Report size
uses: andresz1/size-limit-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}**Why**: `size-limit` measures gzipped output, which is what the browser actually downloads. Failing CI on bundle size regressions prevents the incremental growth pattern where no single PR is "bad" but 6 months of PRs add 150KB.
---
Pattern Catalog
Average Multiple Lighthouse Runs
**Detection**:
grep -rn "numberOfRuns\|number-of-runs" .lighthouserc.* .lhci* 2>/dev/null # If no output: numberOfRuns is not configured (defaults to 1)
**Signal**:
// .lighthouserc.js
module.exports = {
ci: {
collect: {
url: ['http://localhost:3000'],
// numberOfRuns missing — defaults to 1
},
},
}**Why this matters**: A single Lighthouse run has 10-15 point score variance on CI machines (shared CPU, GC pauses, network jitter). A score of 85 one run, 72 the next — both from the same code. Single-run CI gates either false-positive (blocking good PRs) or miss real regressions.
**Preferred action**:
collect: {
url: ['http://localhost:3000'],
numberOfRuns: 3, // Minimum for stable averages
},---
Use error Level for Core Performance Assertions
**Detection**:
grep -rn "largest-contentful-paint\|first-contentful-paint\|total-blocking-time" .lighthouserc.* # Check if it uses 'warn' instead of 'error' for core metrics
**Signal**:
assertions:
Essays and writing behind this toolkit live at vexjoy.com. AI agents skip steps. "Looks correct" replaces running tests. "Trivial change" replaces verification.
Repo: notque/vexjoy-agent
Other agents on vexjoy-agent.
- ansible-automation-engineer
Ansible automation: playbooks, roles, collections, Molecule testing, Vault security.
Open agent - modules
**Scope**: Module selection patterns, builtin vs command/shell decisions, collection modules, and version-specific module changes **Version range**: ansible-core 2.14+ / Ansible Collections (community.general 7.0+) **Generated**: 2026-04-04 — verify against current Ansible
Open agent - testing
**Scope**: Molecule test scenarios, ansible-lint rules, idempotency validation, and check-mode patterns **Version range**: Molecule 6.0+ / ansible-lint 6.0+ / ansible-core 2.14+ **Generated**: 2026-04-04 — verify against current Molecule and ansible-lint documentation
Open agent - base-instructions
Universal operational rules injected by /do at agent dispatch. Domain-specific rules live in each agent's .md file.
Open agent - communication-patterns
**Scope**: Failure modes in agent output style — over-reporting, self-congratulation, verbose narration, and hedging. Covers what to detect and how to fix each. **Version range**: all versions **Generated**: 2026-05-11
Open agent - combat-effects-upgrade
Zero-dependency combat visual upgrades: CSS particle replacement, Framer Motion combat juice, CSS 3D card transforms.
Open agent

