workflow-agent-cli 1.1.2

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,887 @@
1
+ # Agent Editing Instructions
2
+
3
+ > **Purpose**: This document defines the rules and patterns that AI agents (GitHub Copilot, Claude, etc.) must follow when making edits to this codebase. Following these guidelines ensures consistency, prevents regressions, and maintains architectural integrity.
4
+
5
+ ---
6
+
7
+ ## Table of Contents
8
+
9
+ 1. [Implementation Plans](#implementation-plans)
10
+ 2. [Required File Checklists by Change Type](#required-file-checklists-by-change-type)
11
+ 3. [Coding Standards](#coding-standards)
12
+ 4. [Architecture Rules](#architecture-rules)
13
+ 5. [Library Usage Policy](#library-usage-policy)
14
+ 6. [Testing Requirements](#testing-requirements)
15
+ 7. [PR Workflow](#pr-workflow)
16
+
17
+ ---
18
+
19
+ ## Implementation Plans
20
+
21
+ ### ⚠️ MANDATORY: Create Branch BEFORE Implementation
22
+
23
+ **Before starting ANY implementation plan work, you MUST:**
24
+
25
+ 1. Ensure you are on `main` and it is up to date:
26
+
27
+ ```bash
28
+ git checkout main
29
+ git pull origin main
30
+ ```
31
+
32
+ 2. Create a new feature branch:
33
+
34
+ ```bash
35
+ git checkout -b <type>/<scope>/<short-description>
36
+ ```
37
+
38
+ 3. Only THEN begin creating the implementation plan and making code changes
39
+
40
+ > 🚫 **NEVER** start implementing a plan while on `main`. All implementation work MUST be on a dedicated feature branch.
41
+
42
+ ### When to Create an Implementation Plan
43
+
44
+ Create an implementation plan for any work that:
45
+
46
+ - Touches **more than 3 files**
47
+ - Implements a **new feature** (not a simple bugfix)
48
+ - Requires **multiple related changes** across the codebase
49
+ - Will be completed over **multiple sessions**
50
+ - Involves **database migrations** or schema changes
51
+
52
+ ### File Location and Naming
53
+
54
+ | Type | Location | Naming Convention |
55
+ | --------------- | ------------------ | ---------------------------- |
56
+ | Active plans | `plans/` | `<FEATURE_NAME>_PLAN.md` |
57
+ | Completed plans | `plans/completed/` | Same name, moved after merge |
58
+
59
+ **Naming examples:**
60
+
61
+ - `plans/DUE_DATE_REMINDERS_PLAN.md`
62
+ - `plans/TEAM_PERMISSIONS_PLAN.md`
63
+ - `plans/NOTIFICATION_REFACTOR_PLAN.md`
64
+
65
+ ### Required Structure
66
+
67
+ Every implementation plan MUST follow this template:
68
+
69
+ ```markdown
70
+ # Implementation Plan: <Feature Name>
71
+
72
+ > **Status**: 🟡 In Progress | 🟢 Complete | 🔴 Blocked
73
+ > **Created**: YYYY-MM-DD
74
+ > **Last Updated**: YYYY-MM-DD
75
+ > **Branch**: `feature/<scope>/<description>`
76
+
77
+ ## Overview
78
+
79
+ Brief description of what is being implemented and why.
80
+
81
+ ## Checklist
82
+
83
+ ### Phase 1: <Phase Name>
84
+
85
+ - [ ] Task description — `path/to/file.ts`
86
+ - [ ] Task description — `path/to/file.ts`
87
+
88
+ ### Phase 2: <Phase Name>
89
+
90
+ - [ ] Task description — `path/to/file.ts`
91
+ - [ ] Task description — `path/to/file.ts`
92
+
93
+ ### Testing
94
+
95
+ - [ ] Unit tests added
96
+ - [ ] E2E tests added (if applicable)
97
+ - [ ] Manual testing completed
98
+
99
+ ### Documentation
100
+
101
+ - [ ] Types updated in `types/index.ts`
102
+ - [ ] LIBRARY_INVENTORY.md updated (if new deps)
103
+
104
+ ## Notes
105
+
106
+ Any decisions, blockers, or context for future reference.
107
+ ```
108
+
109
+ ### Progress Update Rules
110
+
111
+ **Agent MUST update the implementation plan as work progresses:**
112
+
113
+ 1. **Mark items complete** — Change `- [ ]` to `- [x]` immediately after completing each task
114
+ 2. **Update "Last Updated" date** — Refresh timestamp on every modification
115
+ 3. **Update "Status"** — Change from 🟡 to 🟢 when all items complete, or 🔴 if blocked
116
+ 4. **Add notes** — Document any deviations, decisions, or blockers in the Notes section
117
+
118
+ ### Pushing Changes After Implementation
119
+
120
+ **After completing implementation work, you MUST push to the feature branch:**
121
+
122
+ 1. **Verify all changes are ready:**
123
+
124
+ ```bash
125
+ pnpm build # Must pass
126
+ pnpm lint # Must be clean
127
+ pnpm test # Must pass
128
+ ```
129
+
130
+ 2. **Stage and commit changes:**
131
+
132
+ ```bash
133
+ git add .
134
+ git commit -m "<type>(<scope>): <description>"
135
+ ```
136
+
137
+ 3. **Push to the feature branch:**
138
+
139
+ ```bash
140
+ git push origin <branch-name>
141
+ ```
142
+
143
+ 4. **Create a Pull Request** for review and merge to `main`
144
+
145
+ > ⚠️ **IMPORTANT**: Never push directly to `main`. Always push to your feature branch and create a PR.
146
+
147
+ ### Auto-Cleanup Rules
148
+
149
+ | Trigger | Action |
150
+ | ---------------------------- | --------------------------------------------- |
151
+ | All checklist items complete | Update status to 🟢 Complete |
152
+ | PR merged to `main` | Move file from `plans/` to `plans/completed/` |
153
+ | 30 days after completion | Delete from `plans/completed/` |
154
+ | Major release | Archive or delete all completed plans |
155
+
156
+ **Cleanup command** (run periodically):
157
+
158
+ ```bash
159
+ # Move completed plans older than 30 days to trash
160
+ find plans/completed -name "*.md" -mtime +30 -delete
161
+ ```
162
+
163
+ ### Example Workflow
164
+
165
+ 1. **Start work**: Create `plans/REMINDER_NOTIFICATIONS_PLAN.md`
166
+ 2. **During work**: Update checkboxes as each task completes
167
+ 3. **Complete work**: Status changes to 🟢, create PR
168
+ 4. **PR merged**: Move to `plans/completed/REMINDER_NOTIFICATIONS_PLAN.md`
169
+ 5. **30 days later**: Auto-delete or manual cleanup
170
+
171
+ ---
172
+
173
+ ## Required File Checklists by Change Type
174
+
175
+ Before completing any change, verify that ALL required files have been touched. This prevents incomplete implementations and data drift.
176
+
177
+ ### New Server Action
178
+
179
+ | File | Action Required |
180
+ | ------------------------------------ | ------------------------------------------------------- |
181
+ | `app/actions/<entity>.ts` | Create/update with `'use server'` directive first line |
182
+ | `app/actions/index.ts` | Export new action module: `export * from "./<entity>";` |
183
+ | `types/index.ts` | Add/update TypeScript interfaces for the entity |
184
+ | `lib/validations/<entity>.schema.ts` | Add Zod validation schema |
185
+ | `lib/validations/index.ts` | Export new schema |
186
+
187
+ ### New Component
188
+
189
+ | File | Action Required |
190
+ | ------------------------------------- | ----------------------------------------------- |
191
+ | `components/<ComponentName>.tsx` | Create with PascalCase filename |
192
+ | `components/<ComponentName>.test.tsx` | Add unit test (required for complex components) |
193
+ | `types/index.ts` | Add prop interface if types are shared |
194
+
195
+ ### New UI Component (Component Library Addition)
196
+
197
+ > **⚠️ MANDATORY**: Before creating ANY new UI component, complete the [UI Component Audit](#ui-component-audit).
198
+
199
+ | File | Action Required |
200
+ | ----------------------------------------------------- | --------------------------------------------------- |
201
+ | Audit | Complete UI Component Audit checklist below |
202
+ | `components/ui/<name>.tsx` or `components/<Name>.tsx` | Create component using design tokens |
203
+ | `components/<Name>.stories.tsx` | Create Storybook story with Controls |
204
+ | `components/<Name>.test.tsx` | Add unit tests with snapshot tests for all variants |
205
+ | `lib/feature-flags.ts` | Add feature flag for rollout control |
206
+ | `.env.example` | Document new `NEXT_PUBLIC_FEATURE_*` variable |
207
+ | `guidelines/COMPONENT_LIBRARY.md` | Add component to appropriate section |
208
+
209
+ ### Modifying Existing UI Component
210
+
211
+ | File | Action Required |
212
+ | --------------------------------- | ------------------------------------------------------ |
213
+ | `guidelines/COMPONENT_LIBRARY.md` | Review current documentation |
214
+ | Component file | Make changes using design tokens |
215
+ | Storybook story | Update/add story for new variant |
216
+ | Test file | Add tests for new behavior, update snapshots if needed |
217
+
218
+ ### New Database Table/Column
219
+
220
+ | File | Action Required |
221
+ | ----------------------------------------- | ------------------------------------------------------ |
222
+ | `supabase/migrations/YYYYMMDD_<name>.sql` | Create migration file |
223
+ | `types/supabase.ts` | Regenerate: `pnpm supabase:gen` |
224
+ | `types/index.ts` | Add application-level TypeScript interface (camelCase) |
225
+ | `app/actions/<entity>.ts` | Add CRUD server actions |
226
+ | `lib/validations/<entity>.schema.ts` | Add Zod validation schema |
227
+
228
+ ### Migration Execution Policy
229
+
230
+ > **⚠️ MANDATORY RULE**: All database migrations MUST be run on BOTH dev and prod databases.
231
+
232
+ **Execution Order:**
233
+
234
+ 1. **Run on DEV first** — Always run migrations on the development database first
235
+ 2. **If DEV fails, STOP** — Fix the issue and retry on dev before proceeding
236
+ 3. **Never proceed to PROD with a failing dev migration**
237
+ 4. **Verify status on both databases** after completion
238
+
239
+ **Required Steps:**
240
+
241
+ ```bash
242
+ # Step 1: Run migration on DEV
243
+ ./scripts/db.sh link dev
244
+ ./scripts/db.sh migrate
245
+ ./scripts/db.sh status # Verify success
246
+
247
+ # Step 2: Only if DEV succeeds, run on PROD
248
+ ./scripts/db.sh link prod
249
+ ./scripts/db.sh migrate
250
+ ./scripts/db.sh status # Verify success
251
+
252
+ # OR use the combined command:
253
+ ./scripts/db.sh migrate-both
254
+ ```
255
+
256
+ **Failure Handling:**
257
+
258
+ | Scenario | Action |
259
+ | ------------------------ | ---------------------------------------------------------------------- |
260
+ | DEV migration fails | STOP. Fix the migration SQL, then retry `./scripts/db.sh migrate-both` |
261
+ | DEV succeeds, PROD fails | Investigate PROD-specific issue. May need manual intervention. |
262
+ | Both succeed | Verify with `./scripts/db.sh status` on both environments |
263
+
264
+ ---
265
+
266
+ ### New Hook
267
+
268
+ | File | Action Required |
269
+ | ------------------------------ | --------------------------------------------- |
270
+ | `hooks/use<HookName>.tsx` | Create with `use` prefix + PascalCase |
271
+ | `hooks/use<HookName>.test.tsx` | Add unit test (required for hooks with logic) |
272
+ | `types/index.ts` | Add return type interfaces if complex |
273
+
274
+ ### New Library Addition
275
+
276
+ | File | Action Required |
277
+ | --------------------------------- | ------------------------------------------ |
278
+ | `package.json` | Add dependency (only after approval) |
279
+ | `guidelines/LIBRARY_INVENTORY.md` | Document: name, version, purpose, patterns |
280
+ | Relevant source file | Add usage example |
281
+
282
+ ### New Feature (Full)
283
+
284
+ Combine all applicable checklists above, plus:
285
+
286
+ | File | Action Required |
287
+ | ------------------------- | -------------------------------------------- |
288
+ | `plans/<FEATURE>_PLAN.md` | Create implementation plan with checklist |
289
+ | `e2e/<feature>.spec.ts` | Add E2E test for critical paths |
290
+ | Component files | Add `data-testid` attributes for testability |
291
+
292
+ ---
293
+
294
+ ## UI Component Audit
295
+
296
+ > **MANDATORY**: Complete this audit before creating or modifying ANY UI component.
297
+
298
+ ### Pre-Work Checklist
299
+
300
+ Before writing any code:
301
+
302
+ - [ ] **Checked `components/ui/`** — Does a primitive already exist?
303
+ - [ ] **Checked Feature Components** in `guidelines/COMPONENT_LIBRARY.md`
304
+ - [ ] **Checked Storybook** — Run `pnpm storybook` and browse existing components
305
+ - [ ] **Verified no existing component** meets the need (even with modifications)
306
+ - [ ] **If modifying**: Reviewed current tests and stories
307
+
308
+ ### Decision Flow
309
+
310
+ ```
311
+ Need exists?
312
+ ├── YES: Existing component with correct behavior → USE IT
313
+ ├── YES: Existing component needs variant → ADD VARIANT (not new component)
314
+ ├── YES: Existing component needs refactor → REFACTOR with feature flag
315
+ └── NO: No existing component → CREATE NEW (with justification in PR)
316
+ ```
317
+
318
+ ### New Component Requirements
319
+
320
+ Every new component MUST have:
321
+
322
+ 1. **Feature Flag** — For controlled rollout (default: `false`)
323
+ 2. **Design Tokens** — Use `lib/design-tokens.ts`, never hardcode colors
324
+ 3. **Storybook Story** — See [Storybook Story Requirements](#storybook-story-requirements) below
325
+ 4. **Unit Tests** — Behavioral tests for all logic
326
+ 5. **Snapshot Tests** — One per variant/size combination
327
+ 6. **Documentation** — Added to `guidelines/COMPONENT_LIBRARY.md`
328
+
329
+ ### Storybook Story Requirements
330
+
331
+ > **⚠️ MANDATORY**: Every component in `components/ui/` or `components/ui/forms/` MUST have a corresponding `.stories.tsx` file.
332
+
333
+ **Story File Location:**
334
+
335
+ - Place stories in `components/` alongside dialog/feature components
336
+ - Example: `FormInput` → `components/FormInput.stories.tsx`
337
+
338
+ **Required Stories:**
339
+ Every component story file MUST include:
340
+
341
+ | Story | Description |
342
+ | ----------------- | --------------------------------------------- |
343
+ | `Default` | Basic usage with minimal props |
344
+ | `WithAllFeatures` | All major props enabled |
345
+ | `SizeVariants` | If component has size prop (sm/md/lg) |
346
+ | `States` | Disabled, error, loading states if applicable |
347
+ | `Controlled` | If component supports controlled mode |
348
+ | `FormExample` | Form integration example for form components |
349
+
350
+ **Story Template:**
351
+
352
+ ```tsx
353
+ import type { Meta, StoryObj } from '@storybook/nextjs-vite';
354
+ import { ComponentName } from './ui/component-name';
355
+ import { mockFeatureFlags } from '@/lib/feature-flags';
356
+
357
+ const meta: Meta<typeof ComponentName> = {
358
+ title: 'Category/ComponentName',
359
+ component: ComponentName,
360
+ tags: ['autodocs'],
361
+ parameters: { layout: 'centered' },
362
+ decorators: [
363
+ (Story) => {
364
+ mockFeatureFlags({ FEATURE_FLAG: true });
365
+ return (
366
+ <div className="w-[400px] p-4">
367
+ <Story />
368
+ </div>
369
+ );
370
+ },
371
+ ],
372
+ argTypes: {
373
+ // Define controls for all props
374
+ },
375
+ };
376
+
377
+ export default meta;
378
+ type Story = StoryObj<typeof ComponentName>;
379
+
380
+ export const Default: Story = {
381
+ args: {
382
+ // Minimal props
383
+ },
384
+ };
385
+ ```
386
+
387
+ **Story Validation Checklist:**
388
+
389
+ - [ ] Meta includes `tags: ["autodocs"]` for auto-generated docs
390
+ - [ ] Feature flag is mocked in decorator
391
+ - [ ] `argTypes` defined for all controllable props
392
+ - [ ] Renders in `pnpm storybook` without errors
393
+ - [ ] Interactive controls work in Storybook
394
+
395
+ ### Variant Addition Requirements
396
+
397
+ When adding a variant to existing component:
398
+
399
+ 1. **Existing tests pass** — Run `pnpm test:components`
400
+ 2. **New variant tested** — Add tests for new behavior
401
+ 3. **Snapshot updated** — Run `pnpm test:update-snapshots` if intentional change
402
+ 4. **Storybook updated** — Add story or update existing
403
+ 5. **Documentation updated** — If variant changes usage patterns
404
+
405
+ ---
406
+
407
+ ## Retroactive Compliance Rule
408
+
409
+ > **⚠️ CRITICAL**: When ANY new rule, standard, or pattern is added to `/guidelines/`, the codebase MUST be brought into compliance BEFORE continuing other work.
410
+
411
+ ### Trigger
412
+
413
+ This rule applies when:
414
+
415
+ - New guideline file is created in `/guidelines/`
416
+ - Existing guideline is updated with new requirements
417
+ - New coding standard or pattern is established
418
+ - New component library component is mandated for existing patterns
419
+
420
+ ### Mandatory Workflow
421
+
422
+ | Step | Action | Deliverable |
423
+ | ---- | ------------------------- | ----------------------------------------------- |
424
+ | 1 | **Create audit plan** | `plans/<RULE_NAME>_COMPLIANCE_PLAN.md` |
425
+ | 2 | **Full codebase scan** | List ALL files/locations violating the new rule |
426
+ | 3 | **Prioritize violations** | Critical → High → Medium → Low |
427
+ | 4 | **Remediate violations** | Fix all with tests, commit incrementally |
428
+ | 5 | **Verify compliance** | All checks pass, no violations remain |
429
+ | 6 | **Document completion** | Move plan to `plans/completed/` |
430
+
431
+ ### Priority Levels
432
+
433
+ | Priority | Definition | Timeline |
434
+ | ------------ | ---------------------------------------------- | ------------------------- |
435
+ | **Critical** | Breaks functionality, security issue | Fix immediately |
436
+ | **High** | Affects user experience, inconsistent behavior | Fix before next release |
437
+ | **Medium** | Code quality, maintainability | Fix within current sprint |
438
+ | **Low** | Nice-to-have, cosmetic | Fix when touching file |
439
+
440
+ ### Blocking Rule
441
+
442
+ **No new feature work may begin until:**
443
+
444
+ 1. All Critical violations are fixed
445
+ 2. All High violations are fixed OR have approved timeline
446
+ 3. Audit plan is created and tracked
447
+
448
+ ### Audit Plan Template
449
+
450
+ Create `plans/<RULE_NAME>_COMPLIANCE_PLAN.md`:
451
+
452
+ ```markdown
453
+ # [Rule Name] Compliance Audit
454
+
455
+ ## Rule Summary
456
+
457
+ Brief description of the new rule/standard.
458
+
459
+ ## Audit Date
460
+
461
+ YYYY-MM-DD
462
+
463
+ ## Violations Found
464
+
465
+ ### Critical (Must fix immediately)
466
+
467
+ - [ ] `path/to/file.tsx:L123` - Description of violation
468
+
469
+ ### High (Fix before next release)
470
+
471
+ - [ ] `path/to/file.tsx:L456` - Description of violation
472
+
473
+ ### Medium (Fix within sprint)
474
+
475
+ - [ ] `path/to/file.tsx:L789` - Description of violation
476
+
477
+ ### Low (Fix when touching file)
478
+
479
+ - [ ] `path/to/file.tsx:L012` - Description of violation
480
+
481
+ ## Remediation Progress
482
+
483
+ - [ ] All critical violations fixed
484
+ - [ ] All high violations fixed
485
+ - [ ] All medium violations fixed
486
+ - [ ] Tests passing
487
+ - [ ] Build passing
488
+
489
+ ## Completion Date
490
+
491
+ YYYY-MM-DD (filled when complete)
492
+ ```
493
+
494
+ ---
495
+
496
+ ## Coding Standards
497
+
498
+ ### Import Ordering
499
+
500
+ Imports MUST follow this exact order, with blank lines between groups:
501
+
502
+ ```typescript
503
+ // 1. Directive (MUST be first line if present)
504
+ 'use server';
505
+ // OR
506
+ 'use client';
507
+
508
+ // 2. React imports
509
+ import React, { useState, useEffect, useCallback } from 'react';
510
+
511
+ // 3. External library imports (alphabetical)
512
+ import { useDrag } from 'react-dnd';
513
+ import { QueryClient } from '@tanstack/react-query';
514
+ import { z } from 'zod';
515
+
516
+ // 4. Internal absolute imports with @/ prefix (alphabetical by path)
517
+ import { createServerClient } from '@/lib/supabase/server';
518
+ import { verifyBoardAccess } from '@/utils/authorization.server';
519
+ import type { Task, Sprint } from '@/types';
520
+ import { useAuth } from '@/app/providers';
521
+
522
+ // 5. Relative imports (parent directories first, then siblings)
523
+ import { Button } from '../ui/button';
524
+ import { TaskCard } from './TaskCard';
525
+ import type { LocalType } from './types';
526
+ ```
527
+
528
+ ### Naming Conventions
529
+
530
+ | Type | Convention | Example |
531
+ | ------------------- | ------------------------ | ------------------------------------ |
532
+ | Component files | PascalCase.tsx | `TaskCard.tsx` |
533
+ | Server action files | camelCase.ts | `tasks.ts`, `organizations.ts` |
534
+ | Hook files | usePascalCase.tsx | `useAuthorization.tsx` |
535
+ | Test files | Original.test.tsx | `TaskCard.test.tsx` |
536
+ | Schema files | entity.schema.ts | `task.schema.ts` |
537
+ | Migration files | YYYYMMDD_description.sql | `20240115_add_teams.sql` |
538
+ | Functions | camelCase | `getOrganizations`, `handleSubmit` |
539
+ | Types/Interfaces | PascalCase | `Task`, `OrganizationMember` |
540
+ | Constants | SCREAMING_SNAKE_CASE | `ROLE_HIERARCHY`, `AUTH_STORAGE_KEY` |
541
+ | Database columns | snake_case | `created_at`, `organization_id` |
542
+ | Frontend props | camelCase | `taskId`, `onUpdate`, `isLoading` |
543
+ | Zod schemas | PascalCaseSchema | `TaskSchema`, `CreateTaskSchema` |
544
+
545
+ ### Error Handling Pattern
546
+
547
+ **Server Actions** - Always return `{ data, error }` or `{ success, error }`:
548
+
549
+ ```typescript
550
+ export async function getEntity(
551
+ id: string
552
+ ): Promise<{ data: Entity | null; error: string | null }> {
553
+ try {
554
+ // 1. Verify access FIRST
555
+ const access = await verifyEntityAccess(id);
556
+ if (!access.hasAccess) {
557
+ return { data: null, error: access.error || 'Access denied' };
558
+ }
559
+
560
+ const supabase = await createServerClient();
561
+ const { data, error } = await supabase.from('entities').select('*').eq('id', id).single();
562
+
563
+ if (error) {
564
+ console.error('Error fetching entity:', error);
565
+ return { data: null, error: error.message };
566
+ }
567
+
568
+ return { data: transformEntity(data), error: null };
569
+ } catch (err) {
570
+ console.error('Unexpected error:', err);
571
+ return { data: null, error: 'An unexpected error occurred' };
572
+ }
573
+ }
574
+ ```
575
+
576
+ **Components** - Use toast for user feedback:
577
+
578
+ ```typescript
579
+ const handleSubmit = async () => {
580
+ try {
581
+ const { data, error } = await createEntity(formData);
582
+ if (error) {
583
+ toast.error(error);
584
+ return;
585
+ }
586
+ toast.success('Created successfully');
587
+ onSuccess?.(data);
588
+ } catch (err) {
589
+ toast.error('An unexpected error occurred');
590
+ }
591
+ };
592
+ ```
593
+
594
+ ### TypeScript Strictness
595
+
596
+ - ❌ **NO `any` type** - Use proper typing or `unknown` with type guards
597
+ - ✅ **Explicit return types** on all exported functions
598
+ - ✅ **Interface over type** for object shapes (unless union type needed)
599
+ - ✅ **Use `as const`** for readonly arrays: `["admin", "member"] as const`
600
+ - ✅ **Prefer type inference** for local variables when obvious
601
+
602
+ ---
603
+
604
+ ## Architecture Rules
605
+
606
+ ### Server Actions
607
+
608
+ 1. **`'use server'` MUST be the first line** of every server action file
609
+ 2. **Authorization check MUST come first** before any data access
610
+ 3. **Transform database snake_case to camelCase** for frontend consumption
611
+ 4. **Use `revalidatePath()`** after mutations to update cached data
612
+ 5. **Never expose sensitive data** - filter fields before returning
613
+
614
+ ```typescript
615
+ 'use server';
616
+
617
+ import { createServerClient } from '@/lib/supabase/server';
618
+ import { verifyBoardAccess } from '@/utils/authorization.server';
619
+ import { revalidatePath } from 'next/cache';
620
+ import type { Task } from '@/types';
621
+
622
+ interface TaskRow {
623
+ id: string;
624
+ ticket_id: string;
625
+ board_id: string;
626
+ created_at: string;
627
+ }
628
+
629
+ function transformTask(row: TaskRow): Task {
630
+ return {
631
+ id: row.id,
632
+ ticketId: row.ticket_id,
633
+ boardId: row.board_id,
634
+ createdAt: row.created_at,
635
+ };
636
+ }
637
+
638
+ export async function getTasks(
639
+ boardId: string
640
+ ): Promise<{ data: Task[] | null; error: string | null }> {
641
+ // 1. Authorization FIRST
642
+ const access = await verifyBoardAccess(boardId);
643
+ if (!access.hasAccess) {
644
+ return { data: null, error: access.error || 'Access denied' };
645
+ }
646
+
647
+ // 2. Fetch data
648
+ const supabase = await createServerClient();
649
+ const { data, error } = await supabase.from('tasks').select('*').eq('board_id', boardId);
650
+
651
+ if (error) {
652
+ return { data: null, error: error.message };
653
+ }
654
+
655
+ // 3. Transform and return
656
+ return { data: data.map(transformTask), error: null };
657
+ }
658
+ ```
659
+
660
+ ### Component Patterns
661
+
662
+ 1. **`'use client'` for interactive components** - Any component with state, effects, or event handlers
663
+ 2. **Hooks at the top** - All hooks must be called before any conditional returns
664
+ 3. **Destructure props with defaults** - `({ optional = defaultValue }: Props)`
665
+ 4. **Add `data-testid` for testable elements** - Required for E2E test targets
666
+
667
+ ```typescript
668
+ "use client";
669
+
670
+ import React, { useState, useEffect } from "react";
671
+ import { useAuth } from "@/app/providers";
672
+ import { Button } from "@/components/ui/button";
673
+
674
+ interface TaskCardProps {
675
+ task: Task;
676
+ onEdit?: (task: Task) => void;
677
+ readOnly?: boolean;
678
+ }
679
+
680
+ export function TaskCard({ task, onEdit, readOnly = false }: TaskCardProps) {
681
+ // 1. Hooks first
682
+ const { user } = useAuth();
683
+ const [isEditing, setIsEditing] = useState(false);
684
+
685
+ // 2. Effects
686
+ useEffect(() => {
687
+ // ...
688
+ }, [task.id]);
689
+
690
+ // 3. Handlers
691
+ const handleEdit = () => {
692
+ if (!readOnly) {
693
+ setIsEditing(true);
694
+ onEdit?.(task);
695
+ }
696
+ };
697
+
698
+ // 4. Render
699
+ return (
700
+ <div data-testid={`task-card-${task.id}`}>
701
+ <h3>{task.title}</h3>
702
+ {!readOnly && (
703
+ <Button data-testid="edit-task-btn" onClick={handleEdit}>
704
+ Edit
705
+ </Button>
706
+ )}
707
+ </div>
708
+ );
709
+ }
710
+ ```
711
+
712
+ ### Authorization Flow
713
+
714
+ 1. **Server-side**: Use `verify*Access()` functions from `@/utils/authorization.server`
715
+ 2. **Client-side**: Use `useAuthorization()` hook for permission checks
716
+ 3. **Never trust client-side checks alone** - Always verify on server
717
+
718
+ ```typescript
719
+ // Server action
720
+ import { verifyBoardAccess, requireContributorAccess } from '@/utils/authorization.server';
721
+
722
+ export async function createTask(boardId: string, data: CreateTaskData) {
723
+ const boardAccess = await verifyBoardAccess(boardId);
724
+ if (!boardAccess.hasAccess) {
725
+ return { data: null, error: 'Access denied' };
726
+ }
727
+
728
+ // For write operations, also check role
729
+ if (boardAccess.workspaceId) {
730
+ try {
731
+ await requireContributorAccess(boardAccess.workspaceId);
732
+ } catch (err) {
733
+ return { data: null, error: (err as Error).message };
734
+ }
735
+ }
736
+
737
+ // ... proceed with creation
738
+ }
739
+ ```
740
+
741
+ ```typescript
742
+ // Client component
743
+ import { useAuthorization } from "@/hooks/useAuthorization";
744
+
745
+ function TaskActions({ task }: { task: Task }) {
746
+ const { can } = useAuthorization();
747
+
748
+ return (
749
+ <>
750
+ {can.editTask() && <Button onClick={handleEdit}>Edit</Button>}
751
+ {can.deleteTask() && <Button onClick={handleDelete}>Delete</Button>}
752
+ </>
753
+ );
754
+ }
755
+ ```
756
+
757
+ ---
758
+
759
+ ## Library Usage Policy
760
+
761
+ ### Rule 1: Use Existing Libraries First
762
+
763
+ Before suggesting any solution, check [LIBRARY_INVENTORY.md](LIBRARY_INVENTORY.md). Common solutions already available:
764
+
765
+ | Need | Use This | NOT This |
766
+ | ------------- | -------------------------------------- | ---------------------------------- |
767
+ | UI Components | `@radix-ui/*` + `components/ui/*` | Installing new UI library |
768
+ | Icons | `lucide-react` | Other icon libraries |
769
+ | Drag & Drop | `react-dnd` | @dnd-kit, react-beautiful-dnd |
770
+ | Data Fetching | `@tanstack/react-query` | SWR, custom fetch hooks |
771
+ | Validation | `zod` | yup, joi, other validators |
772
+ | Date Handling | `date-fns` | moment, dayjs |
773
+ | Styling | Tailwind CSS + `clsx`/`tailwind-merge` | styled-components, emotion |
774
+ | Forms | `react-hook-form` | formik, other form libraries |
775
+ | Rich Text | `@tiptap/*` | Draft.js, Slate, Quill |
776
+ | Toasts | `sonner` | react-toastify, other toast libs |
777
+ | Animations | `motion` (framer-motion) | react-spring, other animation libs |
778
+
779
+ ### Rule 2: Ask Before Adding New Libraries
780
+
781
+ If a task seems to require a new library:
782
+
783
+ 1. **STOP** - Do not install or add to `package.json`
784
+ 2. **ASK** - "This would require adding `[library]`. Should I proceed, or would you prefer an alternative approach using existing tools?"
785
+ 3. **JUSTIFY** - Explain why existing libraries cannot solve the problem
786
+ 4. **WAIT** - Only proceed after explicit user approval
787
+
788
+ ### Rule 3: Document Approved Libraries
789
+
790
+ When a new library is approved and added:
791
+
792
+ 1. Add entry to [LIBRARY_INVENTORY.md](LIBRARY_INVENTORY.md)
793
+ 2. Include: Package name, version, purpose, usage location, approved patterns
794
+ 3. Update this document if it replaces an existing recommendation
795
+
796
+ ---
797
+
798
+ ## Testing Requirements
799
+
800
+ ### When Tests Are Required
801
+
802
+ | Change Type | Unit Test Required | Storybook Story Required | E2E Test Required |
803
+ | ------------------------------------------- | ------------------ | ------------------------ | ----------------------- |
804
+ | New UI component (`components/ui/`) | ✅ Yes | ✅ **Yes (mandatory)** | ❌ No |
805
+ | New form component (`components/ui/forms/`) | ✅ Yes | ✅ **Yes (mandatory)** | ❌ No |
806
+ | New hook with logic | ✅ Yes | ❌ No | ❌ No |
807
+ | Complex feature component | ✅ Yes | ⚠️ Recommended | ⚠️ If critical path |
808
+ | Server action | ⚠️ Recommended | ❌ No | ❌ No |
809
+ | Utility function | ✅ Yes | ❌ No | ❌ No |
810
+ | New feature | ✅ Yes | ⚠️ If has UI | ✅ Yes (critical paths) |
811
+ | Bug fix | ✅ Regression test | ❌ No | ⚠️ If E2E exists |
812
+
813
+ ### Test File Conventions
814
+
815
+ - Place test files alongside source: `Component.tsx` → `Component.test.tsx`
816
+ - Use fixtures from `test/fixtures.tsx`
817
+ - Mock Supabase using MSW handlers in `lib/test-utils/`
818
+
819
+ ### Required `data-testid` Attributes
820
+
821
+ Add `data-testid` to elements that E2E tests need to interact with:
822
+
823
+ ```typescript
824
+ // Good - specific, predictable IDs
825
+ <button data-testid="create-task-btn">Create</button>
826
+ <div data-testid={`task-card-${task.id}`}>...</div>
827
+ <input data-testid="task-title-input" />
828
+
829
+ // Bad - generic or missing
830
+ <button>Create</button>
831
+ <div className="task-card">...</div>
832
+ ```
833
+
834
+ ---
835
+
836
+ ## PR Workflow
837
+
838
+ ### Using Agent to Fill PR Descriptions
839
+
840
+ When creating a PR, use Agent to automatically fill out the PR description:
841
+
842
+ 1. Stage your changes: `git add .`
843
+ 2. Ask Agent: "Fill out the PR description for my staged changes using the PR template"
844
+ 3. Agent will analyze the diff and populate:
845
+ - Summary of changes
846
+ - Type of change (feature/bugfix/refactor/etc.)
847
+ - Scope from the fixed list
848
+ - Files changed with checklist verification
849
+ - Testing performed
850
+ - Breaking changes assessment
851
+
852
+ ### Pre-PR Checklist
853
+
854
+ Before creating a PR, Agent must verify:
855
+
856
+ - [ ] All required files for the change type have been touched
857
+ - [ ] No `any` types introduced
858
+ - [ ] Authorization added for new data access
859
+ - [ ] Types updated in `types/index.ts`
860
+ - [ ] Unit tests added/updated and passing (`pnpm test`)
861
+ - [ ] E2E tests pass for affected flows (`pnpm test:e2e`)
862
+ - [ ] No unapproved new libraries added
863
+ - [ ] Import order follows the standard
864
+ - [ ] `data-testid` attributes added for testable elements
865
+
866
+ ---
867
+
868
+ ## Quick Reference: Path Aliases
869
+
870
+ | Alias | Path | Example |
871
+ | ---------------- | ---------------- | -------------------------- |
872
+ | `@/*` | Root | `@/utils/authorization` |
873
+ | `@/components/*` | `./components/*` | `@/components/ui/button` |
874
+ | `@/lib/*` | `./lib/*` | `@/lib/supabase/server` |
875
+ | `@/hooks/*` | `./hooks/*` | `@/hooks/useAuthorization` |
876
+ | `@/types/*` | `./types/*` | `@/types/index` |
877
+ | `@test/*` | `./test/*` | `@test/fixtures` |
878
+ | `@app/types` | `./types/index` | `@app/types` |
879
+
880
+ ---
881
+
882
+ ## Related Documents
883
+
884
+ - [SINGLE_SOURCE_OF_TRUTH.md](SINGLE_SOURCE_OF_TRUTH.md) - Canonical file locations
885
+ - [LIBRARY_INVENTORY.md](LIBRARY_INVENTORY.md) - Approved dependencies
886
+ - [TESTING_STRATEGY.md](TESTING_STRATEGY.md) - Testing patterns
887
+ - [BRANCHING_STRATEGY.md](BRANCHING_STRATEGY.md) - Git workflow