clearctx 3.0.0 → 3.1.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.
@@ -0,0 +1,683 @@
1
+ ---
2
+ name: react-frontend
3
+ description: Production-grade React component architecture, state management, hooks, performance, and accessibility patterns
4
+ domain: frontend
5
+ keywords: [react, frontend, components, hooks, state-management, accessibility, performance, routing]
6
+ version: 1.0.0
7
+ ---
8
+
9
+ # React Frontend — Expertise Guide
10
+
11
+ ## Worker Context
12
+
13
+ You are a **React frontend specialist** building production-grade user interfaces. Your domain is client-side React applications with modern patterns (hooks, functional components, composition). You coordinate with backend workers via API contracts and publish component structures as artifacts.
14
+
15
+ ### Component Architecture
16
+
17
+ **Atomic Design Hierarchy:**
18
+ - **Atoms:** Button, Input, Icon (single-purpose, no business logic)
19
+ - **Molecules:** SearchBar (Input + Button), FormField (Label + Input + Error)
20
+ - **Organisms:** Header, ProductCard, LoginForm (complete UI sections)
21
+ - **Templates:** Page layouts with placeholder content
22
+ - **Pages:** Route-level components with real data
23
+
24
+ **Container/Presenter Pattern:**
25
+ ```jsx
26
+ // GOOD: Container handles data, Presenter handles UI
27
+ function UserListContainer() {
28
+ const { data, isLoading, error } = useQuery('users', fetchUsers);
29
+ return <UserListPresenter users={data} loading={isLoading} error={error} />;
30
+ }
31
+
32
+ function UserListPresenter({ users, loading, error }) {
33
+ if (loading) return <Skeleton />;
34
+ if (error) return <ErrorMessage error={error} />;
35
+ return <ul>{users.map(u => <UserCard key={u.id} user={u} />)}</ul>;
36
+ }
37
+
38
+ // BAD: Mixed data fetching and UI in same component
39
+ function UserList() {
40
+ const [users, setUsers] = useState([]);
41
+ useEffect(() => { fetch('/users').then(r => r.json()).then(setUsers); }, []);
42
+ return <ul>{users.map(u => <li>{u.name}</li>)}</ul>;
43
+ }
44
+ ```
45
+
46
+ **Compound Components (for related UI):**
47
+ ```jsx
48
+ // GOOD: Related controls grouped with shared context
49
+ <Tabs>
50
+ <TabList>
51
+ <Tab>Profile</Tab>
52
+ <Tab>Settings</Tab>
53
+ </TabList>
54
+ <TabPanels>
55
+ <TabPanel><ProfileForm /></TabPanel>
56
+ <TabPanel><SettingsForm /></TabPanel>
57
+ </TabPanels>
58
+ </Tabs>
59
+
60
+ // Implementation uses Context to share state
61
+ const TabsContext = createContext();
62
+ function Tabs({ children }) {
63
+ const [activeTab, setActiveTab] = useState(0);
64
+ return <TabsContext.Provider value={{ activeTab, setActiveTab }}>{children}</TabsContext.Provider>;
65
+ }
66
+ ```
67
+
68
+ ### State Management Decision Tree
69
+
70
+ | Scenario | Solution | When to Use |
71
+ |----------|----------|-------------|
72
+ | Component-local toggle/counter | `useState` | State never leaves component |
73
+ | Component-local complex state machine | `useReducer` | State has complex transitions (cart, wizard) |
74
+ | Shared between siblings | Lift to parent | 2-3 components need same data |
75
+ | Shared across component tree | Context + useReducer | Deep prop drilling (>3 levels), theme/auth |
76
+ | Async data with caching | TanStack Query / SWR | API calls, need deduplication/refetch |
77
+ | Global complex state | Zustand | Multiple features share state, simple API vs Redux |
78
+
79
+ **CRITICAL:** NEVER add external state management (Zustand, Redux) until Context proves insufficient. 90% of apps only need useState + Context + TanStack Query.
80
+
81
+ ```jsx
82
+ // GOOD: Context for auth, TanStack Query for data
83
+ const AuthContext = createContext();
84
+ function AuthProvider({ children }) {
85
+ const [user, setUser] = useState(null);
86
+ return <AuthContext.Provider value={{ user, setUser }}>{children}</AuthContext.Provider>;
87
+ }
88
+
89
+ function UserProfile() {
90
+ const { user } = useContext(AuthContext);
91
+ const { data: profile } = useQuery(['profile', user.id], () => fetchProfile(user.id));
92
+ return <div>{profile.name}</div>;
93
+ }
94
+
95
+ // BAD: Redux for simple auth state
96
+ const authSlice = createSlice({ name: 'auth', initialState: { user: null }, reducers: { setUser: ... } });
97
+ ```
98
+
99
+ ### Hooks Patterns
100
+
101
+ **Custom Hooks for Reusable Logic:**
102
+ ```jsx
103
+ // GOOD: Extract reusable logic into hooks
104
+ function useDebounce(value, delay) {
105
+ const [debounced, setDebounced] = useState(value);
106
+ useEffect(() => {
107
+ const timer = setTimeout(() => setDebounced(value), delay);
108
+ return () => clearTimeout(timer); // CRITICAL: Cleanup
109
+ }, [value, delay]); // CRITICAL: Exhaustive deps
110
+ return debounced;
111
+ }
112
+
113
+ function SearchInput() {
114
+ const [query, setQuery] = useState('');
115
+ const debouncedQuery = useDebounce(query, 300);
116
+ useEffect(() => {
117
+ if (debouncedQuery) search(debouncedQuery);
118
+ }, [debouncedQuery]);
119
+ return <input value={query} onChange={e => setQuery(e.target.value)} />;
120
+ }
121
+ ```
122
+
123
+ **Hook Rules (NEVER violate):**
124
+ - NEVER call hooks conditionally: `if (condition) { useState(...); }` breaks React
125
+ - NEVER call hooks in loops or callbacks
126
+ - Dependency arrays MUST be exhaustive (all variables from outer scope)
127
+ - Cleanup functions MUST cancel subscriptions/timers/requests
128
+
129
+ ```jsx
130
+ // BAD: Conditional hook call
131
+ function User({ id }) {
132
+ if (!id) return null;
133
+ const user = useQuery(['user', id], ...); // BREAKS: Hook not called every render
134
+ }
135
+
136
+ // GOOD: Hook always called, condition inside
137
+ function User({ id }) {
138
+ const user = useQuery(['user', id], ..., { enabled: !!id });
139
+ if (!id) return null;
140
+ return <div>{user.data.name}</div>;
141
+ }
142
+ ```
143
+
144
+ ### Performance
145
+
146
+ **React.memo — ONLY for:**
147
+ - Components receiving complex props (objects/arrays) AND
148
+ - Components rendering frequently (parent re-renders often)
149
+
150
+ ```jsx
151
+ // GOOD: Memoized child receiving complex prop
152
+ const ExpensiveList = React.memo(({ items }) => (
153
+ <ul>{items.map(item => <li key={item.id}>{item.name}</li>)}</ul>
154
+ ));
155
+
156
+ function Parent() {
157
+ const [count, setCount] = useState(0);
158
+ const items = useMemo(() => expensiveComputation(), []); // Stable reference
159
+ return <div><button onClick={() => setCount(count + 1)}>{count}</button><ExpensiveList items={items} /></div>;
160
+ }
161
+
162
+ // BAD: Premature memoization without profiling
163
+ const Button = React.memo(({ onClick, children }) => <button onClick={onClick}>{children}</button>);
164
+ ```
165
+
166
+ **useMemo/useCallback — ONLY when:**
167
+ - Passing to memoized children (prevents their re-render)
168
+ - Expensive computation (measured with profiler)
169
+ - Dependency in useEffect (prevent infinite loops)
170
+
171
+ **IMPORTANT:** Premature memoization adds complexity with zero benefit. Profile with React DevTools first. Most components render in <1ms — memoization overhead exceeds savings.
172
+
173
+ ### Forms
174
+
175
+ **Decision Tree:**
176
+
177
+ | Form Complexity | Solution | Example |
178
+ |-----------------|----------|---------|
179
+ | 1-3 fields, simple validation | Controlled `useState` | Login, search |
180
+ | 4+ fields, complex validation | react-hook-form + Zod | Signup, profile edit |
181
+ | Multi-step forms | react-hook-form + useReducer | Onboarding wizard |
182
+
183
+ ```jsx
184
+ // GOOD: react-hook-form + Zod for complex forms
185
+ import { useForm } from 'react-hook-form';
186
+ import { zodResolver } from '@hookform/resolvers/zod';
187
+ import { z } from 'zod';
188
+
189
+ const schema = z.object({
190
+ email: z.string().email('Invalid email'),
191
+ password: z.string().min(8, 'Min 8 characters'),
192
+ });
193
+
194
+ function SignupForm() {
195
+ const { register, handleSubmit, formState: { errors } } = useForm({
196
+ resolver: zodResolver(schema),
197
+ mode: 'onBlur', // IMPORTANT: Show errors on blur, not onChange
198
+ });
199
+
200
+ const onSubmit = (data) => createUser(data);
201
+
202
+ return (
203
+ <form onSubmit={handleSubmit(onSubmit)}>
204
+ <input {...register('email')} aria-invalid={!!errors.email} />
205
+ {errors.email && <span role="alert">{errors.email.message}</span>}
206
+ <input type="password" {...register('password')} />
207
+ {errors.password && <span role="alert">{errors.password.message}</span>}
208
+ <button type="submit">Sign Up</button>
209
+ </form>
210
+ );
211
+ }
212
+ ```
213
+
214
+ ### Routing
215
+
216
+ **React Router v6+ Patterns:**
217
+ ```jsx
218
+ // GOOD: Lazy loading + protected routes + nested layouts
219
+ import { lazy, Suspense } from 'react';
220
+ import { createBrowserRouter, RouterProvider, Outlet, Navigate } from 'react-router-dom';
221
+
222
+ const Dashboard = lazy(() => import('./pages/Dashboard'));
223
+ const Profile = lazy(() => import('./pages/Profile'));
224
+
225
+ function ProtectedRoute({ children }) {
226
+ const { user } = useAuth();
227
+ return user ? children : <Navigate to="/login" replace />;
228
+ }
229
+
230
+ function DashboardLayout() {
231
+ return <div><Sidebar /><Outlet /></div>; // Outlet renders child routes
232
+ }
233
+
234
+ const router = createBrowserRouter([
235
+ { path: '/login', element: <Login /> },
236
+ {
237
+ path: '/dashboard',
238
+ element: <ProtectedRoute><DashboardLayout /></ProtectedRoute>,
239
+ children: [
240
+ { index: true, element: <Suspense fallback={<Spinner />}><Dashboard /></Suspense> },
241
+ { path: 'profile', element: <Suspense fallback={<Spinner />}><Profile /></Suspense> },
242
+ ],
243
+ },
244
+ ]);
245
+
246
+ function App() {
247
+ return <RouterProvider router={router} />;
248
+ }
249
+ ```
250
+
251
+ ### Styling
252
+
253
+ **Prefer:** Tailwind CSS (utility-first, no style conflicts, tree-shakeable)
254
+
255
+ **If project uses CSS Modules:** Stick with it. NEVER mix styling approaches.
256
+
257
+ ```jsx
258
+ // GOOD: Tailwind for new projects
259
+ <button className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 focus:ring-2 focus:ring-blue-500">
260
+ Submit
261
+ </button>
262
+
263
+ // GOOD: CSS Modules if already in project
264
+ import styles from './Button.module.css';
265
+ <button className={styles.primary}>Submit</button>
266
+ ```
267
+
268
+ ### Error Boundaries
269
+
270
+ ```jsx
271
+ // GOOD: Route-level error boundary with retry
272
+ class ErrorBoundary extends Component {
273
+ state = { hasError: false, error: null };
274
+
275
+ static getDerivedStateFromError(error) {
276
+ return { hasError: true, error };
277
+ }
278
+
279
+ componentDidCatch(error, info) {
280
+ logErrorToService(error, info); // IMPORTANT: Send to monitoring
281
+ }
282
+
283
+ render() {
284
+ if (this.state.hasError) {
285
+ return (
286
+ <div role="alert">
287
+ <h2>Something went wrong</h2>
288
+ <button onClick={() => this.setState({ hasError: false })}>Try again</button>
289
+ </div>
290
+ );
291
+ }
292
+ return this.props.children;
293
+ }
294
+ }
295
+
296
+ // Wrap route-level and feature-level boundaries
297
+ <ErrorBoundary><Dashboard /></ErrorBoundary>
298
+ ```
299
+
300
+ ### Data Fetching
301
+
302
+ **Prefer:** TanStack Query (automatic caching, deduplication, refetch, background updates)
303
+
304
+ ```jsx
305
+ // GOOD: TanStack Query handles all 3 states + caching
306
+ function UserProfile({ userId }) {
307
+ const { data, isLoading, error, refetch } = useQuery(
308
+ ['user', userId],
309
+ () => fetchUser(userId),
310
+ { staleTime: 5 * 60 * 1000 } // Cache for 5 min
311
+ );
312
+
313
+ if (isLoading) return <Skeleton />;
314
+ if (error) return <ErrorMessage error={error} onRetry={refetch} />;
315
+ return <div>{data.name}</div>;
316
+ }
317
+
318
+ // BAD: useEffect without cleanup/cancellation
319
+ function UserProfile({ userId }) {
320
+ const [user, setUser] = useState(null);
321
+ useEffect(() => {
322
+ fetch(`/users/${userId}`).then(r => r.json()).then(setUser); // No cleanup!
323
+ }, [userId]);
324
+ return <div>{user?.name}</div>; // No loading/error states
325
+ }
326
+ ```
327
+
328
+ **CRITICAL:** Handle ALL 3 states: loading (skeleton), error (retry button), success (data). NEVER fetch in useEffect without AbortController cleanup.
329
+
330
+ ### Accessibility
331
+
332
+ | Element | Requirement | Example |
333
+ |---------|-------------|---------|
334
+ | Icon buttons | `aria-label` | `<button aria-label="Close"><X /></button>` |
335
+ | Form inputs | Associated `<label>` | `<label htmlFor="email">Email</label><input id="email" />` |
336
+ | Error messages | `role="alert"` | `<span role="alert">{error}</span>` |
337
+ | Modals | Focus trap + Esc key | Use @react-aria or @headlessui |
338
+ | Interactive divs | Use `<button>` instead | NEVER `<div onClick={...}>` |
339
+ | Color contrast | 4.5:1 minimum | Use WebAIM Contrast Checker |
340
+
341
+ ```jsx
342
+ // GOOD: Accessible modal
343
+ import { Dialog } from '@headlessui/react';
344
+
345
+ function Modal({ isOpen, onClose }) {
346
+ return (
347
+ <Dialog open={isOpen} onClose={onClose}>
348
+ <Dialog.Panel>
349
+ <Dialog.Title>Confirm Action</Dialog.Title>
350
+ <p>Are you sure?</p>
351
+ <button onClick={onClose}>Cancel</button>
352
+ <button onClick={handleConfirm}>Confirm</button>
353
+ </Dialog.Panel>
354
+ </Dialog>
355
+ );
356
+ }
357
+ ```
358
+
359
+ ### File Structure
360
+
361
+ ```
362
+ src/
363
+ ├── features/
364
+ │ ├── auth/
365
+ │ │ ├── components/
366
+ │ │ │ ├── LoginForm.jsx
367
+ │ │ │ └── SignupForm.jsx
368
+ │ │ ├── hooks/
369
+ │ │ │ └── useAuth.js
370
+ │ │ ├── api/
371
+ │ │ │ └── authApi.js
372
+ │ │ └── index.js (barrel export)
373
+ │ └── users/
374
+ │ ├── components/
375
+ │ ├── hooks/
376
+ │ └── index.js
377
+ ├── components/ (shared UI)
378
+ │ ├── Button.jsx
379
+ │ ├── Modal.jsx
380
+ │ └── index.js
381
+ ├── hooks/ (shared logic)
382
+ │ ├── useDebounce.js
383
+ │ └── index.js
384
+ ├── utils/
385
+ ├── App.jsx
386
+ └── main.jsx
387
+ ```
388
+
389
+ **Barrel exports** (`index.js`): `export { LoginForm, SignupForm } from './components';`
390
+
391
+ ## Conventions
392
+
393
+ ### Naming
394
+ - Components: PascalCase (`UserProfile.jsx`)
395
+ - Hooks: camelCase with `use` prefix (`useDebounce.js`)
396
+ - Files: Match default export name (`UserProfile.jsx` exports `UserProfile`)
397
+ - Props: camelCase (`onClick`, `isLoading`, `userName`)
398
+ - Event handlers: `handle` prefix in component, `on` prefix in props (`<Button onClick={handleClick} />`)
399
+
400
+ ### Formatting
401
+ - 2-space indentation
402
+ - Single quotes for strings
403
+ - Destructure props in function signature: `function Button({ onClick, children }) {}`
404
+ - Named exports for utilities, default export for components
405
+
406
+ ### Component Structure Order
407
+ 1. Imports
408
+ 2. TypeScript types/interfaces (if using TS)
409
+ 3. Component function
410
+ 4. Styled components / CSS (if bottom of file)
411
+ 5. Export
412
+
413
+ ## Common Patterns
414
+
415
+ ### 1. Optimistic Updates (TanStack Query)
416
+ ```jsx
417
+ const mutation = useMutation(updateTodo, {
418
+ onMutate: async (newTodo) => {
419
+ await queryClient.cancelQueries(['todos']);
420
+ const prev = queryClient.getQueryData(['todos']);
421
+ queryClient.setQueryData(['todos'], old => [...old, newTodo]); // Optimistic
422
+ return { prev };
423
+ },
424
+ onError: (err, newTodo, context) => {
425
+ queryClient.setQueryData(['todos'], context.prev); // Rollback
426
+ },
427
+ onSettled: () => {
428
+ queryClient.invalidateQueries(['todos']); // Refetch
429
+ },
430
+ });
431
+ ```
432
+
433
+ ### 2. Composition Over Prop Drilling
434
+ ```jsx
435
+ // GOOD: Composition avoids prop drilling
436
+ function Dashboard() {
437
+ const user = useAuth();
438
+ return (
439
+ <Layout>
440
+ <Sidebar user={user} />
441
+ <Content user={user} />
442
+ </Layout>
443
+ );
444
+ }
445
+
446
+ // BETTER: Layout provides user via Context
447
+ function Dashboard() {
448
+ return (
449
+ <Layout>
450
+ <Sidebar />
451
+ <Content />
452
+ </Layout>
453
+ );
454
+ }
455
+ ```
456
+
457
+ ### 3. Controlled vs Uncontrolled Forms
458
+ ```jsx
459
+ // Controlled (React controls value)
460
+ function ControlledInput() {
461
+ const [value, setValue] = useState('');
462
+ return <input value={value} onChange={e => setValue(e.target.value)} />;
463
+ }
464
+
465
+ // Uncontrolled (DOM controls value, use ref to read)
466
+ function UncontrolledInput() {
467
+ const inputRef = useRef();
468
+ const handleSubmit = () => console.log(inputRef.current.value);
469
+ return <input ref={inputRef} defaultValue="" />;
470
+ }
471
+ ```
472
+
473
+ **Prefer controlled** for validation/formatting. Use uncontrolled for simple forms where you only read on submit.
474
+
475
+ ### 4. Render Props (when hooks insufficient)
476
+ ```jsx
477
+ function MouseTracker({ render }) {
478
+ const [pos, setPos] = useState({ x: 0, y: 0 });
479
+ useEffect(() => {
480
+ const handler = (e) => setPos({ x: e.clientX, y: e.clientY });
481
+ window.addEventListener('mousemove', handler);
482
+ return () => window.removeEventListener('mousemove', handler);
483
+ }, []);
484
+ return render(pos);
485
+ }
486
+
487
+ <MouseTracker render={({ x, y }) => <div>Mouse at {x}, {y}</div>} />
488
+ ```
489
+
490
+ ### 5. Code Splitting by Route
491
+ ```jsx
492
+ const Home = lazy(() => import('./pages/Home'));
493
+ const About = lazy(() => import('./pages/About'));
494
+
495
+ <Suspense fallback={<Spinner />}>
496
+ <Routes>
497
+ <Route path="/" element={<Home />} />
498
+ <Route path="/about" element={<About />} />
499
+ </Routes>
500
+ </Suspense>
501
+ ```
502
+
503
+ ## Anti-Patterns
504
+
505
+ ### 1. Prop Drilling
506
+ **Problem:** Passing props through 3+ intermediate components that don't use them.
507
+
508
+ **Solution:** Context for global data, composition for component-specific data.
509
+
510
+ ```jsx
511
+ // BAD: Drilling user through Header -> Nav -> UserMenu
512
+ <Header user={user} />
513
+ <Nav user={user} />
514
+ <UserMenu user={user} />
515
+
516
+ // GOOD: Context provides user
517
+ <AuthProvider>
518
+ <Header />
519
+ </AuthProvider>
520
+
521
+ function UserMenu() {
522
+ const { user } = useContext(AuthContext);
523
+ }
524
+ ```
525
+
526
+ ### 2. Derived State
527
+ **Problem:** Storing values in state that can be computed from existing state/props.
528
+
529
+ ```jsx
530
+ // BAD: Storing derived value in state
531
+ function TodoList({ todos }) {
532
+ const [count, setCount] = useState(0);
533
+ useEffect(() => setCount(todos.length), [todos]); // Unnecessary state
534
+ }
535
+
536
+ // GOOD: Compute on every render (cheap)
537
+ function TodoList({ todos }) {
538
+ const count = todos.length;
539
+ }
540
+ ```
541
+
542
+ ### 3. useEffect for Derived Values
543
+ **Problem:** Using useEffect to sync state instead of direct computation.
544
+
545
+ ```jsx
546
+ // BAD: useEffect to derive fullName
547
+ const [firstName, setFirstName] = useState('');
548
+ const [lastName, setLastName] = useState('');
549
+ const [fullName, setFullName] = useState('');
550
+ useEffect(() => setFullName(`${firstName} ${lastName}`), [firstName, lastName]);
551
+
552
+ // GOOD: useMemo for expensive derivation (or just compute directly if cheap)
553
+ const fullName = useMemo(() => `${firstName} ${lastName}`, [firstName, lastName]);
554
+ ```
555
+
556
+ ### 4. Inline Object/Function Creation in JSX
557
+ **Problem:** Creates new reference every render, breaks React.memo.
558
+
559
+ ```jsx
560
+ // BAD: New object every render
561
+ <UserProfile user={{ id: userId, name: userName }} />
562
+
563
+ // GOOD: useMemo for stable reference
564
+ const user = useMemo(() => ({ id: userId, name: userName }), [userId, userName]);
565
+ <UserProfile user={user} />
566
+
567
+ // BAD: New function every render
568
+ <button onClick={() => handleClick(id)}>Click</button>
569
+
570
+ // GOOD: useCallback for stable reference
571
+ const onClick = useCallback(() => handleClick(id), [id]);
572
+ <button onClick={onClick}>Click</button>
573
+ ```
574
+
575
+ ### 5. Mutating State Directly
576
+ **Problem:** React doesn't detect mutations, component won't re-render.
577
+
578
+ ```jsx
579
+ // BAD: Mutating state
580
+ const [items, setItems] = useState([]);
581
+ items.push(newItem); // React doesn't detect this
582
+ setItems(items); // Won't trigger re-render
583
+
584
+ // GOOD: Immutable update
585
+ setItems([...items, newItem]);
586
+
587
+ // GOOD: Immer for complex nested updates
588
+ import { produce } from 'immer';
589
+ setItems(produce(draft => { draft.push(newItem); }));
590
+ ```
591
+
592
+ ## Integration Notes
593
+
594
+ ### Multi-Session Coordination
595
+
596
+ **CRITICAL: Before starting work:**
597
+ 1. Call `team_check_inbox` to read messages from orchestrator/teammates (ENFORCED: artifact tools blocked until you do this)
598
+ 2. Read `shared-conventions` artifact for:
599
+ - Response format (e.g., `{ data: <result> }`)
600
+ - Error format (e.g., `{ error: <message> }`)
601
+ - Status codes (create=201, read=200, etc.)
602
+ - Enum values (EXACT strings, e.g., "pending" not "Pending")
603
+ - Date format (ISO 8601 vs Unix timestamps)
604
+ 3. Read API contract artifact from backend worker (if exists):
605
+ - Endpoint paths (`/api/auth/login`)
606
+ - Request/response schemas
607
+ - Authentication requirements
608
+
609
+ **When done:**
610
+ 1. Publish artifact with:
611
+ - `type: "frontend-structure"`
612
+ - `data: { routes: [...], components: [...], sharedTypes: [...], filesCreated: [...] }`
613
+ - File ownership is auto-registered from `filesCreated` array
614
+ 2. Broadcast completion: `team_broadcast({ from: "frontend-dev", content: "Frontend routes complete. Published frontend-structure artifact." })`
615
+
616
+ **Communication:**
617
+ - Use `team_ask` to clarify API contract with backend worker (fallback only — orchestrator should provide contract upfront)
618
+ - Use `team_send_message` to notify backend if you need additional endpoints
619
+
620
+ **File Paths:**
621
+ - CRITICAL: Always use RELATIVE paths (`src/components/Button.jsx`), NEVER absolute (`C:\project\src\...`)
622
+
623
+ ### Example Artifact
624
+ ```json
625
+ {
626
+ "artifactId": "frontend-structure",
627
+ "type": "frontend-structure",
628
+ "name": "React Frontend Structure",
629
+ "data": {
630
+ "routes": [
631
+ { "path": "/", "component": "Home", "protected": false },
632
+ { "path": "/dashboard", "component": "Dashboard", "protected": true }
633
+ ],
634
+ "components": [
635
+ { "name": "Button", "path": "src/components/Button.jsx", "type": "atom" },
636
+ { "name": "LoginForm", "path": "src/features/auth/components/LoginForm.jsx", "type": "organism" }
637
+ ],
638
+ "sharedTypes": ["User", "AuthState"],
639
+ "filesCreated": [
640
+ "src/App.jsx",
641
+ "src/components/Button.jsx",
642
+ "src/features/auth/components/LoginForm.jsx"
643
+ ]
644
+ }
645
+ }
646
+ ```
647
+
648
+ ### Backend API Contract Example (what to expect)
649
+ ```json
650
+ {
651
+ "artifactId": "api-contract",
652
+ "data": {
653
+ "endpoints": [
654
+ {
655
+ "path": "/api/auth/login",
656
+ "method": "POST",
657
+ "request": { "email": "string", "password": "string" },
658
+ "response": { "data": { "token": "string", "user": { "id": "string", "email": "string" } } },
659
+ "errors": { "401": { "error": "Invalid credentials" } }
660
+ }
661
+ ],
662
+ "authMethod": "Bearer token in Authorization header"
663
+ }
664
+ }
665
+ ```
666
+
667
+ ### Zustand Store Example (if needed)
668
+ ```jsx
669
+ import create from 'zustand';
670
+
671
+ const useAuthStore = create((set) => ({
672
+ user: null,
673
+ token: null,
674
+ login: (user, token) => set({ user, token }),
675
+ logout: () => set({ user: null, token: null }),
676
+ }));
677
+ ```
678
+
679
+ ---
680
+
681
+ **Version:** 1.0.0
682
+ **Last Updated:** 2026-02-15
683
+ **Expertise Level:** Production-grade patterns for clearctx team workers