beads-orchestration 2.0.0
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.
- package/LICENSE +21 -0
- package/README.md +214 -0
- package/SKILL.md +263 -0
- package/bootstrap.py +928 -0
- package/package.json +37 -0
- package/scripts/cli.js +64 -0
- package/scripts/postinstall.js +71 -0
- package/skills/create-beads-orchestration/SKILL.md +263 -0
- package/skills/subagents-discipline/SKILL.md +158 -0
- package/templates/CLAUDE.md +326 -0
- package/templates/agents/architect.md +121 -0
- package/templates/agents/code-reviewer.md +248 -0
- package/templates/agents/detective.md +101 -0
- package/templates/agents/discovery.md +492 -0
- package/templates/agents/merge-supervisor.md +119 -0
- package/templates/agents/scout.md +100 -0
- package/templates/agents/scribe.md +96 -0
- package/templates/beads-workflow-injection-api.md +116 -0
- package/templates/beads-workflow-injection-git.md +108 -0
- package/templates/beads-workflow-injection.md +111 -0
- package/templates/frontend-reviews-requirement.md +61 -0
- package/templates/hooks/block-orchestrator-tools.sh +98 -0
- package/templates/hooks/clarify-vague-request.sh +39 -0
- package/templates/hooks/enforce-bead-for-supervisor.sh +32 -0
- package/templates/hooks/enforce-branch-before-edit.sh +47 -0
- package/templates/hooks/enforce-concise-response.sh +41 -0
- package/templates/hooks/enforce-sequential-dispatch.sh +63 -0
- package/templates/hooks/inject-discipline-reminder.sh +28 -0
- package/templates/hooks/log-dispatch-prompt.sh +39 -0
- package/templates/hooks/memory-capture.sh +104 -0
- package/templates/hooks/remind-inprogress.sh +14 -0
- package/templates/hooks/session-start.sh +121 -0
- package/templates/hooks/validate-completion.sh +131 -0
- package/templates/hooks/validate-epic-close.sh +84 -0
- package/templates/mcp.json.template +12 -0
- package/templates/memory/recall.sh +121 -0
- package/templates/settings.json +74 -0
- package/templates/skills/react-best-practices/SKILL.md +487 -0
- package/templates/skills/subagents-discipline/SKILL.md +127 -0
- package/templates/ui-constraints.md +76 -0
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: react-best-practices
|
|
3
|
+
description: React and Next.js performance optimization patterns. Use BEFORE implementing any React code to ensure best practices are followed.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# React Best Practices
|
|
7
|
+
|
|
8
|
+
**Version 1.0.0**
|
|
9
|
+
Source: Vercel Engineering (vercel-labs/agent-skills)
|
|
10
|
+
|
|
11
|
+
> **Note:**
|
|
12
|
+
> This document is for agents and LLMs to follow when maintaining,
|
|
13
|
+
> generating, or refactoring React and Next.js codebases. Contains 40+ rules across 8 categories, prioritized by impact.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## How to Use This Skill
|
|
18
|
+
|
|
19
|
+
**Before implementing ANY React/Next.js code:**
|
|
20
|
+
|
|
21
|
+
1. Review the relevant sections based on what you're building
|
|
22
|
+
2. Apply the patterns as you write code
|
|
23
|
+
3. Use the "Incorrect" vs "Correct" examples as templates
|
|
24
|
+
|
|
25
|
+
**Priority order:** Eliminating Waterfalls > Bundle Size > Server-Side > Client-Side > Re-renders > Rendering > JS Perf > Advanced
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Quick Reference: Critical Rules
|
|
30
|
+
|
|
31
|
+
### Top 5 Rules (Always Apply)
|
|
32
|
+
|
|
33
|
+
1. **Promise.all() for independent operations** - Never sequential awaits for independent data
|
|
34
|
+
2. **Avoid barrel file imports** - Import directly from source files
|
|
35
|
+
3. **Dynamic imports for heavy components** - Lazy-load Monaco, charts, etc.
|
|
36
|
+
4. **Parallel data fetching with component composition** - Structure RSC for parallelism
|
|
37
|
+
5. **Minimize serialization at RSC boundaries** - Only pass needed fields to client
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 1. Eliminating Waterfalls
|
|
42
|
+
|
|
43
|
+
**Impact: CRITICAL** - Waterfalls are the #1 performance killer.
|
|
44
|
+
|
|
45
|
+
### 1.1 Defer Await Until Needed
|
|
46
|
+
|
|
47
|
+
Move `await` into branches where actually used.
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
// BAD: blocks both branches
|
|
51
|
+
async function handleRequest(userId: string, skipProcessing: boolean) {
|
|
52
|
+
const userData = await fetchUserData(userId)
|
|
53
|
+
if (skipProcessing) return { skipped: true }
|
|
54
|
+
return processUserData(userData)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// GOOD: only blocks when needed
|
|
58
|
+
async function handleRequest(userId: string, skipProcessing: boolean) {
|
|
59
|
+
if (skipProcessing) return { skipped: true }
|
|
60
|
+
const userData = await fetchUserData(userId)
|
|
61
|
+
return processUserData(userData)
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 1.2 Promise.all() for Independent Operations
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
// BAD: 3 round trips
|
|
69
|
+
const user = await fetchUser()
|
|
70
|
+
const posts = await fetchPosts()
|
|
71
|
+
const comments = await fetchComments()
|
|
72
|
+
|
|
73
|
+
// GOOD: 1 round trip
|
|
74
|
+
const [user, posts, comments] = await Promise.all([
|
|
75
|
+
fetchUser(),
|
|
76
|
+
fetchPosts(),
|
|
77
|
+
fetchComments()
|
|
78
|
+
])
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 1.3 Strategic Suspense Boundaries
|
|
82
|
+
|
|
83
|
+
```tsx
|
|
84
|
+
// BAD: wrapper blocked by data
|
|
85
|
+
async function Page() {
|
|
86
|
+
const data = await fetchData()
|
|
87
|
+
return (
|
|
88
|
+
<div>
|
|
89
|
+
<Sidebar />
|
|
90
|
+
<DataDisplay data={data} />
|
|
91
|
+
<Footer />
|
|
92
|
+
</div>
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// GOOD: wrapper shows immediately
|
|
97
|
+
function Page() {
|
|
98
|
+
return (
|
|
99
|
+
<div>
|
|
100
|
+
<Sidebar />
|
|
101
|
+
<Suspense fallback={<Skeleton />}>
|
|
102
|
+
<DataDisplay />
|
|
103
|
+
</Suspense>
|
|
104
|
+
<Footer />
|
|
105
|
+
</div>
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 2. Bundle Size Optimization
|
|
113
|
+
|
|
114
|
+
**Impact: CRITICAL** - Reduces TTI and LCP.
|
|
115
|
+
|
|
116
|
+
### 2.1 Avoid Barrel File Imports
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
// BAD: loads 1,583 modules
|
|
120
|
+
import { Check, X, Menu } from 'lucide-react'
|
|
121
|
+
|
|
122
|
+
// GOOD: loads only 3 modules
|
|
123
|
+
import Check from 'lucide-react/dist/esm/icons/check'
|
|
124
|
+
import X from 'lucide-react/dist/esm/icons/x'
|
|
125
|
+
import Menu from 'lucide-react/dist/esm/icons/menu'
|
|
126
|
+
|
|
127
|
+
// ALTERNATIVE: Next.js 13.5+ config
|
|
128
|
+
// next.config.js
|
|
129
|
+
module.exports = {
|
|
130
|
+
experimental: {
|
|
131
|
+
optimizePackageImports: ['lucide-react', '@mui/material']
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 2.2 Dynamic Imports for Heavy Components
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
// BAD: Monaco bundles with main chunk (~300KB)
|
|
140
|
+
import { MonacoEditor } from './monaco-editor'
|
|
141
|
+
|
|
142
|
+
// GOOD: Monaco loads on demand
|
|
143
|
+
import dynamic from 'next/dynamic'
|
|
144
|
+
const MonacoEditor = dynamic(
|
|
145
|
+
() => import('./monaco-editor').then(m => m.MonacoEditor),
|
|
146
|
+
{ ssr: false }
|
|
147
|
+
)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### 2.3 Defer Non-Critical Libraries
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
// BAD: blocks initial bundle
|
|
154
|
+
import { Analytics } from '@vercel/analytics/react'
|
|
155
|
+
|
|
156
|
+
// GOOD: loads after hydration
|
|
157
|
+
import dynamic from 'next/dynamic'
|
|
158
|
+
const Analytics = dynamic(
|
|
159
|
+
() => import('@vercel/analytics/react').then(m => m.Analytics),
|
|
160
|
+
{ ssr: false }
|
|
161
|
+
)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### 2.4 Preload on User Intent
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
function EditorButton({ onClick }: { onClick: () => void }) {
|
|
168
|
+
const preload = () => {
|
|
169
|
+
if (typeof window !== 'undefined') {
|
|
170
|
+
void import('./monaco-editor')
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return (
|
|
174
|
+
<button onMouseEnter={preload} onFocus={preload} onClick={onClick}>
|
|
175
|
+
Open Editor
|
|
176
|
+
</button>
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 3. Server-Side Performance
|
|
184
|
+
|
|
185
|
+
**Impact: HIGH**
|
|
186
|
+
|
|
187
|
+
### 3.1 Minimize Serialization at RSC Boundaries
|
|
188
|
+
|
|
189
|
+
```tsx
|
|
190
|
+
// BAD: serializes all 50 fields
|
|
191
|
+
async function Page() {
|
|
192
|
+
const user = await fetchUser() // 50 fields
|
|
193
|
+
return <Profile user={user} />
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// GOOD: serializes only needed fields
|
|
197
|
+
async function Page() {
|
|
198
|
+
const user = await fetchUser()
|
|
199
|
+
return <Profile name={user.name} avatar={user.avatar} />
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### 3.2 Parallel Data Fetching with Component Composition
|
|
204
|
+
|
|
205
|
+
```tsx
|
|
206
|
+
// BAD: Sidebar waits for Header's fetch
|
|
207
|
+
export default async function Page() {
|
|
208
|
+
const header = await fetchHeader()
|
|
209
|
+
return (
|
|
210
|
+
<div>
|
|
211
|
+
<div>{header}</div>
|
|
212
|
+
<Sidebar />
|
|
213
|
+
</div>
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// GOOD: both fetch simultaneously
|
|
218
|
+
async function Header() {
|
|
219
|
+
const data = await fetchHeader()
|
|
220
|
+
return <div>{data}</div>
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async function Sidebar() {
|
|
224
|
+
const items = await fetchSidebarItems()
|
|
225
|
+
return <nav>{items.map(renderItem)}</nav>
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export default function Page() {
|
|
229
|
+
return (
|
|
230
|
+
<div>
|
|
231
|
+
<Header />
|
|
232
|
+
<Sidebar />
|
|
233
|
+
</div>
|
|
234
|
+
)
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### 3.3 Per-Request Deduplication with React.cache()
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
import { cache } from 'react'
|
|
242
|
+
|
|
243
|
+
export const getCurrentUser = cache(async () => {
|
|
244
|
+
const session = await auth()
|
|
245
|
+
if (!session?.user?.id) return null
|
|
246
|
+
return await db.user.findUnique({ where: { id: session.user.id } })
|
|
247
|
+
})
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### 3.4 Use after() for Non-Blocking Operations
|
|
251
|
+
|
|
252
|
+
```tsx
|
|
253
|
+
import { after } from 'next/server'
|
|
254
|
+
|
|
255
|
+
export async function POST(request: Request) {
|
|
256
|
+
await updateDatabase(request)
|
|
257
|
+
|
|
258
|
+
// Log after response is sent
|
|
259
|
+
after(async () => {
|
|
260
|
+
const userAgent = (await headers()).get('user-agent')
|
|
261
|
+
logUserAction({ userAgent })
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
return Response.json({ status: 'success' })
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## 4. Client-Side Data Fetching
|
|
271
|
+
|
|
272
|
+
**Impact: MEDIUM-HIGH**
|
|
273
|
+
|
|
274
|
+
### 4.1 Use SWR for Automatic Deduplication
|
|
275
|
+
|
|
276
|
+
```tsx
|
|
277
|
+
// BAD: no deduplication
|
|
278
|
+
function UserList() {
|
|
279
|
+
const [users, setUsers] = useState([])
|
|
280
|
+
useEffect(() => {
|
|
281
|
+
fetch('/api/users').then(r => r.json()).then(setUsers)
|
|
282
|
+
}, [])
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// GOOD: multiple instances share one request
|
|
286
|
+
import useSWR from 'swr'
|
|
287
|
+
function UserList() {
|
|
288
|
+
const { data: users } = useSWR('/api/users', fetcher)
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
---
|
|
293
|
+
|
|
294
|
+
## 5. Re-render Optimization
|
|
295
|
+
|
|
296
|
+
**Impact: MEDIUM**
|
|
297
|
+
|
|
298
|
+
### 5.1 Use Functional setState Updates
|
|
299
|
+
|
|
300
|
+
```tsx
|
|
301
|
+
// BAD: requires state as dependency, risk of stale closure
|
|
302
|
+
const addItems = useCallback((newItems: Item[]) => {
|
|
303
|
+
setItems([...items, ...newItems])
|
|
304
|
+
}, [items])
|
|
305
|
+
|
|
306
|
+
// GOOD: stable callback, no stale closures
|
|
307
|
+
const addItems = useCallback((newItems: Item[]) => {
|
|
308
|
+
setItems(curr => [...curr, ...newItems])
|
|
309
|
+
}, [])
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### 5.2 Use Lazy State Initialization
|
|
313
|
+
|
|
314
|
+
```tsx
|
|
315
|
+
// BAD: runs on every render
|
|
316
|
+
const [settings] = useState(JSON.parse(localStorage.getItem('settings') || '{}'))
|
|
317
|
+
|
|
318
|
+
// GOOD: runs only once
|
|
319
|
+
const [settings] = useState(() => {
|
|
320
|
+
const stored = localStorage.getItem('settings')
|
|
321
|
+
return stored ? JSON.parse(stored) : {}
|
|
322
|
+
})
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### 5.3 Use Transitions for Non-Urgent Updates
|
|
326
|
+
|
|
327
|
+
```tsx
|
|
328
|
+
import { startTransition } from 'react'
|
|
329
|
+
|
|
330
|
+
function ScrollTracker() {
|
|
331
|
+
const [scrollY, setScrollY] = useState(0)
|
|
332
|
+
useEffect(() => {
|
|
333
|
+
const handler = () => {
|
|
334
|
+
startTransition(() => setScrollY(window.scrollY))
|
|
335
|
+
}
|
|
336
|
+
window.addEventListener('scroll', handler, { passive: true })
|
|
337
|
+
return () => window.removeEventListener('scroll', handler)
|
|
338
|
+
}, [])
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### 5.4 Narrow Effect Dependencies
|
|
343
|
+
|
|
344
|
+
```tsx
|
|
345
|
+
// BAD: re-runs on any user field change
|
|
346
|
+
useEffect(() => {
|
|
347
|
+
console.log(user.id)
|
|
348
|
+
}, [user])
|
|
349
|
+
|
|
350
|
+
// GOOD: re-runs only when id changes
|
|
351
|
+
useEffect(() => {
|
|
352
|
+
console.log(user.id)
|
|
353
|
+
}, [user.id])
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## 6. Rendering Performance
|
|
359
|
+
|
|
360
|
+
**Impact: MEDIUM**
|
|
361
|
+
|
|
362
|
+
### 6.1 CSS content-visibility for Long Lists
|
|
363
|
+
|
|
364
|
+
```css
|
|
365
|
+
.message-item {
|
|
366
|
+
content-visibility: auto;
|
|
367
|
+
contain-intrinsic-size: 0 80px;
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### 6.2 Hoist Static JSX Elements
|
|
372
|
+
|
|
373
|
+
```tsx
|
|
374
|
+
// BAD: recreates element every render
|
|
375
|
+
function Container() {
|
|
376
|
+
return loading && <div className="animate-pulse h-20 bg-gray-200" />
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// GOOD: reuses same element
|
|
380
|
+
const loadingSkeleton = <div className="animate-pulse h-20 bg-gray-200" />
|
|
381
|
+
function Container() {
|
|
382
|
+
return loading && loadingSkeleton
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### 6.3 Animate SVG Wrapper, Not SVG Element
|
|
387
|
+
|
|
388
|
+
```tsx
|
|
389
|
+
// BAD: no hardware acceleration
|
|
390
|
+
<svg className="animate-spin">...</svg>
|
|
391
|
+
|
|
392
|
+
// GOOD: hardware accelerated
|
|
393
|
+
<div className="animate-spin">
|
|
394
|
+
<svg>...</svg>
|
|
395
|
+
</div>
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
## 7. JavaScript Performance
|
|
401
|
+
|
|
402
|
+
**Impact: LOW-MEDIUM**
|
|
403
|
+
|
|
404
|
+
### 7.1 Build Index Maps for Repeated Lookups
|
|
405
|
+
|
|
406
|
+
```typescript
|
|
407
|
+
// BAD: O(n) per lookup
|
|
408
|
+
items.filter(item => allowedIds.includes(item.id))
|
|
409
|
+
|
|
410
|
+
// GOOD: O(1) per lookup
|
|
411
|
+
const allowedSet = new Set(allowedIds)
|
|
412
|
+
items.filter(item => allowedSet.has(item.id))
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### 7.2 Use toSorted() Instead of sort()
|
|
416
|
+
|
|
417
|
+
```typescript
|
|
418
|
+
// BAD: mutates original array
|
|
419
|
+
const sorted = users.sort((a, b) => a.name.localeCompare(b.name))
|
|
420
|
+
|
|
421
|
+
// GOOD: creates new array
|
|
422
|
+
const sorted = users.toSorted((a, b) => a.name.localeCompare(b.name))
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### 7.3 Early Return from Functions
|
|
426
|
+
|
|
427
|
+
```typescript
|
|
428
|
+
// BAD: processes all items after finding error
|
|
429
|
+
function validateUsers(users: User[]) {
|
|
430
|
+
let hasError = false
|
|
431
|
+
for (const user of users) {
|
|
432
|
+
if (!user.email) hasError = true
|
|
433
|
+
}
|
|
434
|
+
return hasError ? { valid: false } : { valid: true }
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// GOOD: returns immediately on first error
|
|
438
|
+
function validateUsers(users: User[]) {
|
|
439
|
+
for (const user of users) {
|
|
440
|
+
if (!user.email) return { valid: false, error: 'Email required' }
|
|
441
|
+
}
|
|
442
|
+
return { valid: true }
|
|
443
|
+
}
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
---
|
|
447
|
+
|
|
448
|
+
## 8. Advanced Patterns
|
|
449
|
+
|
|
450
|
+
**Impact: LOW**
|
|
451
|
+
|
|
452
|
+
### 8.1 useEffectEvent for Stable Callbacks
|
|
453
|
+
|
|
454
|
+
```tsx
|
|
455
|
+
import { useEffectEvent } from 'react'
|
|
456
|
+
|
|
457
|
+
function useWindowEvent(event: string, handler: () => void) {
|
|
458
|
+
const onEvent = useEffectEvent(handler)
|
|
459
|
+
useEffect(() => {
|
|
460
|
+
window.addEventListener(event, onEvent)
|
|
461
|
+
return () => window.removeEventListener(event, onEvent)
|
|
462
|
+
}, [event])
|
|
463
|
+
}
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
---
|
|
467
|
+
|
|
468
|
+
## Checklist Before Implementation
|
|
469
|
+
|
|
470
|
+
- [ ] Independent async operations use Promise.all()
|
|
471
|
+
- [ ] Heavy components use dynamic imports
|
|
472
|
+
- [ ] RSC boundaries pass only needed fields
|
|
473
|
+
- [ ] Suspense boundaries isolate data fetching
|
|
474
|
+
- [ ] No barrel file imports for large libraries
|
|
475
|
+
- [ ] State updates use functional form when depending on current state
|
|
476
|
+
- [ ] Effects have narrow dependencies
|
|
477
|
+
- [ ] Repeated lookups use Set/Map
|
|
478
|
+
|
|
479
|
+
---
|
|
480
|
+
|
|
481
|
+
## References
|
|
482
|
+
|
|
483
|
+
- [React Documentation](https://react.dev)
|
|
484
|
+
- [Next.js Documentation](https://nextjs.org)
|
|
485
|
+
- [SWR Documentation](https://swr.vercel.app)
|
|
486
|
+
- [Vercel Blog: Package Import Optimization](https://vercel.com/blog/how-we-optimized-package-imports-in-next-js)
|
|
487
|
+
- [Vercel Blog: Dashboard Performance](https://vercel.com/blog/how-we-made-the-vercel-dashboard-twice-as-fast)
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: subagents-discipline
|
|
3
|
+
description: Core engineering principles for implementation tasks
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Implementation Principles
|
|
7
|
+
|
|
8
|
+
## Rule 0: Read the Bead First
|
|
9
|
+
|
|
10
|
+
Before implementing anything, **read the bead comments** for context:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
bd show {BEAD_ID}
|
|
14
|
+
bd comments {BEAD_ID}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
The orchestrator's dispatch prompt is automatically logged as a DISPATCH comment on the bead. This contains:
|
|
18
|
+
- The investigation findings
|
|
19
|
+
- Root cause analysis (file, function, line)
|
|
20
|
+
- Related files that may need changes
|
|
21
|
+
- Gotchas and edge cases
|
|
22
|
+
|
|
23
|
+
**Use this context.** Don't re-investigate. The comments contain everything you need to implement confidently.
|
|
24
|
+
|
|
25
|
+
If no dispatch or context comments exist, ask the orchestrator to provide context before proceeding.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Rule 1: Look Before You Code
|
|
30
|
+
|
|
31
|
+
Before writing code that touches external data (API, database, file, config):
|
|
32
|
+
|
|
33
|
+
1. **Fetch/read the ACTUAL data** - run the command, see the output
|
|
34
|
+
2. **Note exact field names, types, formats** - not what docs say, what you SEE
|
|
35
|
+
3. **Code against what you observed** - not what you assumed
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
WITHOUT looking first:
|
|
39
|
+
Assumed: column is "reference_images"
|
|
40
|
+
Reality: column is "reference_image_url"
|
|
41
|
+
Result: Query fails
|
|
42
|
+
|
|
43
|
+
WITH looking first:
|
|
44
|
+
Ran: SELECT column_name FROM information_schema.columns WHERE table_name = 'assets';
|
|
45
|
+
Saw: reference_image_url
|
|
46
|
+
Coded against: reference_image_url
|
|
47
|
+
Result: Works
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Rule 2: Test Functionally (Close the Loop)
|
|
51
|
+
|
|
52
|
+
**Principle: Optimize for the fastest way to verify your work actually works.**
|
|
53
|
+
|
|
54
|
+
| You built | Fast verification | Slower alternative |
|
|
55
|
+
|-----------|------------------|--------------------|
|
|
56
|
+
| API endpoint | `curl` the endpoint, check response | Write integration test |
|
|
57
|
+
| Database change | Run migration, query the result | Write migration test |
|
|
58
|
+
| Frontend component | Load in browser, interact with it | Write component test |
|
|
59
|
+
| CLI tool | Run the command, check output | Write unit test |
|
|
60
|
+
| Config change | Restart service, verify behavior | N/A — just verify |
|
|
61
|
+
|
|
62
|
+
**Two strategies:**
|
|
63
|
+
|
|
64
|
+
1. **User Journey Tests** — Test actual behavior as a user experiences it:
|
|
65
|
+
```bash
|
|
66
|
+
# API: curl with real data
|
|
67
|
+
curl -X POST localhost:3000/api/users -d '{"name":"test"}' -H "Content-Type: application/json"
|
|
68
|
+
|
|
69
|
+
# CLI: run the command
|
|
70
|
+
bd create "Test" -d "Testing" && bd list
|
|
71
|
+
|
|
72
|
+
# Error case: curl with invalid auth
|
|
73
|
+
curl -X POST localhost:3000/api/users -H "Authorization: Bearer invalid"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
2. **Component Tests** — Supplement for regression prevention when fast verification isn't possible:
|
|
77
|
+
- Complex logic with many edge cases
|
|
78
|
+
- Code that runs in environments you can't easily replicate
|
|
79
|
+
- Shared libraries used by multiple consumers
|
|
80
|
+
|
|
81
|
+
**"Close the Loop" principle:** Run the actual thing. Verify it works. Check error cases.
|
|
82
|
+
|
|
83
|
+
Good: "Curled endpoint with invalid auth, got 401 as expected"
|
|
84
|
+
Bad: "Wrote tests, they compile"
|
|
85
|
+
|
|
86
|
+
## Rule 3: Use Your Tools
|
|
87
|
+
|
|
88
|
+
Before claiming you can't fully test:
|
|
89
|
+
|
|
90
|
+
1. **Check what MCP servers you have access to** - list available tools
|
|
91
|
+
2. **If any tool can help verify the feature works**, use it
|
|
92
|
+
3. **Be resourceful** - browser automation, database inspection, API testing tools
|
|
93
|
+
|
|
94
|
+
## Rule 4: Log Your Approach (Optional)
|
|
95
|
+
|
|
96
|
+
If you deviated from the orchestrator's suggestion, found a better path, or made a choice future maintainers might question:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
bd comment {BEAD_ID} "APPROACH: Used X instead of Y because Z"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
When to log:
|
|
103
|
+
- Deviated from the suggested fix
|
|
104
|
+
- Multiple valid solutions, chose one for a specific reason
|
|
105
|
+
- Future maintainers might question the approach
|
|
106
|
+
|
|
107
|
+
Skip if the code is self-explanatory. This is not enforced.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## For Epic Children
|
|
112
|
+
|
|
113
|
+
If your BEAD_ID contains a dot (e.g., BD-001.2), you're implementing part of a larger feature:
|
|
114
|
+
|
|
115
|
+
1. **Check for design doc**: `bd show {EPIC_ID} --json | jq -r '.[0].design'`
|
|
116
|
+
2. **Read it if it exists** - this is your contract
|
|
117
|
+
3. **Match it exactly** - same field names, same types, same shapes
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Red Flags - Stop and Verify
|
|
122
|
+
|
|
123
|
+
When you catch yourself thinking:
|
|
124
|
+
- "This should work..." → run it and see
|
|
125
|
+
- "I assume the field is..." → look at the actual data
|
|
126
|
+
- "I'll test it later..." → test it now
|
|
127
|
+
- "It's too simple to break..." → verify anyway
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# UI Constraints
|
|
2
|
+
|
|
3
|
+
Apply these opinionated constraints when building interfaces.
|
|
4
|
+
|
|
5
|
+
## Stack
|
|
6
|
+
|
|
7
|
+
- MUST use Tailwind CSS defaults unless custom values already exist or are explicitly requested
|
|
8
|
+
- MUST use `motion/react` (formerly `framer-motion`) when JavaScript animation is required
|
|
9
|
+
- SHOULD use `tw-animate-css` for entrance and micro-animations in Tailwind CSS
|
|
10
|
+
- MUST use `cn` utility (`clsx` + `tailwind-merge`) for class logic
|
|
11
|
+
|
|
12
|
+
## Components
|
|
13
|
+
|
|
14
|
+
- MUST use accessible component primitives for anything with keyboard or focus behavior (`Base UI`, `React Aria`, `Radix`)
|
|
15
|
+
- MUST use the project's existing component primitives first
|
|
16
|
+
- NEVER mix primitive systems within the same interaction surface
|
|
17
|
+
- SHOULD prefer [`Base UI`](https://base-ui.com/react/components) for new primitives if compatible with the stack
|
|
18
|
+
- MUST add an `aria-label` to icon-only buttons
|
|
19
|
+
- NEVER rebuild keyboard or focus behavior by hand unless explicitly requested
|
|
20
|
+
|
|
21
|
+
## Interaction
|
|
22
|
+
|
|
23
|
+
- MUST use an `AlertDialog` for destructive or irreversible actions
|
|
24
|
+
- SHOULD use structural skeletons for loading states
|
|
25
|
+
- NEVER use `h-screen`, use `h-dvh`
|
|
26
|
+
- MUST respect `safe-area-inset` for fixed elements
|
|
27
|
+
- MUST show errors next to where the action happens
|
|
28
|
+
- NEVER block paste in `input` or `textarea` elements
|
|
29
|
+
|
|
30
|
+
## Animation
|
|
31
|
+
|
|
32
|
+
- NEVER add animation unless it is explicitly requested
|
|
33
|
+
- MUST animate only compositor props (`transform`, `opacity`)
|
|
34
|
+
- NEVER animate layout properties (`width`, `height`, `top`, `left`, `margin`, `padding`)
|
|
35
|
+
- SHOULD avoid animating paint properties (`background`, `color`) except for small, local UI (text, icons)
|
|
36
|
+
- SHOULD use `ease-out` on entrance
|
|
37
|
+
- NEVER exceed `200ms` for interaction feedback
|
|
38
|
+
- MUST pause looping animations when off-screen
|
|
39
|
+
- SHOULD respect `prefers-reduced-motion`
|
|
40
|
+
- NEVER introduce custom easing curves unless explicitly requested
|
|
41
|
+
- SHOULD avoid animating large images or full-screen surfaces
|
|
42
|
+
|
|
43
|
+
## Typography
|
|
44
|
+
|
|
45
|
+
- MUST use `text-balance` for headings and `text-pretty` for body/paragraphs
|
|
46
|
+
- MUST use `tabular-nums` for data
|
|
47
|
+
- SHOULD use `truncate` or `line-clamp` for dense UI
|
|
48
|
+
- NEVER modify `letter-spacing` (`tracking-*`) unless explicitly requested
|
|
49
|
+
|
|
50
|
+
## Layout
|
|
51
|
+
|
|
52
|
+
- MUST use a fixed `z-index` scale (no arbitrary `z-*`)
|
|
53
|
+
- SHOULD use `size-*` for square elements instead of `w-*` + `h-*`
|
|
54
|
+
|
|
55
|
+
## Performance
|
|
56
|
+
|
|
57
|
+
- NEVER animate large `blur()` or `backdrop-filter` surfaces
|
|
58
|
+
- NEVER apply `will-change` outside an active animation
|
|
59
|
+
- NEVER use `useEffect` for anything that can be expressed as render logic
|
|
60
|
+
|
|
61
|
+
## Design
|
|
62
|
+
|
|
63
|
+
- NEVER use gradients unless explicitly requested
|
|
64
|
+
- NEVER use purple or multicolor gradients
|
|
65
|
+
- NEVER use glow effects as primary affordances
|
|
66
|
+
- SHOULD use Tailwind CSS default shadow scale unless explicitly requested
|
|
67
|
+
- MUST give empty states one clear next action
|
|
68
|
+
- SHOULD limit accent color usage to one per view
|
|
69
|
+
- SHOULD use existing theme or Tailwind CSS color tokens before introducing new ones
|
|
70
|
+
|
|
71
|
+
## Accessibility
|
|
72
|
+
|
|
73
|
+
- MUST meet WCAG AA color contrast (4.5:1 for text, 3:1 for large text/UI)
|
|
74
|
+
- MUST ensure all interactive elements are keyboard accessible
|
|
75
|
+
- SHOULD provide visible focus indicators
|
|
76
|
+
- MUST use semantic HTML elements where appropriate
|