- name
- perfaudit
- description
- Forensic performance audit v1 (Gestalt-Popper). 23-phase deep analysis of everything that is SLOW RIGHT NOW: Core Web Vitals, bundle size bloat, render performance, JavaScript execution, image optimization, font loading, caching strategy, CDN configuration, SSR/SSG analysis, lazy loading, code splitting, API response times, N+1 query detection, database query performance, memory leaks, connection pooling, resource hints, third-party script impact, plus verdict, fix plan, fix execution, re-audit, and integration smoke gate. Score /360. Preamble v1.0 compliant. Audit -> Plan -> Fix -> Re-audit. Use when user says "/perfaudit", "performance audit", "why is it slow", "speed audit", "optimize performance", "core web vitals", "bundle analysis".
- allowed-tools
- ["Read","Write","Edit","Bash","Glob","Grep","Agent","TaskCreate","TaskUpdate","TaskList","TaskGet"]
- domain
- performance
- phases
- 23
- max_score
- 360
- read_only
- false
- triggers
- ["perf","performance","core web vitals","why is it slow","speed audit"]
<!-- AUDIT-META-V2-INJECTED -->
> ## ⚠️ MANDATORY FIRST STEP — READ THE V2 META-PROTOCOL
>
> **Before doing ANYTHING else**, Read `../_shared/audit-meta-protocol-v2.md`.
>
> That file overrides any conflicting guidance below for these five aspects:
> 1. Required CLI inputs (`--user-need`, `--hinge` are MANDATORY since 2026-05-08)
> 2. Required JSON output schema (v2: score + confidence + falsifiable_tests + user_need_match + hinge_findings)
> 3. Popper falsification — every PASS must cite ≥3 concrete commands run with actual output
> 4. Confidence calibration — `high` requires direct verification of every claim
> 5. Banned shortcut phrases — `looks correct`, `should be fine`, `appears to work` = automatic FAIL
>
> If `--user-need` or `--hinge` is missing from your invocation, refuse to run and write
> `{"score":0,"confidence":"low","error":"missing v2 inputs","request_redispatch":true}`.
>
> The legacy v1 schema (`{"score":100,"skill_used":"<name>"}`) is accepted with a warning until 2026-06-01,
> then removed. Always emit v2 going forward.
>
> Model context: this audit runs on Opus 4.7 with max effort. There is no time pressure.
> Run every test you claim to have run. Cite verbatim outputs. No exceptions.
---
# /perfaudit v1 — Forensic Performance Audit (Gestalt-Popper)
> *"The other audits ask 'does it work?' I ask 'does it work FAST ENOUGH for humans to care?'"*
---
## DOCTRINE
You are not a performance tester. You are a **performance forensic pathologist**. The running system is your patient — possibly hemorrhaging milliseconds, definitely bloated, pretending to be fast because nobody measured. Your job is to find every bottleneck, every wasted byte, every render-blocking resource while Lighthouse says "looks fine."
**The 5 Laws of Performance Forensics (Gestalt-Popper Synthesis):**
1. **If it loads, it's still guilty.** A page that renders in 8 seconds "works." But the user already left at 3. Performance bugs are invisible murders — the corpses are bounced sessions.
2. **Lighthouse scores lie (Popper).** A 90 on lab doesn't mean 90 on real devices. Test on throttled network, test on slow CPU, test the second visit, test the cold cache. FALSIFY every green score.
3. **Every kilobyte is debt.** That 200KB utility library for one function. That uncompressed hero image. That font with 400 glyphs when you use 80. Each byte is latency for someone on 3G.
4. **Clarity before measuring (Gestalt).** Before launching any profiler, UNDERSTAND the product. Read VISION.md, CLAUDE.md, README. Identify the **HINGE PAGE** — the page users hit first and most. Profile the hinge page with 10x depth.
5. **The cache lies (Popper).** "It's fast for me" means "I have a warm cache and fiber internet." FALSIFY every "it's fast" claim with cold cache, throttled CPU, 3G network.
**Gestalt Hinge Page:** Before Phase 1, identify THE page that defines the product experience. The landing page. The dashboard. The search results. THIS page gets every phase at maximum depth.
**Popper Performance Falsification Categories:**
- **SCORE vs REALITY** — Lighthouse says 90, real users experience 5s load
- **LAB vs FIELD** — Fast on your machine, slow on real devices
- **FIRST vs REPEAT** — Fast on second visit (cached), slow on first (cold)
- **DESKTOP vs MOBILE** — Fast on wired desktop, unusable on mobile 4G
- **AVERAGE vs P95** — Average response 200ms, P95 is 4 seconds
---
## SCOPE DETECTION (automatic)
```
EXAMPLES:
"/perfaudit"
-> Full 18-phase pipeline. Discover all pages, profile everything.
"/perfaudit the dashboard"
-> TARGETED: only dashboard pages
-> All phases scoped to dashboard routes
"/perfaudit after deploy"
-> POST-DEPLOY mode: compare before/after, focus on regressions
"/perfaudit bundle"
-> BUNDLE-FOCUSED: JS/CSS size, tree shaking, code splitting
"/perfaudit images"
-> IMAGE-FOCUSED: formats, compression, lazy loading, srcsets
"/perfaudit api"
-> API-FOCUSED: response times, N+1, caching, connection pooling
```
---
## OUTPUT CONTRACT
```
audits/.perfaudit/
|-- session.log
|-- discovery/
| |-- pages.json # All discovered routes/pages
| |-- api-endpoints.json # All API routes
| |-- assets.json # All static assets inventory
| |-- bundles.json # Bundle analysis breakdown
|-- reports/
| |-- core-web-vitals.md # Phase 1
| |-- bundle-analysis.md # Phase 2
| |-- render-performance.md # Phase 3
| |-- js-execution.md # Phase 4
| |-- image-optimization.md # Phase 5
| |-- font-loading.md # Phase 6
| |-- caching-strategy.md # Phase 7
| |-- cdn-configuration.md # Phase 8
| |-- ssr-ssg-analysis.md # Phase 9
| |-- lazy-loading.md # Phase 10
| |-- code-splitting.md # Phase 11
| |-- api-response-times.md # Phase 12
| |-- n-plus-one-queries.md # Phase 13
| |-- db-query-performance.md # Phase 14
| |-- memory-leaks.md # Phase 15
| |-- connection-pooling.md # Phase 16
| |-- resource-hints.md # Phase 17
| |-- third-party-scripts.md # Phase 18
|-- verdict.json
|-- verdict.md
|-- fix-plan.json
|-- fix-plan.md
|-- progress.json
|-- fix-log.md
```
---
## PHASE 0 — PROGRAMMATIC GATHER (HYBRID, runs FIRST, before all other phases)
> **NEW (2026-05-08, hybrid framework):** before any LLM analysis, programmatic
> tools gather every machine-checkable finding deterministically. The LLM then
> READS the resulting JSON instead of hand-grepping the codebase. Freed token
> budget is REINVESTED in deeper Popper falsification, hinge-point synthesis,
> user-need verification, and edge-case hunting.
### 0.1 Run the gather script (mandatory, FIRST step)
```bash
~/.omega/lib/audit-runner.sh perf "$PROJECT_PATH" \
--files="$FILES_MODIFIED" \
--url="$URL" \
--user-need="$USER_NEED_QUOTE" \
--hinge="$HINGE_POINT" \
--ticket="$TICKET_ID"
```
This invokes `~/.omega/lib/audit-gather/perf.sh` which runs:
Lighthouse perf category, size-limit (if configured), build-dir size analysis (.next/dist/build), node_modules size signal
Output is written to:
```
$PROJECT_PATH/audits/.perfaudit/
├── raw/ # raw tool outputs (JSON / text per tool)
└── evidence-summary.json # normalized findings, single source of truth for the LLM
```
When run inside a Linear-fix mission (`--ticket=ID`), the artifacts move to
`$PROJECT_PATH/audits/.linear-fix/<ID>/.perfaudit/` so multiple audits on the same
ticket can cross-reference each other (see 0.5).
### 0.2 evidence-summary.json schema
```jsonc
{
"audit": "perf",
"tools_run": ["..."],
"tools_skipped": [{"tool": "...", "reason": "..."}],
"findings_total": 514,
"findings_by_severity": {"critical": 2, "high": 17, "medium": 89, "low": 406, "info": 0},
"findings": [
{
"tool": "...",
"severity": "critical|high|medium|low|info",
"location": "file:line[:col]",
"rule": "...",
"message": "...",
"suggested_fix": "...",
"cross_tool_confirmed": false
}
],
"metrics": { /* tool-specific quantitative data */ },
"evidence_index": { /* paths to raw/ files for drill-down */ }
}
```
### 0.3 What you do AFTER the gather (this replaces hand-greps)
You now consume `evidence-summary.json` programmatically. You MUST:
1. **Read `evidence-summary.json` in full.** This is your evidence base.
2. **Read 3-5 critical files only** — the ones flagged as load-bearing in
`~/.omega/state/hinge-points-<ticket>.json` (or computed via
`${OMEGA_DIR:-$HOME/.omega}/skills/audits/_shared/hinge-analyzer.sh` if no ticket).
3. **DO NOT manually grep the codebase for what the gather already covered.**
The tools have already exhaustively scanned every file. Re-running grep
wastes tokens and produces the same evidence.
4. **DO read additional files** when (a) a finding's context is unclear from
message+location, (b) you need to verify a Popper falsification, or
(c) you suspect a missed edge case (Phase 2.4 below).
### 0.4 Banned operations after Phase 0
These are now forbidden because the gather already did them. If you catch
yourself about to run one, STOP and read `evidence-summary.json` first:
- ❌ `grep -rn "TODO" .` (the gather scanned for it)
- ❌ `find . -name "*.ts" | xargs wc -l` (the gather has size metrics)
- ❌ `npm audit` / `pip-audit` (the gather ran them — read the JSON)
- ❌ `eslint .` / `tsc --noEmit` / `lighthouse <url>` (already in raw/)
- ❌ Generic "let me check every file" loops (the gather's job, not yours)
You MAY still:
- ✅ Read SPECIFIC files cited in findings (verify the issue)
- ✅ Run a SPECIFIC `grep` to falsify a finding (Popper test, see Phase 2.1)
- ✅ Run a SPECIFIC tool the gather couldn't (e.g. dynamic Playwright probe
for a flow scenario the static gather can't model)
### 0.5 Cross-audit synthesis (read sibling evidence-summary.json files)
If this audit runs as part of a Linear-fix mission, sibling audits' summaries
are at `$PROJECT_PATH/audits/.linear-fix/<TICKET>/.<other-audit-id>/evidence-summary.json`.
Read them. Use them.
Examples of high-value cross-audit findings:
- **codeaudit + secaudit** flag the same `auth.ts` line → confidence escalation,
the file is BOTH a code-quality risk AND a security risk.
- **perfaudit + a11yaudit** on the same image → joint fix opportunity (lazy-load
+ `alt` attribute in one change).
- **apiaudit + dataaudit** on the same endpoint+table pair → contract drift
between the API surface and the schema.
- **debugaudit + flowaudit** report the same broken page → user-flow blocker.
When you find such a confluence, mark the finding `cross_audit_confirmed: true`
in your `verdict.json` and bump severity by one level.
---
## PHASE 0: RECONNAISSANCE
> *"Know the patient's baseline before diagnosing."*
```
1. PROJECT DISCOVERY
-> Read CLAUDE.md, README, package.json/pyproject.toml
-> Identify: stack, framework, build tool, deploy target
-> Find: prod URL, dev URL, CDN, hosting provider
2. PAGE/ROUTE DISCOVERY
-> Scan all routes (Next.js app/, pages/, etc.)
-> Build complete sitemap with page types (SSR, SSG, CSR, ISR)
-> Identify high-traffic pages (hinge pages)
3. ASSET INVENTORY
-> List all JS bundles with sizes
-> List all CSS files with sizes
-> List all images with formats and sizes
-> List all fonts with formats and subsets
-> List all third-party scripts
4. PERFORMANCE BASELINE
-> Current Lighthouse scores (performance, accessibility, best practices)
-> Current bundle sizes (total JS, total CSS)
-> Current largest assets
-> This becomes the "before" for comparison
```
---
## PHASE 1: CORE WEB VITALS AUDIT
> *"Google measures these. Your ranking depends on them. Your users feel them."*
```
FOR EVERY discoverable page:
1. LCP (Largest Contentful Paint) — target < 2.5s
-> Identify LCP element (image? text? video?)
-> Measure time to LCP on desktop and mobile
-> If > 2.5s: trace the critical path to LCP
-> Common culprits: unoptimized hero image, render-blocking CSS, slow server
2. FID / INP (Interaction to Next Paint) — target < 200ms
-> Measure input delay on first interaction
-> Identify long tasks blocking main thread
-> Check for heavy JS execution during page load
-> Common culprits: hydration, third-party scripts, large event handlers
3. CLS (Cumulative Layout Shift) — target < 0.1
-> Measure layout shifts during page load
-> Identify elements causing shifts (images without dimensions, dynamic content, fonts)
-> Check for late-loading ads/embeds pushing content
-> Common culprits: images without width/height, web fonts, dynamic injection
4. TTFB (Time to First Byte) — target < 800ms
-> Measure server response time per page
-> If > 800ms: server-side bottleneck (DB, computation, cold start)
5. FCP (First Contentful Paint) — target < 1.8s
-> Measure time to first visible content
-> If > 1.8s: render-blocking resources in critical path
FALSIFY: Run at 3G throttling + 4x CPU slowdown. If scores drop more than 30 points, the "good" score was a lie.
```
---
## PHASE 2: BUNDLE SIZE ANALYSIS
> *"Every byte you ship is a byte the user pays for in time."*
```
1. TOTAL BUNDLE SIZE
-> Total JS (compressed + uncompressed)
-> Total CSS (compressed + uncompressed)
-> Target: < 200KB JS compressed for initial load
2. CHUNK ANALYSIS
-> List all chunks with sizes
-> Identify largest chunks — what's inside?
-> Are vendor chunks separated from app chunks?
-> Are route-based chunks working? (each page loads only its code)
3. TREE SHAKING VERIFICATION
-> Import entire library vs named imports? (import _ from 'lodash' vs import { map })
-> Dead exports in the bundle?
-> Side-effect-free packages marked correctly?
4. DUPLICATE DEPENDENCIES
-> Same package at multiple versions? (bundle bloat)
-> Multiple packages doing the same thing? (moment + dayjs + date-fns)
-> Polyfills for features all target browsers support?
5. SOURCE MAP AUDIT
-> Source maps disabled in production? (security + size)
-> If enabled: not served to public (only error tracking)
```
---
## PHASE 3: RENDER PERFORMANCE
> *"The browser painted it. But at what cost?"*
```
1. CRITICAL RENDERING PATH
-> How many render-blocking resources?
-> CSS in <head> blocking first paint?
-> Synchronous <script> tags blocking parser?
-> Inline critical CSS? Defer non-critical?
2. RE-RENDER ANALYSIS (React/Vue/Svelte)
-> Components re-rendering unnecessarily?
-> Missing memoization (React.memo, useMemo, useCallback)?
-> State updates causing full-tree re-renders?
-> Context providers triggering widespread re-renders?
3. LAYOUT THRASHING
-> Forced synchronous layouts (read-write-read-write patterns)?
-> offsetHeight/getBoundingClientRect in loops?
-> DOM measurements during animation frames?
4. PAINT ANALYSIS
-> Layers being repainted unnecessarily?
-> will-change overuse creating too many layers?
-> Composited layers count (target: < 20 for typical page)
GitHub에서 보기