@vpxa/aikit 0.1.186 → 0.1.187

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -164,7 +164,7 @@ Code signals: A switch/if-else chain replaced with a strategy pattern or subclas
164
164
  Code signals: Multiple related parameters grouped into a single options/config object. Function signatures simplified.
165
165
  `},{file:`SKILL.md`,content:`---
166
166
  name: lesson-learned
167
- description: "Analyze recent code changes via git history and extract software engineering lessons. Use when the user asks 'what is the lesson here?', 'what can I learn from this?', 'engineering takeaway', 'what did I just learn?', 'reflect on this code', or wants to extract principles from recent work."
167
+ description: "Analyze recent code changes via git history and extract software engineering lessons. Use when the user asks 'what is the lesson here?', 'what can I learn from this?', 'engineering takeaway', 'what did I just learn?', 'reflect on this code', 'what principles does this show?', or wants to extract actionable insights from recent work. Also use after completing any implementation task to capture learning."
168
168
  metadata:
169
169
  category: cross-cutting
170
170
  domain: general
@@ -177,17 +177,22 @@ metadata:
177
177
 
178
178
  # Lesson Learned
179
179
 
180
- Extract specific, grounded software engineering lessons from actual code changes. Not a lecture -- a mirror. Show the user what their code already demonstrates.
180
+ Extract specific, grounded software engineering lessons from actual code changes. Not a lecture -- a mirror. Show the user what their code already demonstrates, and only elevate lessons that are earned by the diff.
181
181
 
182
182
  ## Before You Begin
183
183
 
184
- **Load the principles reference first.**
184
+ - Determine the analysis scope first.
185
+ - Use the inline principles below for the common case.
186
+ - Read 'references/se-principles.md' only when the dominant pattern is unclear or you need a deeper principle match.
187
+ - Read 'references/anti-patterns.md' only when the diff suggests a gentle improvement opportunity.
185
188
 
186
- 1. Read \`references/se-principles.md\` to have the principle catalog available
187
- 2. Optionally read \`references/anti-patterns.md\` if you suspect the changes include areas for improvement
188
- 3. Determine the scope of analysis (see Phase 1)
189
+ ## Core Principles
189
190
 
190
- **Do not proceed until you've loaded at least \`se-principles.md\`.**
191
+ - **SRP** -- one module, one reason to change. Diff signal: a file/function was split because it was carrying mixed responsibilities.
192
+ - **Separation of Concerns** -- keep UI, domain, data, config, and orchestration in their own lanes. Diff signal: logic moved across a boundary into a more appropriate layer.
193
+ - **YAGNI** -- build only what current requirements justify. Diff signal: speculative abstractions, options, or branches were removed or avoided.
194
+ - **Fail Fast** -- detect bad input or invalid state at the boundary. Diff signal: guards, validation, assertions, or early returns were added near the entry point.
195
+ - **DRY** -- shared knowledge should live in one place. Diff signal: duplicated literals, branches, or helper logic were consolidated into one source.
191
196
 
192
197
  ## Phase 1: Determine Scope
193
198
 
@@ -207,23 +212,46 @@ Ask the user or infer from context what to analyze.
207
212
 
208
213
  ## Phase 2: Gather Changes
209
214
 
210
- 1. Run \`git log\` with the determined scope to get the commit list and messages
211
- 2. Run \`git diff\` for the full diff of the scope
212
- 3. If the diff is large (>500 lines), use \`git diff --stat\` first, then selectively read the top 3-5 most-changed files
213
- 4. **Read commit messages carefully** -- they contain intent that raw diffs miss
214
- 5. Only read changed files. Do not read the entire repo.
215
+ 1. Run \`git log\` with the determined scope to get commits and messages.
216
+ 2. Run \`git diff\` for the full diff of that scope.
217
+ 3. If the diff is large (>500 lines), start with \`git diff --stat\`, then inspect the 3-5 most-changed files.
218
+ 4. Read commit messages carefully -- they often reveal intent that raw diffs do not.
219
+ 5. Only read changed files. Do not widen scope beyond the diff.
220
+ 6. Capture concrete evidence as you go: commit SHA, file path, and line references.
215
221
 
216
222
  ## Phase 3: Analyze
217
223
 
218
- Identify the **dominant pattern** -- the single most instructive thing about these changes.
224
+ Apply this decision tree to find the dominant lesson:
219
225
 
220
- Look for:
221
- - **Structural decisions** -- How was the code organized? Why those boundaries?
222
- - **Trade-offs made** -- What was gained vs. sacrificed? (readability vs. performance, DRY vs. clarity, speed vs. correctness)
223
- - **Problems solved** -- What was the before/after? What made the "after" better?
224
- - **Missed opportunities** -- Where could the code improve? (present gently as "next time, consider...")
226
+ 1. **Is there a structural change?** (files split/merged, new module boundary, layer introduced)
227
+ -> Lesson is about architecture principles (SRP, Separation of Concerns, Cohesion)
225
228
 
226
- Map findings to specific principles from \`references/se-principles.md\`. Be specific -- quote actual code, reference actual file names and line changes.
229
+ 2. **Is there a removed or simplified path?** (deleted code, simplified conditionals, fewer dependencies)
230
+ -> Lesson is about simplicity principles (YAGNI, KISS, Premature Abstraction avoidance)
231
+
232
+ 3. **Is there error handling or edge case work?** (try/catch added, validation, fallbacks)
233
+ -> Lesson is about robustness principles (Fail Fast, Defensive Programming)
234
+
235
+ 4. **Is there duplication resolved?** (extracted shared logic, parameterized, DRY applied)
236
+ -> Lesson is about DRY and Rule of Three
237
+
238
+ 5. **Is there a naming or API change?** (renames, interface changes, contract adjustments)
239
+ -> Lesson is about Principle of Least Surprise and Encapsulation
240
+
241
+ 6. **None of the above clearly dominate?**
242
+ -> The changes may be routine. Check commit messages for unstated intent. If still nothing emerges, be honest that no deep lesson exists.
243
+
244
+ For the dominant pattern, map to the specific principle from \`references/se-principles.md\` when needed and cite the concrete code change that demonstrates it.
245
+
246
+ ## Quality Gate
247
+
248
+ A lesson is only worth presenting if ALL of these are true:
249
+ - **Non-obvious** -- someone reading the diff would not immediately see it without prompting
250
+ - **Actionable** -- it changes future behavior, not just "interesting observation"
251
+ - **Specific** -- cites actual file:line, not generic advice
252
+ - **Earned** -- emerges naturally from the code, not force-fit to a principle
253
+
254
+ If no lesson meets all four criteria, say so honestly: "These changes are solid execution -- no deep lesson here beyond good engineering practice."
227
255
 
228
256
  ## Phase 4: Present the Lesson
229
257
 
@@ -245,7 +273,7 @@ Use this template:
245
273
  [One concrete, actionable sentence the user can apply to future work]
246
274
  \`\`\`
247
275
 
248
- If there is a second lesson worth noting (maximum 2 additional):
276
+ If there is a second lesson worth noting, include at most one more:
249
277
 
250
278
  \`\`\`markdown
251
279
  ---
@@ -257,6 +285,8 @@ If there is a second lesson worth noting (maximum 2 additional):
257
285
  **Takeaway:** [1 sentence]
258
286
  \`\`\`
259
287
 
288
+ Do not present more than 2 lessons total. If the diff is mostly clean execution with no deeper pattern, say that directly.
289
+
260
290
  ## What NOT to Do
261
291
 
262
292
  | Avoid | Why | Instead |
@@ -266,7 +296,17 @@ If there is a second lesson worth noting (maximum 2 additional):
266
296
  | Ignoring commit messages | They contain intent that diffs miss | Read them as primary context |
267
297
  | Abstract advice disconnected from the code | Not actionable | Always reference specific files/lines |
268
298
  | Negative-only feedback | Demoralizing | Lead with what works, then suggest improvements |
269
- | More than 3 lessons | Dilutes the insight | One well-grounded lesson beats seven vague ones |
299
+ | More than 2 lessons | Dilutes the insight | One well-grounded lesson beats seven vague ones |
300
+
301
+ ## NEVER
302
+
303
+ - **NEVER present obvious refactoring as a lesson** ("the code got cleaner") -- this is table stakes, not insight
304
+ - **NEVER force-fit a principle** that does not naturally emerge from the diff -- if you have to stretch to connect code to principle, the connection is not there
305
+ - **NEVER produce more than 2 lessons** per analysis -- one well-grounded insight beats seven vague observations
306
+ - **NEVER use "best practice" without citing WHICH practice and WHY it is best HERE** -- "best practice" is the refuge of those who cannot explain the reasoning
307
+ - **NEVER analyze files that were not changed** -- scope creep dilutes focus
308
+ - **NEVER start with criticism** -- lead with what the code demonstrates well, then optionally note opportunities
309
+ - **NEVER produce a lesson without a concrete code reference** -- if you cannot point to a specific line or commit, the lesson is too abstract
270
310
 
271
311
  ## Conversation Style
272
312
 
@@ -22,18 +22,84 @@ metadata:
22
22
  - Optimizing React performance (re-renders, bundle size, data fetching)
23
23
  - Reviewing React code for correctness and best practices
24
24
 
25
+ ## Thinking Model
26
+
27
+ React is a **description language for UI**, not an imperative framework. Think in snapshots, not steps:
28
+ - "Given this state, what should the screen look like?" (declarative)
29
+ - NOT "When this happens, do these 5 things in order" (imperative)
30
+
31
+ The render function is a PURE function of (props, state) → JSX. Side effects live in clearly-marked boundaries (effects, event handlers, server actions).
32
+
33
+ **Server vs Client boundary** — the most common architectural mistake in modern React:
34
+ - Default to Server Components (zero JS shipped, direct data access)
35
+ - Add 'use client' only when you need: event handlers, useState/useEffect, browser APIs
36
+ - The boundary is a CONTRACT — server can render client, client CANNOT import server
37
+
38
+ Before coding, decide in this order:
39
+ 1. **What is pure UI derivation?** Keep it in render.
40
+ 2. **What is interaction state?** Keep it in the smallest client boundary.
41
+ 3. **What is data loading or mutation?** Prefer server.
42
+ 4. **What is side effect vs event response?** Effects synchronize with outside systems; event handlers respond to user intent.
43
+
44
+ ## Decision Flow
45
+
46
+ When implementing a React feature, follow this order:
47
+
48
+ 1. **Server or Client?**
49
+ - If it needs browser APIs, event handlers, or local interaction state → Client Component.
50
+ - Everything else starts as a Server Component.
51
+ - Keep the client boundary as small as possible: interactive leaf, server parent.
52
+
53
+ 2. **Derived state or stored state?**
54
+ - If a value can be computed from props/state during render, do NOT store it.
55
+ - Store only user input, async status, or state you cannot recompute cheaply or correctly.
56
+
57
+ 3. **Data loading location?**
58
+ - Initial fetch on server.
59
+ - Client refetch/cache only when data must stay live after hydration or respond to client events.
60
+
61
+ 4. **Mutation path?**
62
+ - Forms and mutations: Server Action + validation.
63
+ - Use \`useActionState\` for submission state/errors.
64
+ - Add \`useOptimistic\` only when instant feedback materially improves UX.
65
+
66
+ 5. **Effect or not?**
67
+ - If synchronizing with DOM, network subscription, timer, or external API → effect.
68
+ - If reacting to user input → event handler.
69
+ - If deriving view state → render logic, not effect.
70
+
71
+ 6. **Error and loading boundaries?**
72
+ - Rendering latency → \`<Suspense>\` with a real skeleton.
73
+ - Render failures → Error Boundary.
74
+ - Mutation failures → return structured errors from the Server Action.
75
+
76
+ 7. **Performance pass?**
77
+ - Start with correct boundaries and minimal client JS.
78
+ - Then rely on React Compiler if enabled.
79
+ - Only then add manual memoization, virtualization, or code splitting where profiling justifies it.
80
+
81
+ 8. **Extraction pass?**
82
+ - Business rules → hooks, utilities, or server functions.
83
+ - Components stay focused on describing UI.
84
+
85
+ ## NEVER
86
+
87
+ - **NEVER use \`useEffect\` for derived state** — if you can compute it from props/state, compute it inline or with \`useMemo\`. Effects for derivation cause extra renders and race conditions.
88
+ - **NEVER put business logic in components** — components are UI description, not business rules. Extract logic to hooks, utilities, or server functions.
89
+ - **NEVER use \`any\` to silence TypeScript in component props** — use \`unknown\` and narrow, or fix the actual type. \`any\` in props infects the entire component tree.
90
+ - **NEVER create a context for state that only one component reads** — prop drilling 2-3 levels is fine. Context adds re-render cost to EVERY consumer.
91
+ - **NEVER \`await\` inside \`useEffect\` directly** — use an inner async function or a data-fetching library. Direct await creates unhandled promise rejections and races.
92
+ - **NEVER conditionally call hooks** — hooks must be called in the same order every render. If you need conditional logic, move it INSIDE the hook.
93
+ - **NEVER spread props onto DOM elements without filtering** — \`{...props}\` passes unknown attributes to the DOM, causing console warnings and potential XSS vectors.
94
+ - **NEVER render a list without a stable key** — index-as-key causes state corruption on reorder. Use a domain ID or generate a stable key from content.
95
+
25
96
  ## React 19 Quick Reference
26
97
 
27
- | API | Use Case | Key Change |
28
- |-----|----------|------------|
29
- | \`ref\` as prop | Access DOM/component instance | No more \`forwardRef\` wrapper |
30
- | \`use()\` | Read promises/context in render | Replaces \`useContext\`, enables async |
31
- | \`useActionState\` | Form submission state + error | Replaces \`useFormState\` |
32
- | \`useOptimistic\` | Instant UI feedback | Optimistic updates before server confirms |
33
- | \`useTransition\` | Non-urgent state updates | \`isPending\` for loading states |
34
- | \`<form action>\` | Server Actions in forms | Direct server function binding |
35
- | Server Components | Zero-bundle components | Default in App Router, no \`"use client"\` |
36
- | Server Actions | Server mutations | \`"use server"\` functions, form actions |
98
+ - \`ref\` as prop no \`forwardRef\` wrapper for common cases
99
+ - \`use()\` — read promises/context during render
100
+ - \`useActionState\` + \`<form action>\` form mutations with server actions
101
+ - \`useOptimistic\` + \`useTransition\` optimistic and non-urgent updates
102
+ - Server Components / Server Actions default to server, opt into client only when needed
37
103
 
38
104
  ## Component Patterns
39
105
 
@@ -77,56 +143,36 @@ function Table<T extends Record<string, unknown>>({ data, columns, onRowClick }:
77
143
  \`\`\`
78
144
 
79
145
  ### Children Typing
80
- \`\`\`tsx
81
- // Specific children
82
- type Props = { children: React.ReactElement<TabProps>[] };
83
-
84
- // Render function children
85
- type Props = { children: (data: T) => React.ReactNode };
86
-
87
- // String only
88
- type Props = { children: string };
89
- \`\`\`
146
+ - Specific children: \`React.ReactElement<TabProps>[]\`
147
+ - Render prop: \`(data: T) => React.ReactNode\`
148
+ - Text-only API: \`string\`
90
149
 
91
150
  ## Ref as Prop (React 19)
92
151
 
93
152
  \`\`\`tsx
94
- // React 19: pass ref directly, no forwardRef needed
95
153
  function Input({ ref, ...props }: React.ComponentProps<"input">) {
96
154
  return <input ref={ref} {...props} />;
97
155
  }
98
-
99
- // Cleanup function (new in React 19)
100
- function MeasuredDiv({ ref, ...props }: React.ComponentProps<"div">) {
101
- return (
102
- <div
103
- ref={(node) => {
104
- if (node) measureElement(node);
105
- return () => cleanupMeasurement(); // cleanup on unmount
106
- }}
107
- {...props}
108
- />
109
- );
110
- }
111
156
  \`\`\`
112
157
 
158
+ Callback refs can return cleanup functions in React 19. Use that for measurement/subscription teardown instead of scattering cleanup elsewhere.
159
+
113
160
  ## Server Components & Actions
114
161
 
115
162
  ### Server Component (default — no directive needed)
116
163
  \`\`\`tsx
117
- // app/users/page.tsx — Server Component (default)
118
164
  import { db } from "@/lib/db";
119
165
 
120
166
  export default async function UsersPage() {
121
- const users = await db.query.users.findMany(); // Direct DB access
122
- return <UserList users={users} />; // Pass data to client
167
+ const users = await db.query.users.findMany();
168
+ return <UserList users={users} />;
123
169
  }
124
170
  \`\`\`
125
171
 
126
172
  ### Client Component (explicit opt-in)
127
173
  \`\`\`tsx
128
174
  "use client";
129
- import { useState, useOptimistic, useTransition } from "react";
175
+ import { useState } from "react";
130
176
 
131
177
  export function Counter({ initialCount }: { initialCount: number }) {
132
178
  const [count, setCount] = useState(initialCount);
@@ -136,7 +182,6 @@ export function Counter({ initialCount }: { initialCount: number }) {
136
182
 
137
183
  ### Server Action with Form
138
184
  \`\`\`tsx
139
- // actions.ts
140
185
  "use server";
141
186
  import { z } from "zod";
142
187
 
@@ -145,7 +190,7 @@ const schema = z.object({ name: z.string().min(1), email: z.string().email() });
145
190
  export async function createUser(_prev: unknown, formData: FormData) {
146
191
  const result = schema.safeParse(Object.fromEntries(formData));
147
192
  if (!result.success) return { error: result.error.flatten().fieldErrors };
148
- await db.insert(users).values(result.data);
193
+ await saveUser(result.data);
149
194
  return { success: true };
150
195
  }
151
196
  \`\`\`
@@ -160,9 +205,7 @@ export function CreateUserForm() {
160
205
  return (
161
206
  <form action={action}>
162
207
  <input name="name" required />
163
- {state?.error?.name && <p className="text-red-500">{state.error.name}</p>}
164
208
  <input name="email" type="email" required />
165
- {state?.error?.email && <p className="text-red-500">{state.error.email}</p>}
166
209
  <button disabled={isPending}>{isPending ? "Creating..." : "Create"}</button>
167
210
  </form>
168
211
  );
@@ -175,10 +218,10 @@ export function CreateUserForm() {
175
218
  import { useOptimistic } from "react";
176
219
 
177
220
  function TodoList({ todos, addTodo }: { todos: Todo[]; addTodo: (text: string) => Promise<void> }) {
178
- const [optimisticTodos, addOptimistic] = useOptimistic(
179
- todos,
180
- (state, newTodo: string) => [...state, { id: "temp", text: newTodo, pending: true }]
181
- );
221
+ const [optimisticTodos, addOptimistic] = useOptimistic(todos, (state, text: string) => [
222
+ ...state,
223
+ { id: "temp", text, pending: true },
224
+ ]);
182
225
 
183
226
  async function handleAdd(formData: FormData) {
184
227
  const text = formData.get("text") as string;
@@ -202,109 +245,33 @@ function TodoList({ todos, addTodo }: { todos: Todo[]; addTodo: (text: string) =
202
245
 
203
246
  ## Event Handler Types
204
247
 
205
- \`\`\`tsx
206
- // Always use specific event types
207
- onClick: (e: React.MouseEvent<HTMLButtonElement>) => void
208
- onChange: (e: React.ChangeEvent<HTMLInputElement>) => void
209
- onSubmit: (e: React.FormEvent<HTMLFormElement>) => void
210
- onKeyDown: (e: React.KeyboardEvent<HTMLInputElement>) => void
211
- onFocus: (e: React.FocusEvent<HTMLInputElement>) => void
212
- onDrag: (e: React.DragEvent<HTMLDivElement>) => void
213
- \`\`\`
248
+ Prefer exact DOM event types at the boundary: \`MouseEvent<HTMLButtonElement>\`, \`ChangeEvent<HTMLInputElement>\`, \`FormEvent<HTMLFormElement>\`. If you start with a broad \`SyntheticEvent\`, you are probably typing too late.
214
249
 
215
250
  ## Hooks Typing
216
251
 
217
- \`\`\`tsx
218
- // useState with union
219
- const [status, setStatus] = useState<"idle" | "loading" | "error">("idle");
220
-
221
- // useRef for DOM pass null, get RefObject
222
- const inputRef = useRef<HTMLInputElement>(null);
223
-
224
- // useRef for mutable value — no null, get MutableRefObject
225
- const intervalRef = useRef<number>(0);
226
-
227
- // useReducer with discriminated actions
228
- type Action = { type: "increment" } | { type: "set"; payload: number };
229
- const [count, dispatch] = useReducer((state: number, action: Action) => {
230
- switch (action.type) {
231
- case "increment": return state + 1;
232
- case "set": return action.payload;
233
- }
234
- }, 0);
235
-
236
- // Custom hook: return as const for tuple types
237
- function useToggle(initial = false) {
238
- const [value, setValue] = useState(initial);
239
- const toggle = useCallback(() => setValue(v => !v), []);
240
- return [value, toggle] as const;
241
- }
242
-
243
- // useContext with null guard
244
- const ctx = useContext(ThemeContext);
245
- if (!ctx) throw new Error("useTheme must be used within ThemeProvider");
246
- \`\`\`
252
+ - \`useState\` — model UI state with explicit unions, not booleans that drift out of sync
253
+ - \`useRef<HTMLInputElement>(null)\` for DOM refs; mutable refs only for non-render state
254
+ - \`useReducer\` discriminated union actions when state transitions are non-trivial
255
+ - Custom hooks returning tuples should use \`as const\`
256
+ - Context consumers should null-guard and fail fast outside their provider
247
257
 
248
258
  ## Performance Rules
249
259
 
250
260
  ### Prevent Async Waterfalls
251
261
  \`\`\`tsx
252
- // BAD — sequential fetching
253
262
  const user = await getUser(id);
254
- const posts = await getPosts(user.id); // Waits for user first
255
- const comments = await getComments(posts[0].id); // Waits for posts
263
+ const posts = await getPosts(user.id);
256
264
 
257
- // GOOD — parallel where possible
258
265
  const [user, posts] = await Promise.all([getUser(id), getPosts(id)]);
259
266
  \`\`\`
260
267
 
261
- ### Bundle Size
262
- - \`dynamic(() => import("./HeavyComponent"))\` lazy load below-fold components
263
- - Tree-shake: use named exports, avoid \`import *\`
264
- - Analyze bundle: \`@next/bundle-analyzer\` to find bloat
265
- - Code-split by route each page loads only its own JS
266
- - Avoid importing entire libraries: \`import { debounce } from "lodash-es"\` not \`import _ from "lodash"\`
267
-
268
- ### Server-Side Performance
269
- - Default to Server Components — zero JS shipped to client
270
- - Stream with Suspense: wrap slow data fetchers in \`<Suspense fallback={<Skeleton />}>\`
271
- - Cache expensive computations with \`"use cache"\` directive
272
- - Avoid serializing large objects across server/client boundary
273
-
274
- ### Client-Side Data Fetching
275
- - Use SWR or React Query — automatic caching, revalidation, error retry
276
- - Stale-while-revalidate: show cached data instantly, refresh in background
277
- - Error boundaries: \`<ErrorBoundary>\` wrapping data-dependent trees
278
- - Prefetch on hover/viewport for likely next navigations
279
-
280
- ### Re-render Optimization
281
- - React Compiler (React 19): auto-memoizes — try this first before manual \`useMemo\`/\`useCallback\`
282
- - If no compiler: \`useMemo\` for expensive computations, \`useCallback\` for stable function references
283
- - Extract static JSX outside components — constants don't re-create
284
- - Split context: separate frequently-changing state from rarely-changing state
285
- - \`React.memo()\` on components receiving only primitive props
286
-
287
- ### Rendering Performance
288
- - **Virtualize** lists > 100 items: \`@tanstack/react-virtual\`, \`react-window\`
289
- - Defer heavy computations with \`useDeferredValue\`
290
- - Batch state updates (React 18+ does this automatically in event handlers)
291
- - Avoid reading DOM layout during renders (force sync layout = jank)
292
-
293
- ### Advanced Patterns
294
- - Progressive hydration: load and hydrate components as they enter viewport
295
- - Concurrent features: \`useTransition\` for non-blocking state updates
296
- - Suspense caching: combine \`<Suspense>\` with data cache for instant page transitions
297
- - React Compiler adoption: enable gradually, check for unsafe patterns first
298
-
299
- ## Decision Flow
300
-
301
- When implementing a React feature:
268
+ ### Performance Checklist
269
+ - Server Components first; ship less JS before optimizing JS
270
+ - \`<Suspense>\` + streaming for slow server work
271
+ - SWR/React Query when client data must stay fresh after hydration
272
+ - React Compiler first, then manual \`useMemo\`/\`useCallback\` only when profiling proves need
273
+ - Virtualize lists >100 items; defer heavy client work with \`useDeferredValue\`
274
+ - Lazy load below-fold components; prefer named imports for tree-shaking
275
+ - Avoid passing large objects across the server/client boundary
302
276
 
303
- 1. **Server or Client?** → If it uses browser APIs, event handlers, or useState → Client. Everything else → Server.
304
- 2. **Data fetching?** → Server Component for initial data. SWR/React Query for client-side mutations.
305
- 3. **Form?** → Server Action + \`useActionState\` + Zod validation.
306
- 4. **Optimistic?** → \`useOptimistic\` for instant feedback.
307
- 5. **Loading?** → \`<Suspense>\` with skeleton fallback.
308
- 6. **Performance?** → Try React Compiler first. Then manual memo. Then virtualization. Then code splitting.
309
- 7. **Error?** → Error boundaries for rendering errors. try/catch for Server Actions.
310
277
  `}];export{e as default};
@@ -1,6 +1,6 @@
1
1
  var e=[{file:`SKILL.md`,content:`---
2
2
  name: requirements-clarity
3
- description: Clarify ambiguous requirements through focused dialogue before implementation. Use when requirements are unclear, features are complex (>2 days), or involve cross-team coordination. Ask two core questions - Why? (YAGNI check) and Simpler? (KISS check) - to ensure clarity before coding.
3
+ description: Clarify ambiguous requirements through focused dialogue before implementation. Use when requirements are unclear, features are complex (>2 days), involve cross-team coordination, or when the user says 'I want to build...', 'add a feature for...', 'implement...', 'create a system that...'. Ask two core questions first - Why? (YAGNI check) and Simpler? (KISS check) - then score 0-100 until clarity reaches 90+. Critical for preventing rework on underspecified features.
4
4
  metadata:
5
5
  category: cross-cutting
6
6
  domain: general
@@ -20,6 +20,17 @@ Do NOT activate when:
20
20
  - Bug fixes with clear reproduction steps
21
21
  - Task is a single-file change with obvious scope
22
22
 
23
+ ## Mindset
24
+
25
+ Requirements engineering is detective work, not stenography. Your job is NOT to write down what the user says - it's to uncover what they haven't said yet.
26
+
27
+ The user knows their PROBLEM but often conflates it with a specific SOLUTION. Separate these. The problem is the requirement; the solution is a design decision.
28
+
29
+ Before asking questions, mentally model:
30
+ - **Stakeholders** - Who else cares? Who gets paged at 2am? Who pays?
31
+ - **Failure modes** - What breaks? What's the blast radius? What's unrecoverable?
32
+ - **Boundaries** - What's explicitly OUT of scope? What's "phase 2"?
33
+
23
34
  ## Protocol
24
35
 
25
36
  ### 1. YAGNI/KISS Gate
@@ -41,7 +52,39 @@ Assess the requirement against this rubric:
41
52
  | Implementation Completeness | /25 | Edge cases (8), error handling (9), data validation (8) |
42
53
  | Business Context | /20 | Problem statement (7), target users (7), success metrics (6) |
43
54
 
44
- If you present this rubric or a score update to the user, use \`present({ schemaVersion: 1, title: 'Requirements Clarity', blocks: [...] })\` with a \`metrics\` block for category scores and \`markdown\` or \`list\` blocks for follow-up notes. NEVER use \`code\` blocks for structured data or JSON.stringify() score payloads into markdown/code blocks.
55
+ ### Calibration Examples
56
+
57
+ **Functional Clarity 10/10:**
58
+ "User clicks Submit -> system validates email format (RFC 5322), checks uniqueness against users table, returns 201 with serialized user object (id, email, createdAt) or 422 with field-level errors array [{field, code, message}]"
59
+
60
+ **Functional Clarity 3/10:**
61
+ "User can sign up"
62
+
63
+ **Technical Specificity 9/9 (constraints):**
64
+ "Response time < 200ms p95 for all CRUD endpoints, PostgreSQL 16+, horizontal scaling via stateless workers behind ALB, no server-side sessions"
65
+
66
+ **Technical Specificity 2/9:**
67
+ "Should be fast and scalable"
68
+
69
+ **Edge Cases 8/8:**
70
+ "Empty input -> show inline validation before submit. Concurrent duplicate emails -> second request gets 409 Conflict. Network timeout -> client retries 3x with exponential backoff, then shows offline banner"
71
+
72
+ **Edge Cases 2/8:**
73
+ "Handle errors appropriately"
74
+
75
+ ### Expert Probing Techniques
76
+
77
+ For each gap category, ask focused questions that reveal hidden requirements:
78
+
79
+ **For edge cases:** Ask about empty states, concurrent access, partial failures, permission boundaries, rate limits, timezone/locale behavior, offline/degraded mode, data migration from existing state.
80
+
81
+ **For error handling:** Ask "What happens when [external dependency] is down?", "What does the user see during a 30-second timeout?", "How does the system recover from partial writes?"
82
+
83
+ **For integration points:** Ask "Who calls this?", "What calls does this make?", "What happens if the downstream service changes its contract?", "Is there a circuit breaker?"
84
+
85
+ **For data validation:** Ask "What's the maximum size?", "What characters are allowed?", "What about Unicode/emoji?", "What happens with 0, null, negative numbers, MAX_INT?"
86
+
87
+ **For business context:** Ask "Who loses money if this breaks?", "What's the rollback plan?", "How will you know this is working in production?"
45
88
 
46
89
  ### 3. Iterate Until ≥ 90
47
90
 
@@ -57,6 +100,16 @@ Prefer short, targeted questions over long questionnaires. Build on previous ans
57
100
 
58
101
  **HARD RULE:** Do not generate the requirements document with a score below 90.
59
102
 
103
+ ## NEVER
104
+
105
+ - **NEVER accept vague acceptance criteria** like "it should work" or "user-friendly" - demand measurable outcomes (response time, error rate, specific user action -> specific system response)
106
+ - **NEVER skip the YAGNI check** - 40% of requirements are premature. Ask "what happens if we ship without this?" first
107
+ - **NEVER ask more than 3 questions per round** - cognitive overload causes shallow answers. Depth > breadth.
108
+ - **NEVER accept "edge cases: handle errors appropriately"** - this means "I haven't thought about it." Push for specific scenarios.
109
+ - **NEVER generate the document below score 90** - incomplete requirements cause more rework than taking time to clarify
110
+ - **NEVER repeat questions the user already answered** - build on previous responses, reference what they said
111
+ - **NEVER score above 80 without concrete acceptance criteria** - if you can't write a test for it, it's not a requirement
112
+
60
113
  ### 4. Generate Requirements Document
61
114
 
62
115
  **Output path (flow-aware):**