Install any skill in seconds. Free to start, no credit card required.
Get Started Free →k6 script templates, load profiles, response time thresholds, SLO validation, and performance testing strategies.
.claude/skills/load-testing-patterns/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-10 | ✗→✓ | ▲ Improved | — | — |
| case-17 | ✗→✓ | ▲ Improved | — | — |
| case-14 | ✗→✓ | ▲ Improved | — | — |
| case-01 | ✗→✓ | ▲ Improved | — | — |
| case-20 | ✗→✓ | ▲ Improved | — | — |
Performance validation with k6 for SLO-driven load testing.
javascriptimport http from 'k6/http' import { check, sleep } from 'k6' import { Rate, Trend } from 'k6/metrics' // Custom metrics const errorRate = new Rate('errors') const loginDuration = new Trend('login_duration') // Thresholds define pass/fail criteria export const options = { thresholds: { http_req_duration: ['p(95)<500', 'p(99)<1000'], // 95th < 500ms, 99th < 1s http_req_failed: ['rate<0.01'], // Error rate < 1% errors: ['rate<0.05'], // Custom error rate < 5% }, scenarios: { smoke: { executor: 'constant-vus', vus: 5, duration: '1m', } } } export default function () { const res = http.get('https://api.example.com/health') check(res, { 'status is 200': (r) => r.status === 200, 'response time < 500ms': (r) => r.timings.duration < 500, 'body has status': (r) => JSON.parse(r.body).status === 'ok', }) || errorRate.add(1) sleep(1) }
javascriptexport const options = { scenarios: { // 1. Smoke Test: verify system works under minimal load smoke: { executor: 'constant-vus', vus: 3, duration: '1m', }, // 2. Load Test: normal expected traffic load: { executor: 'ramping-vus', startVUs: 0, stages: [ { duration: '2m', target: 50 }, // Ramp up { duration: '5m', target: 50 }, // Steady state { duration: '2m', target: 0 }, // Ramp down ], }, // 3. Stress Test: find breaking point stress: { executor: 'ramping-vus', startVUs: 0, stages: [ { duration: '2m', target: 100 }, { duration: '5m', target: 100 }, { duration: '2m', target: 200 }, // Beyond normal { duration: '5m', target: 200 }, { duration: '2m', target: 300 }, // Breaking point? { duration: '5m', target: 300 }, { duration: '5m', target: 0 }, ], }, // 4. Spike Test: sudden traffic surge spike: { executor: 'ramping-vus', startVUs: 0, stages: [ { duration: '30s', target: 10 }, { duration: '10s', target: 500 }, // Instant spike { duration: '1m', target: 500 }, { duration: '10s', target: 10 }, // Instant drop { duration: '1m', target: 10 }, ], }, // 5. Soak Test: sustained load over time (memory leaks, connection exhaustion) soak: { executor: 'constant-vus', vus: 50, duration: '2h', }, } }
javascriptimport http from 'k6/http' import { check, group, sleep } from 'k6' const BASE_URL = __ENV.BASE_URL || 'https://api.example.com' export default function () { // Simulate real user journey, not isolated endpoints let token group('01_login', () => { const res = http.post(`${BASE_URL}/auth/login`, JSON.stringify({ email: `user${__VU}@test.com`, password: 'testpass123' }), { headers: { 'Content-Type': 'application/json' } }) check(res, { 'login successful': (r) => r.status === 200 }) token = JSON.parse(res.body).token }) sleep(Math.random() * 3 + 1) // Think time: 1-4 seconds group('02_browse_products', () => { const headers = { Authorization: `Bearer ${token}` } const listRes = http.get(`${BASE_URL}/products?page=1&limit=20`, { headers }) check(listRes, { 'products loaded': (r) => r.status === 200 }) const products = JSON.parse(listRes.body).data if (products.length > 0) { const product = products[Math.floor(Math.random() * products.length)] const detailRes = http.get(`${BASE_URL}/products/${product.id}`, { headers }) check(detailRes, { 'product detail loaded': (r) => r.status === 200 }) } }) sleep(Math.random() * 2 + 1) group('03_add_to_cart', () => { const res = http.post(`${BASE_URL}/cart/items`, JSON.stringify({ productId: 'prod_001', quantity: 1 }), { headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` } }) check(res, { 'item added to cart': (r) => r.status === 201 }) }) }
javascriptexport const options = { thresholds: { // SLO: Availability > 99.9% http_req_failed: ['rate<0.001'], // SLO: p50 < 100ms, p95 < 500ms, p99 < 1000ms http_req_duration: [ 'p(50)<100', 'p(95)<500', 'p(99)<1000', ], // SLO per endpoint using tags 'http_req_duration{name:login}': ['p(95)<800'], 'http_req_duration{name:get_products}': ['p(95)<200'], 'http_req_duration{name:checkout}': ['p(95)<2000'], // SLO: Throughput > 1000 RPS http_reqs: ['rate>1000'], } } // Tag requests for per-endpoint SLOs export default function () { http.get(`${BASE_URL}/products`, { tags: { name: 'get_products' } }) http.post(`${BASE_URL}/auth/login`, payload, { tags: { name: 'login' } }) }
yaml# .github/workflows/load-test.yml name: Load Test on: pull_request: branches: [main] jobs: load-test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: grafana/k6-action@v0.3.1 with: filename: tests/load/smoke.js env: BASE_URL: ${{ secrets.STAGING_URL }} # k6 exits non-zero if thresholds fail → PR check fails
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-21 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-10 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-23 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +26 percentage points is the difference between those two pass rates over the 23 comparable cases.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.