name: 403-sf-perf description: "Audit performance, bundles, rendering, CWV, data, and databases." disable-model-invocation: true argument-hint: '[file-path | "global"] (omit for full project)'
Canonical Paths
Before resolving any ShipFlow-owned file, load $SHIPFLOW_ROOT/skills/references/canonical-paths.md ($SHIPFLOW_ROOT defaults to $HOME/shipflow). ShipFlow tools, shared references, skill-local references/*, templates, workflow docs, and internal scripts must resolve from $SHIPFLOW_ROOT, not from the project repo where the skill is running. Project artifacts and source files still resolve from the current project root unless explicitly stated otherwise.
Chantier Tracking
Trace category: conditionnel.
Process role: source-de-chantier.
Before producing the final report, load $SHIPFLOW_ROOT/skills/references/chantier-tracking.md when this run is attached to a spec-first chantier. If exactly one active specs/*.md chantier is identified, append the current run to Skill Run History, update Current Chantier Flow when the run changes the chantier state, and include a final Chantier block. If no unique chantier is identified, do not write to any spec; report Chantier: non applicable or Chantier: non trace with the reason.
Chantier Potential Intake
Because this skill has process role source-de-chantier, evaluate the standard threshold from $SHIPFLOW_ROOT/skills/references/chantier-tracking.md before the final report. If the findings reveal non-trivial future work and no unique chantier owns it, do not write to an existing spec; add a Chantier potentiel block with oui, non, or incertain, a proposed title, reason, severity, scope, evidence, recommended /100-sf-spec ... command, and next step. If the work is only a direct local fix or already belongs to the current chantier, state Chantier potentiel: non with the concrete reason.
Context
- Current directory: !
pwd - Project CLAUDE.md: !
head -120 CLAUDE.md 2>/dev/null || echo "no CLAUDE.md" - Package.json: !
cat package.json 2>/dev/null | head -80 || echo "no package.json" - Build config: !
cat astro.config.* next.config.* vite.config.* 2>/dev/null | head -60 || echo "no build config" - Framework detection: !
cat package.json 2>/dev/null | grep -oE '"(astro|next|react|vue|expo|fastapi|flask)"' | head -5 || echo "unknown" - Image/font assets: !
find . -maxdepth 3 \( -name "*.png" -o -name "*.jpg" -o -name "*.jpeg" -o -name "*.gif" -o -name "*.webp" -o -name "*.avif" -o -name "*.woff" -o -name "*.woff2" -o -name "*.ttf" \) 2>/dev/null | grep -v node_modules | wc -l - Third-party scripts: !
grep -r "script.*src=" src/ 2>/dev/null | grep -v node_modules | head -10 || echo "none" - Project structure: !
find . -maxdepth 3 -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.jsx" -o -name "*.astro" -o -name "*.vue" -o -name "*.py" -o -name "*.sh" \) 2>/dev/null | grep -v node_modules | grep -v .git | grep -v dist | sort | head -40
Mode detection
403-sf-perfanswers one specialist question:
What performance risks are limiting this scope across bundle, rendering, loading, fetching, caching, or database behavior?
$ARGUMENTSis "global" → GLOBAL MODE: performance audit across ALL projects.$ARGUMENTSis a file path → FILE MODE: deep performance review of that single file.$ARGUMENTSis empty → PROJECT MODE: full performance audit.
Keep the boundary explicit: stay in 403-sf-perf when performance is already the dominant audit domain. Route back to 400-sf-audit only when the operator needs multi-domain audit planning or consolidation beyond performance.
GLOBAL MODE
Audit performance across ALL projects in the workspace.
Read
discovered project-local corpora (shipflow_data/markers)— check the Domain Applicability table. Identify projects with ✓ in the Perf column.Use the runtime's structured question tool when available to let the user choose:
- Question: "Which projects should I audit for performance?"
multiSelect: true- One option per applicable project: label = project name, description = stack inferred from project-local markers
Use available parallel agent/tooling to launch one bounded worker per selected project in a single parallel batch when supported. If unavailable, run the selected projects sequentially.
Agent prompt must include:
cd [path]then readCLAUDE.mdfor project context- The absolute date, exact project path, and the perf context already surfaced by this skill (build config, framework detection, third-party scripts, assets)
- The complete PROJECT MODE section from this skill (all 6 phases)
- The Tracking section from this skill
- Rule: before scoring, identify linked systems and performance consequences across bundle, rendering, fetching, caching, and third-party integrations
- Rule: do not ask follow-up questions; if context is missing, state assumptions / confidence limits and continue
- Required sub-report sections:
Scope understood,Context read,Linked systems & consequences,Findings,Confidence / missing context
After all agents return, compile a cross-project performance report:
GLOBAL PERFORMANCE AUDIT — [date] ═══════════════════════════════════════ PROJECT SCORES [project] [A/B/C/D] — summary ... CROSS-PROJECT PATTERNS [Shared perf issues: duplicate large deps, common anti-patterns] ALL ISSUES BY SEVERITY 🔴 [project] file:line — description 🟠 [project] file:line — description 🟡 [project] file:line — description Total: X critical, Y high, Z medium across N projects ═══════════════════════════════════════Update project-local
shipflow_data/workflow/AUDIT_LOG.md(one traffic-first audit record per project, Perf column) and project-localshipflow_data/workflow/TASKS.md(each project's### Audit: Perfsubsection).Ask: "Which projects should I fix?" — list projects with scores. Fix only approved projects, one at a time.
FILE MODE
Step 1: Gather context
- Read the target file (
$ARGUMENTS). - Read files it imports/depends on (follow the imports, 1 level deep).
- Identify the file's role: component, page, API route, utility, layout.
Step 2: Audit the file
Score each category A/B/C/D.
1. Bundle Impact
- No large dependencies imported for a small feature (e.g., lodash for one function)
- Imports use tree-shakeable paths (named imports, not full-library imports)
- Dynamic imports / lazy loading for heavy components
- No barrel file re-exports pulling in unused code
2. Rendering Performance
- No unnecessary re-renders (React: stable callbacks, proper deps arrays, memo where needed)
- No expensive computations on every render cycle
- CSS-in-JS not generating new class names on every render
- Conditional rendering for expensive subtrees
- Lists use proper keys (not index for dynamic lists)
3. Data Fetching
- No waterfall requests (sequential when could be parallel)
- Data fetched at the right level (not too high, not too low)
- No over-fetching (requesting fields that aren't used)
- Proper loading/error states
- Caching or deduplication for repeated queries
4. Memory & Cleanup
- Event listeners removed on unmount
- Intervals/timeouts cleared
- Subscriptions unsubscribed
- AbortController for fetch requests on unmount
- No closures capturing stale state
Step 3: Fix
Fix issues rated B or worse. Explain each change.
Step 4: Report
PERFORMANCE REVIEW: [file name]
─────────────────────────────────────
Bundle Impact [A/B/C/D] — one-line summary
Rendering [A/B/C/D] — one-line summary
Data Fetching [A/B/C/D] — one-line summary
Memory & Cleanup [A/B/C/D] — one-line summary
─────────────────────────────────────
OVERALL [A/B/C/D]
Fixed: X issues | Needs decision: Y
PROJECT MODE
Workspace root detection
If the current directory has no project markers (no package.json, no requirements.txt, no src/ dir, no lib.sh) BUT contains multiple project subdirectories — you are at the workspace root, not inside a project.
Use the runtime's structured question tool when available, or a concise plain-text question:
- Question: "You're at the workspace root. Which project(s) should I audit for performance?"
multiSelect: true- Options:
- All projects — "Run performance audit across every project" (Recommended)
- One option per project from
discovered project-local corpora (shipflow_data/markers): label = project name, description = stack
Then proceed to GLOBAL MODE with the selected projects.
PHASE 1: BUNDLE & LOADING
Analyze the project's build output and dependency weight.
1.1 Dependency Weight
- Identify top 10 largest dependencies by install size
- Flag oversized dependencies with lighter alternatives (moment→dayjs, lodash→lodash-es or native)
- Detect duplicate packages in the dependency tree (same package, different versions)
- Check for barrel exports (
index.ts) re-exporting entire modules — bundle bloat
1.2 Code Splitting
- Route-level code splitting configured
- Heavy components lazily loaded (
React.lazy,defineAsyncComponent, dynamic imports) - No single bundle containing rarely-used features
- Sentry browser tracing, profiling, replay, and source-map helpers are not bundled more broadly than needed
1.3 Asset Optimization
- Images use modern formats (WebP/AVIF with fallbacks)
- Images have
srcset/ responsive sizing - SVGs optimized (no unnecessary metadata)
- Fonts use WOFF2 format
-
font-display: swaporoptionalconfigured - Font subsetting applied (only needed character ranges)
1.4 Script Loading
- Third-party scripts use
asyncordefer - Analytics/tracking/Sentry initialization does not block render
- No synchronous
<script>tags in<head> - Prefetch/preload for critical resources
- Sentry replay, tracing, and profiling sampling rates are proportionate to the environment and privacy/performance budget
PHASE 2: RENDERING
2.1 Rendering Strategy
- Static pages use SSG (not SSR or CSR)
- Dynamic pages use appropriate strategy (SSR for SEO, CSR for app-like)
- Client-side hydration is minimal (Astro islands, React Server Components)
2.2 Client Hydration Analysis
- Components that could be server-only are not hydrated client-side
-
client:loadvsclient:visiblevsclient:idleused appropriately (Astro) - No full-page client hydration for mostly-static content
2.3 Re-render Issues
- No parent re-renders cascading to all children unnecessarily
- Context providers scoped (not wrapping entire app for local state)
- Expensive renders behind React.memo / computed / memoization
- Event handlers are stable references (useCallback where needed)
2.4 Virtualization
- Long lists (>50 items) use virtualization (react-window, @tanstack/virtual)
- Tables with many rows are virtualized
- Infinite scroll uses proper windowing
PHASE 3: CORE WEB VITALS READINESS
3.1 LCP (Largest Contentful Paint)
- Hero image/video has
fetchpriority="high"andloading="eager" - LCP element is in initial HTML (not injected by JS)
- No render-blocking CSS/JS delaying LCP
- Fonts preloaded if they affect LCP text
-
<Image>component used with priority prop for above-fold images
3.2 CLS (Cumulative Layout Shift)
- Images and videos have explicit
widthandheightattributes - No FOUT (Flash of Unstyled Text) —
font-displayconfigured - No content injected above the fold after load (ads, banners, lazy content)
- Skeleton screens match actual content dimensions
- No layout-shifting animations
3.3 INP (Interaction to Next Paint)
- Click/tap handlers respond under 200ms
- Heavy computations offloaded to Web Workers
- No long tasks blocking the main thread (>50ms)
- Input handlers debounced appropriately
- Optimistic UI updates for perceived speed
- Sentry instrumentation, breadcrumbs, and replay capture do not add avoidable long tasks to key interactions
3.4 TTFB (Time to First Byte)
- Static pages served from CDN/edge
- SSR pages have efficient data fetching (no waterfalls)
- Database queries for page data are indexed
- Caching headers configured (Cache-Control, ETag)
PHASE 4: DATA FETCHING
4.1 Request Patterns
- No waterfall requests (use
Promise.allor parallel loaders) - No N+1 query patterns (fetching related data in loops)
- No over-fetching (GraphQL: use fragments; REST: use sparse fieldsets or dedicated endpoints)
- Request deduplication for same data needed in multiple components
4.2 Caching Strategy
- API responses cached appropriately (SWR, React Query, Convex reactive queries)
- Stale-while-revalidate pattern for non-critical data
- Cache invalidation on mutations
- Static data pre-fetched at build time where possible
4.3 Optimistic Updates
- Write operations update UI immediately (don't wait for server response)
- Rollback on failure with user feedback
- Convex: use optimistic updates for mutations
4.4 Prefetching
- Next page data prefetched on link hover/focus
- Critical API calls initiated early (not waiting for component mount)
- Route prefetching configured for likely next pages
PHASE 5: DATABASE & BACKEND
5.1 Query Optimization
- Indexes defined for common query patterns
- No full table scans for filtered queries
- Compound indexes for multi-field queries
- Convex: indexes defined in
schema.tsfor allwithIndexqueries
5.2 Unbounded Queries
- All list queries have limits (
.take(n)/LIMIT) - Pagination implemented for large datasets
- No
SELECT *or equivalent fetching all fields - Cursor-based pagination for large datasets (not offset)
5.3 Connection & Resource Management
- Connection pooling configured for database
- No connection leaks (connections always returned to pool)
- Timeouts configured for external API calls
- Rate limiting respected for third-party APIs
5.4 Caching Layer
- Frequently accessed data cached (in-memory, Redis, or CDN)
- Cache invalidation strategy defined
- Cache TTL appropriate for data freshness requirements
- No stale cache served for critical data (auth, payments)
PHASE 6: FIX + REPORT
Fix Priority
- Critical — N+1 queries, missing indexes, unbounded queries, memory leaks
- High — Missing code splitting, no image optimization, waterfall requests
- Medium — Missing lazy loading, suboptimal caching, minor CWV issues
- Low — Prefetching, optimistic updates, minor bundle size improvements
Report
PERFORMANCE AUDIT: [project name] — [stack detected]
═══════════════════════════════════════════════════
BUNDLE & LOADING [A/B/C/D]
Dependency Weight [A/B/C/D]
Code Splitting [A/B/C/D]
Asset Optimization [A/B/C/D]
Script Loading [A/B/C/D]
RENDERING [A/B/C/D]
Strategy [A/B/C/D]
Client Hydration [A/B/C/D]
Re-render Issues [A/B/C/D]
CORE WEB VITALS [A/B/C/D]
LCP Readiness [A/B/C/D]
CLS Readiness [A/B/C/D]
INP Readiness [A/B/C/D]
TTFB Readiness [A/B/C/D]
DATA FETCHING [A/B/C/D]
Request Patterns [A/B/C/D]
Caching [A/B/C/D]
Optimization [A/B/C/D]
DATABASE & BACKEND [A/B/C/D]
Query Optimization [A/B/C/D]
Resource Management [A/B/C/D]
═══════════════════════════════════════════════════
OVERALL [A/B/C/D]
Fixed: X issues | Needs decision: Y
Stack-specific notes
Apply the relevant section based on detected framework:
Astro: Focus on islands pattern — verify client:* directives are minimal. Static pages should have zero JS. Check astro:assets Image component usage. View Transitions API perf impact.
Next.js: Check RSC (React Server Components) boundaries — "use client" only where needed. Streaming SSR for slow data. next/image with priority for above-fold. next/font for zero-layout-shift fonts.
React Native / Expo: List virtualization (FlatList vs FlashList). Image caching (expo-image). Bundle size with npx expo-doctor. Hermes engine optimizations. Avoid inline styles in render loops.
Python (FastAPI): Async I/O for all external calls. Connection pooling (SQLAlchemy async). Background tasks for heavy operations. Response model filtering (don't return full ORM objects).
Bash/Shell: Script execution time. Subshell avoidance where possible. Caching of expensive operations (BuildFlowz 5s TTL pattern). Avoid repeated file reads in loops.
Tracking (all modes)
Shared file write protocol for AUDIT_LOG.md and TASKS.md:
- First load
$SHIPFLOW_ROOT/skills/references/operational-record-format.md; new audit and task records must use that traffic-first operational format. - Treat the snapshots loaded at skill start as informational only.
- Right before each write, re-read the target file from disk and use that version as authoritative.
- Append or replace only the intended row or subsection; never rewrite the whole file from stale context.
- If the expected anchor moved or changed, re-read once and recompute.
- If it is still ambiguous after the second read, stop and ask the user instead of forcing the write.
After generating the report and applying fixes:
Log the audit
Create or update traffic-first audit operational records in the target audit logs:
- Project-local
shipflow_data/workflow/AUDIT_LOG.md: create or update a traffic-firstaudit:record for the Perf audit. - Project-local
./AUDIT_LOG.md: same project-explicit traffic-first record; keep the required[project]token.
Create either file if missing with a short heading and traffic-first audit records per the shared operational record format.
Update TASKS.md
- Local TASKS.md (project root): create or update traffic-first task records for the Perf audit findings.
- Project-local
shipflow_data/workflow/TASKS.md: create or update the same traffic-first task records.
Important
- audit-seo keeps its own SEO-specific performance checks (render-blocking resources, LCP for SEO ranking). This skill does deep performance analysis.
- Be specific about file paths and line numbers for every issue.
- Detect the stack automatically. Only audit relevant sections (e.g., skip CWV for React Native).
- For Bash/Shell projects, focus on script efficiency and caching patterns.
- Performance issues are never optional — a slow site is a broken site.