# Performance Audit Pack **Pack ID:** `ccgm/performance` **Applies when:** `always` --- ## Scope This pack audits common performance anti-patterns in application code. It checks for N+1 query patterns (a data-fetching loop that issues one query per item rather than batching), React components that re-render unnecessarily due to missing `React.memo`, and large library imports that pull in entire packages when only a small subset is needed. It does NOT audit server infrastructure, network latency, database index design, or build pipeline performance. React-specific checks (missing-react-memo) produce no findings on non-React repositories. --- ## applies_when Rationale | Condition | Reason | |-----------|--------| | `always` | N+1 query and large-bundle-import checks apply to any stack; missing-react-memo is React-specific but produces no findings on non-React repos, making `always` the correct gate (same behavior as today's Agent 8 category prompt). | --- ## Checks --- ### `performance/n-plus-one-query` **Severity:** `high` **Confidence:** `medium` **Detection:** `llm` #### Detection **LLM instruction (if detection = llm or hybrid):** ``` Audit performance patterns. Most findings need human review. READ AND APPLY: ~/.claude/skills/audit/reference/fix-patterns.md Use the Fix Type Reference table and confidence levels from that file when classifying findings. Do not rely on memory — open and apply the file. Check for N+1 query patterns: - Scan data access code (ORM calls, raw SQL, Supabase/Prisma/TypeORM/Mongoose queries, fetch calls to internal APIs, etc.) for patterns where a query or fetch is issued inside a loop or array map. - Flag code where: - A `.findMany()`, `.select()`, `.query()`, or equivalent is called inside a `for`, `while`, `forEach`, `map`, `reduce`, or `flatMap` over a result set. - Each iteration fetches related data that could be obtained via a JOIN or batch query (e.g. fetching a user for each order in a list of orders). - Do NOT flag cases where the loop body is performing non-database work (calculations, transformations) and the DB call is outside the loop. - Do NOT flag intentional per-item operations where batching is not possible or where the set size is bounded to 1. Report each finding with: file path, line number, a brief description of the pattern (e.g. "fetches user inside orders loop"), severity HIGH, and auto_fixable=false (requires a refactor to batch queries or use eager loading). ``` #### Spine Wiring This check is LLM-only. No spine tool is involved. ```yaml check_id: performance/n-plus-one-query detection: llm tool: ~ ``` #### Severity / Confidence **Severity rationale:** N+1 query patterns cause quadratic or worse database load as dataset size grows, directly degrading response times and database resource usage. High severity reflects the production impact when these patterns hit real data volumes. **Confidence rationale:** Identifying a query inside a loop requires understanding the semantics of the called function and the loop's purpose, which the LLM can reason about but may misidentify in complex or abstracted code, yielding medium confidence. **Rubric entry:** `performance/n-plus-one-query` #### Fixture **True positive** (`src/api/orders.ts` fetches user inside orders loop): ```typescript // src/api/orders.ts async function getOrdersWithUsers(orderIds: string[]) { const orders = await db.orders.findMany({ where: { id: { in: orderIds } } }); // N+1: one user query per order return Promise.all(orders.map(async (order) => { const user = await db.users.findUnique({ where: { id: order.userId } }); return { ...order, user }; })); } ``` Finding: `src/api/orders.ts:5` — `db.users.findUnique` called inside `map` over orders list; batch with `findMany` and join in memory. **True negative** (should produce NO finding): ```typescript // src/api/orders.ts async function getOrdersWithUsers(orderIds: string[]) { const orders = await db.orders.findMany({ where: { id: { in: orderIds } }, include: { user: true }, // eager-loaded in single query }); return orders; } ``` No finding: user is loaded via `include` in a single batched query. --- ### `performance/missing-react-memo` **Severity:** `low` **Confidence:** `medium` **Detection:** `llm` #### Detection **LLM instruction (if detection = llm or hybrid):** ``` Audit performance patterns. Most findings need human review. READ AND APPLY: ~/.claude/skills/audit/reference/fix-patterns.md Use the Fix Type Reference table and confidence levels from that file when classifying findings. Do not rely on memory — open and apply the file. Check for missing React.memo: - Scan React component files (*.tsx, *.jsx) for functional components that: - Accept props (not zero-prop components). - Are re-exported or used as children of other components. - Have a parent component that re-renders frequently (e.g. contains state that changes on user interaction) where the child's props do not change. - Flag components where wrapping in React.memo would prevent unnecessary re-renders. - Be conservative: only flag when there is evidence the parent re-renders on state change and the child's props are stable (primitives, stable references via useMemo/ useCallback). Do NOT flag every component — only those where memo would have clear value. - Do NOT flag components that use context or internal state, as memo does not prevent re-renders caused by context changes. Report each finding with: file path, line number (the component's declaration line), component name, severity LOW, and auto_fixable=true at medium confidence (add memo wrapper per fix-patterns.md). ``` #### Spine Wiring This check is LLM-only. No spine tool is involved. ```yaml check_id: performance/missing-react-memo detection: llm tool: ~ ``` #### Severity / Confidence **Severity rationale:** A component re-rendering unnecessarily wastes CPU and can cause cascading child re-renders, but the UI remains correct. Low severity reflects that this is a performance optimization opportunity rather than a correctness defect. **Confidence rationale:** Determining whether `React.memo` would have net benefit requires understanding both parent rendering frequency and prop stability; this analysis is context-dependent and may be incorrect for complex component trees, yielding medium confidence. **Rubric entry:** `performance/missing-react-memo` #### Fixture **True positive** (`src/components/UserCard.tsx` not memoized, parent re-renders on typing): ```tsx // src/components/UserCard.tsx interface UserCardProps { userId: string; name: string; } export function UserCard({ userId, name }: UserCardProps) { return