kramme-cc-workflow/skills/kramme:code:performance/SKILL.md
(experimental) Measure-first performance discipline tied to Core Web Vitals (LCP, INP, CLS). Use when users or monitoring report slowness, CWV scores miss thresholds, performance requirements exist in the spec, you suspect a recent change introduced a regression, or you're building features that handle large datasets or high traffic. Enforces baseline measurement, single-bottleneck fixes, verification, and regression guards. Complements the review-time `performance-oracle` agent.
npx skillsauth add abildtoft/kramme-cc-workflow kramme:code:performanceInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Measure before optimizing. Performance work without measurement is guessing — and guessing leads to premature optimization that adds complexity without improving what matters. Profile first, identify the actual bottleneck, fix it, measure again. Optimize only what measurements prove matters.
| Metric | Good | Needs Improvement | Poor | | --- | --- | --- | --- | | LCP (Largest Contentful Paint) | ≤ 2.5 s | ≤ 4.0 s | > 4.0 s | | INP (Interaction to Next Paint) | ≤ 200 ms | ≤ 500 ms | > 500 ms | | CLS (Cumulative Layout Shift) | ≤ 0.1 | ≤ 0.25 | > 0.25 |
A change that regresses any metric from Good into Needs Improvement is a regression, even if the absolute number still looks fine. Full measurement commands, what each metric measures, mobile/desktop differences, and the noise floor live in references/core-web-vitals.md.
Each optimization is one pass through this loop:
1. MEASURE → Establish baseline with real data
2. IDENTIFY → Find the actual bottleneck (not assumed)
3. FIX → Address the specific bottleneck
4. VERIFY → Measure again, confirm improvement
5. GUARD → Add monitoring or tests to prevent regression
Before writing any optimization, emit a SIMPLICITY CHECK marker stating the smallest change that would clear the budget. Only expand beyond that if remeasurement proves it is not enough.
SIMPLICITY CHECK: <one-line summary of the smallest fix that would clear the budget>
If the fix you end up shipping is not the smallest version, write a second line explaining what forced the expansion. Every extra abstraction — a cache wrapper, a memoized selector, a code-split boundary — adds complexity. Only add it when a measurement demands it.
When profiling surfaces a second bottleneck outside the current slice — an N+1 query in an adjacent endpoint, a missing image dimension on a neighboring page, a useEffect that looks wrong but is not on the hot path — emit a NOTICED BUT NOT TOUCHING marker and keep going. Do not silently fix perf smells that are not on the measured bottleneck.
NOTICED BUT NOT TOUCHING: <the perf smell you saw>
Why skipping: <not on measured bottleneck / out of scope / deferred>
The reason: every "while I'm here" fix dilutes the before/after delta for the change you are measuring, and makes it impossible to attribute the gain cleanly.
Emit both markers in your response text, using the exact formats above, so a calling agent or reviewer can parse them.
Two complementary approaches — use both:
web-vitals library, Chrome User Experience Report, project APM): real user data in real conditions. Required to validate that a fix actually improved user experience, not just the synthetic number.Frontend:
// Synthetic: Lighthouse in Chrome DevTools (or CI)
// Chrome DevTools → Performance tab → Record
// RUM: web-vitals library in code
import { onCLS, onINP, onLCP } from "web-vitals";
onLCP(console.log);
onINP(console.log);
onCLS(console.log);
Backend:
// Response time logging
// Application Performance Monitoring (APM)
// Database query logging with timing
// Simple timing
console.time("db-query");
const result = await db.query(/* … */);
console.timeEnd("db-query");
Record the baseline number with units in the ticket or commit message. "Fast enough" is not a baseline.
Common bottlenecks by category:
Frontend:
| Symptom | Likely cause | Investigation | | --- | --- | --- | | Slow LCP | Large images, render-blocking resources, slow server | Check network waterfall, image sizes | | High CLS | Images without dimensions, late-loading content, font shifts | Check layout-shift attribution | | Poor INP | Heavy JavaScript on main thread, large DOM updates | Check long tasks in Performance trace | | Slow initial load | Large bundle, many network requests | Check bundle size, code splitting |
Backend:
| Symptom | Likely cause | Investigation | | --- | --- | --- | | Slow API responses | N+1 queries, missing indexes, unoptimized queries | Check database query log | | Memory growth | Leaked references, unbounded caches, large payloads | Heap snapshot analysis | | CPU spikes | Synchronous heavy computation, regex backtracking | CPU profiling | | High latency | Missing caching, redundant computation, network hops | Trace requests through the stack |
Use the symptom to decide what to profile first:
What is slow?
├── First page load
│ ├── Large bundle? --> Measure bundle size, check code splitting
│ ├── Slow server response? --> Measure TTFB in DevTools Network waterfall
│ │ ├── DNS long? --> Add dns-prefetch / preconnect for known origins
│ │ ├── TCP/TLS long? --> Enable HTTP/2, check edge deployment, keep-alive
│ │ └── Waiting (server) long? --> Profile backend, check queries and caching
│ └── Render-blocking resources? --> Check network waterfall for CSS/JS blocking
├── Interaction feels sluggish
│ ├── UI freezes on click? --> Profile main thread, look for long tasks (>50 ms)
│ ├── Form input lag? --> Check re-renders, controlled-component overhead
│ └── Animation jank? --> Check layout thrashing, forced reflows
├── Page after navigation
│ ├── Data loading? --> Measure API response times, check for waterfalls
│ └── Client rendering? --> Profile component render time, check for N+1 fetches
└── Backend / API
├── Single endpoint slow? --> Profile database queries, check indexes
├── All endpoints slow? --> Check connection pool, memory, CPU
└── Intermittent slowness? --> Check for lock contention, GC pauses, external deps
Use the tree as a triage path, not a checklist. Follow one branch per measurement.
Six common anti-patterns, each with a canonical fix. Full before/after code examples live in references/anti-patterns.md. The named anti-patterns:
include / join / eager loading.take + skip or cursor pagination) and a hard server-side limit.<img> without width/height, no srcset, no loading="lazy", uncompressed format. Fix with <picture>, srcset, explicit dimensions, modern formats (AVIF/WebP), fetchpriority="high" for LCP image, loading="lazy" for below the fold.useMemo), React.memo only when profiling proves it helps.lazy + Suspense), dynamic imports for heavy features, and tree-shaking (ESM + sideEffects: false). Profile before micro-optimizing import styles.Cache-Control headers for static assets, and content hashing in filenames for immutable long-cached resources.The memoization trap. React.memo, useMemo, and useCallback everywhere is itself a perf anti-pattern: each adds bookkeeping cost and obscures render causes. Apply only when profiling shows a measured win — and document the measurement next to the memo.
Remeasure with the same tool, on the same device class, on the same network profile you used for the baseline. Then check:
If the change does not clear the budget, revert and re-identify — do not stack a second optimization on top of an unverified first one.
Lock in the fix so it cannot silently regress:
bundlesize or the bundler's built-in budget, fails CI when a route exceeds the limit.lhci autorun with score thresholds and CWV assertions in the PR pipeline.A fix without a guard is a fix that will regress the next time someone changes the code.
Set explicit budgets and enforce them in CI:
JavaScript bundle: < 200 KB gzipped (initial load)
CSS: < 50 KB gzipped
Images: < 200 KB per image (above the fold)
Fonts: < 100 KB total
API response time: < 200 ms (p95)
Time to Interactive: < 3.5 s on 4G
Lighthouse Performance score: ≥ 90
Enforce in CI:
# Bundle size check
npx bundlesize --config bundlesize.config.json
# Lighthouse CI
npx lhci autorun
Budgets are floors, not ceilings — a PR that adds 30 KB to the bundle without justifying it against the budget is a PR that should not merge. Example bundlesize.config.json, lighthouserc.json, and a custom regression test live in references/performance-checklist.md.
Before declaring a perf slice done, confirm every item:
NOTICED BUT NOT TOUCHING entry exists for every perf smell observed outside the measured bottleneck.If any item is unchecked, the slice is not done. Fix the gap or split the slice.
If these siblings are installed:
performance-oracle agent verifies measurements and bottleneck identification post-hoc. Following MEASURE/VERIFY discipline here makes that review mechanical.kramme:code:incremental: each optimization is one slice through the incremental loop. The five-step workflow fits inside a single increment; the budget becomes the increment's exit criterion.These are the lies you will tell yourself to justify skipping the measurement or the guard. Each one has a correct response:
If you notice any of these, stop and return to step 1:
SIMPLICITY CHECK that is missing at the top of the fix.development
Compare an existing PR's title and body against the actual branch diff and report drift — false claims, missing major changes, stale scope, missing risk callouts. Use after pushing changes to a branch with an open PR, or before requesting review. Read-only by default; add --fix to delegate to kramme:pr:generate-description for an updated description. Complements kramme:pr:code-review (which checks description accuracy as one signal among many code-quality checks) by being a fast, focused, single-purpose check that runs in seconds.
tools
Reviews plugin skills for focused scope, progressive disclosure, portability, safety, retry behavior, and documentation quality. Use when auditing a SKILL.md, skill directory, or proposed skill text against skill-authoring standards. Not for creating new skills, editing skills, or reviewing ordinary application code.
tools
Reviews recent agent session transcripts to find repeated manual workflows or repeated user asks, then proposes and optionally scaffolds only useful new skills or custom subagents. Use when the user asks to inspect recent sessions, find automation opportunities, or create reusable workflows from repeated work. Not for summarizing one session, general retrospectives, or codebase refactoring.
data-ai
Remove all DONE issues and renumber remaining issues within each prefix group. Not for editing live issue content, archiving still-open issues, or moving issues between prefix groups.