@dv.nghiem/flowdeck 0.2.3 → 0.3.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.
Files changed (100) hide show
  1. package/README.md +24 -41
  2. package/dist/hooks/memory-hook.d.ts +21 -0
  3. package/dist/hooks/memory-hook.d.ts.map +1 -0
  4. package/dist/hooks/orchestrator-guard-hook.d.ts +2 -1
  5. package/dist/hooks/orchestrator-guard-hook.d.ts.map +1 -1
  6. package/dist/hooks/todo-hook.d.ts +1 -7
  7. package/dist/hooks/todo-hook.d.ts.map +1 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +649 -310
  10. package/dist/services/memory-store.d.ts +40 -0
  11. package/dist/services/memory-store.d.ts.map +1 -0
  12. package/dist/services/telemetry.d.ts +1 -1
  13. package/dist/services/telemetry.d.ts.map +1 -1
  14. package/dist/tools/memory-search.d.ts +3 -0
  15. package/dist/tools/memory-search.d.ts.map +1 -0
  16. package/docs/commands/fd-doctor.md +21 -0
  17. package/docs/commands/fd-quick.md +33 -0
  18. package/docs/commands/fd-reflect.md +23 -0
  19. package/docs/commands/fd-status.md +31 -0
  20. package/docs/commands/fd-translate-intent.md +17 -0
  21. package/docs/commands.md +209 -271
  22. package/docs/configuration.md +2 -1
  23. package/docs/index.md +22 -28
  24. package/docs/memory.md +69 -0
  25. package/docs/quick-start.md +1 -1
  26. package/docs/workflows.md +72 -320
  27. package/package.json +1 -2
  28. package/src/commands/fd-deploy-check.md +189 -34
  29. package/src/commands/fd-discuss.md +44 -6
  30. package/src/commands/fd-fix-bug.md +47 -20
  31. package/src/commands/fd-map-codebase.md +66 -18
  32. package/src/commands/fd-multi-repo.md +130 -6
  33. package/src/commands/fd-new-feature.md +164 -21
  34. package/src/commands/fd-new-project.md +14 -1
  35. package/src/commands/fd-plan.md +66 -44
  36. package/src/commands/fd-quick.md +60 -0
  37. package/src/commands/fd-reflect.md +41 -2
  38. package/src/commands/fd-status.md +84 -0
  39. package/src/commands/fd-write-docs.md +55 -23
  40. package/src/rules/README.md +8 -7
  41. package/src/skills/agent-harness-construction/SKILL.md +227 -0
  42. package/src/skills/api-design/SKILL.md +5 -0
  43. package/src/skills/backend-patterns/SKILL.md +105 -0
  44. package/src/skills/clean-architecture/SKILL.md +85 -0
  45. package/src/skills/cqrs/SKILL.md +230 -0
  46. package/src/skills/ddd-architecture/SKILL.md +104 -0
  47. package/src/skills/django-patterns/SKILL.md +304 -0
  48. package/src/skills/django-tdd/SKILL.md +297 -0
  49. package/src/skills/event-driven-architecture/SKILL.md +152 -0
  50. package/src/skills/frontend-pattern/SKILL.md +159 -0
  51. package/src/skills/hexagonal-architecture/SKILL.md +80 -0
  52. package/src/skills/layered-architecture/SKILL.md +64 -0
  53. package/src/skills/postgres-patterns/SKILL.md +74 -0
  54. package/src/skills/python-patterns/SKILL.md +5 -0
  55. package/src/skills/saga-architecture/SKILL.md +113 -0
  56. package/dist/tools/run-parallel.d.ts +0 -4
  57. package/dist/tools/run-parallel.d.ts.map +0 -1
  58. package/docs/command-migration.md +0 -175
  59. package/docs/commands/fd-analyze-change.md +0 -107
  60. package/docs/commands/fd-dashboard.md +0 -11
  61. package/docs/commands/fd-evaluate-risk.md +0 -134
  62. package/docs/commands/fd-guarded-edit.md +0 -105
  63. package/docs/commands/fd-progress.md +0 -11
  64. package/docs/commands/fd-review-code.md +0 -29
  65. package/docs/commands/fd-roadmap.md +0 -10
  66. package/docs/commands/fd-settings.md +0 -10
  67. package/docs/parallel-execution.md +0 -227
  68. package/src/commands/fd-analyze-change.md +0 -57
  69. package/src/commands/fd-approve.md +0 -64
  70. package/src/commands/fd-blast-radius.md +0 -49
  71. package/src/commands/fd-dashboard.md +0 -57
  72. package/src/commands/fd-evaluate-risk.md +0 -62
  73. package/src/commands/fd-guarded-edit.md +0 -69
  74. package/src/commands/fd-impact-radar.md +0 -51
  75. package/src/commands/fd-learn.md +0 -36
  76. package/src/commands/fd-progress.md +0 -50
  77. package/src/commands/fd-regression-predict.md +0 -57
  78. package/src/commands/fd-review-code.md +0 -62
  79. package/src/commands/fd-review-route.md +0 -54
  80. package/src/commands/fd-roadmap.md +0 -46
  81. package/src/commands/fd-settings.md +0 -57
  82. package/src/commands/fd-test-gap.md +0 -54
  83. package/src/commands/fd-volatility-map.md +0 -64
  84. package/src/commands/fd-workspace-status.md +0 -34
  85. package/src/skills/parallel-execute/SKILL.md +0 -92
  86. package/src/workflows/debug-flow.md +0 -119
  87. package/src/workflows/deploy-check-flow.md +0 -98
  88. package/src/workflows/discuss-flow.md +0 -97
  89. package/src/workflows/execute-flow.md +0 -233
  90. package/src/workflows/execute-phase.md +0 -145
  91. package/src/workflows/fix-bug-flow.md +0 -210
  92. package/src/workflows/map-codebase-flow.md +0 -92
  93. package/src/workflows/multi-repo-flow.md +0 -226
  94. package/src/workflows/parallel-execution-flow.md +0 -236
  95. package/src/workflows/plan-flow.md +0 -126
  96. package/src/workflows/plan-phase.md +0 -101
  97. package/src/workflows/refactor-flow.md +0 -122
  98. package/src/workflows/review-code-flow.md +0 -105
  99. package/src/workflows/spec-driven-flow.md +0 -43
  100. package/src/workflows/write-docs-flow.md +0 -95
@@ -0,0 +1,297 @@
1
+ ---
2
+ name: django-tdd
3
+ description: Test-driven development patterns for Django covering pytest, Django TestCase, factory_boy, test client, and best practices for testing Django applications.
4
+ origin: FlowDeck
5
+ ---
6
+
7
+ # Django TDD Skill
8
+
9
+ Test-driven development workflow for Django applications. Covers pytest fixtures, Django TestCase, factory_boy, and testing patterns.
10
+
11
+ ## When to Activate
12
+
13
+ Activate when:
14
+ - Writing new Django features using TDD workflow
15
+ - Adding tests to existing Django applications
16
+ - Debugging test failures in Django projects
17
+ - Setting up testing infrastructure for Django apps
18
+
19
+ ## TDD Workflow
20
+
21
+ 1. Write a failing test (RED)
22
+ 2. Run the test - it should fail
23
+ 3. Write minimal implementation (GREEN)
24
+ 4. Run tests - they should pass
25
+ 5. Refactor (IMPROVE)
26
+ 6. Verify coverage
27
+
28
+ ## Test Setup
29
+
30
+ ### Using pytest with Django
31
+
32
+ ```python
33
+ # conftest.py
34
+ import pytest
35
+ import django
36
+ from django.conf import settings
37
+
38
+ @pytest.fixture(scope="session")
39
+ def django_db_setup():
40
+ settings.DATABASES["default"] = {
41
+ "ENGINE": "django.db.backends.sqlite3",
42
+ "NAME": ":memory:",
43
+ }
44
+
45
+ @pytest.fixture
46
+ def api_client():
47
+ from rest_framework.test import APIClient
48
+ return APIClient()
49
+ ```
50
+
51
+ ### Django TestCase
52
+
53
+ ```python
54
+ from django.test import TestCase, Client
55
+ from django.urls import reverse
56
+ from django.contrib.auth import get_user_model
57
+ from .models import Article, Author
58
+
59
+ User = get_user_model()
60
+
61
+ class ArticleViewTest(TestCase):
62
+ def setUp(self):
63
+ self.client = Client()
64
+ self.user = User.objects.create_user(
65
+ email='test@example.com',
66
+ password='testpass123'
67
+ )
68
+ self.author = Author.objects.create(name='Author', email='a@test.com')
69
+ self.article = Article.objects.create(
70
+ title='Test',
71
+ content='Content',
72
+ author=self.author,
73
+ status='published'
74
+ )
75
+ ```
76
+
77
+ ## Testing Views
78
+
79
+ ### List View Tests
80
+
81
+ ```python
82
+ def test_article_list_view(self):
83
+ response = self.client.get(reverse('article-list'))
84
+ self.assertEqual(response.status_code, 200)
85
+ self.assertContains(response, 'Test')
86
+ self.assertTemplateUsed(response, 'articles/list.html')
87
+ self.assertEqual(len(response.context['articles']), 1)
88
+ ```
89
+
90
+ ### Detail View Tests
91
+
92
+ ```python
93
+ def test_article_detail_view(self):
94
+ response = self.client.get(
95
+ reverse('article-detail', kwargs={'pk': self.article.pk})
96
+ )
97
+ self.assertEqual(response.status_code, 200)
98
+ self.assertContains(response, self.article.title)
99
+
100
+ def test_article_detail_not_found(self):
101
+ response = self.client.get(
102
+ reverse('article-detail', kwargs={'pk': 9999})
103
+ )
104
+ self.assertEqual(response.status_code, 404)
105
+ ```
106
+
107
+ ### Authentication Tests
108
+
109
+ ```python
110
+ def test_create_article_requires_login(self):
111
+ response = self.client.get(reverse('article-create'))
112
+ self.assertRedirects(response, '/accounts/login/?next=/articles/create/')
113
+
114
+ def test_create_article_authenticated(self):
115
+ self.client.login(email='test@example.com', password='testpass123')
116
+ response = self.client.post(reverse('article-create'), {
117
+ 'title': 'New Article',
118
+ 'content': 'New content',
119
+ 'status': 'draft',
120
+ })
121
+ self.assertEqual(response.status_code, 302)
122
+ self.assertTrue(Article.objects.filter(title='New Article').exists())
123
+ ```
124
+
125
+ ### JSON API Tests
126
+
127
+ ```python
128
+ def test_json_response(self):
129
+ response = self.client.get(
130
+ reverse('api-articles'),
131
+ content_type='application/json'
132
+ )
133
+ self.assertEqual(response.status_code, 200)
134
+ data = response.json()
135
+ self.assertIn('articles', data)
136
+ ```
137
+
138
+ ## Testing Models
139
+
140
+ ```python
141
+ def test_article_creation(self):
142
+ article = Article.objects.create(
143
+ title='Test Article',
144
+ content='Test content',
145
+ author=self.author,
146
+ status='draft'
147
+ )
148
+ self.assertEqual(article.title, 'Test Article')
149
+ self.assertEqual(article.status, 'draft')
150
+ self.assertEqual(str(article), 'Test Article')
151
+
152
+ def test_article_ordering(self):
153
+ Article.objects.create(title='Second', author=self.author)
154
+ Article.objects.create(title='First', author=self.author)
155
+ articles = list(Article.objects.all())
156
+ self.assertEqual(articles[0].title, 'First')
157
+ ```
158
+
159
+ ## Factory Boy Fixtures
160
+
161
+ ### Defining Factories
162
+
163
+ ```python
164
+ # factories.py
165
+ import factory
166
+ from factory.django import DjangoModelFactory
167
+ from .models import Author, Article
168
+
169
+ class AuthorFactory(DjangoModelFactory):
170
+ class Meta:
171
+ model = Author
172
+
173
+ name = factory.Sequence(lambda n: f"Author {n}")
174
+ email = factory.LazyAttribute(lambda obj: f"{obj.name.replace(' ', '')}@example.com")
175
+
176
+ class ArticleFactory(DjangoModelFactory):
177
+ class Meta:
178
+ model = Article
179
+
180
+ title = factory.Sequence(lambda n: f"Article {n}")
181
+ content = "Test content"
182
+ author = factory.SubFactory(AuthorFactory)
183
+ status = 'draft'
184
+ ```
185
+
186
+ ### Using Factories in Tests
187
+
188
+ ```python
189
+ from .factories import AuthorFactory, ArticleFactory
190
+
191
+ def test_article_with_factory(self):
192
+ author = AuthorFactory(name="Jane Doe")
193
+ article = ArticleFactory(title="Test", author=author)
194
+ assert article.author.name == "Jane Doe"
195
+ assert article.title == "Test"
196
+ ```
197
+
198
+ ## Pytest Fixtures
199
+
200
+ ### Basic Fixture
201
+
202
+ ```python
203
+ import pytest
204
+
205
+ @pytest.fixture
206
+ def sample_data():
207
+ return {"name": "test", "value": 42}
208
+
209
+ def test_sample_data(sample_data):
210
+ assert sample_data["name"] == "test"
211
+ assert sample_data["value"] == 42
212
+ ```
213
+
214
+ ### Fixture with Teardown
215
+
216
+ ```python
217
+ import tempfile
218
+ import os
219
+
220
+ @pytest.fixture
221
+ def temp_file():
222
+ fd, path = tempfile.mkstemp()
223
+ os.write(fd, b"test content")
224
+ os.close(fd)
225
+ yield path
226
+ os.unlink(path)
227
+
228
+ def test_temp_file(temp_file):
229
+ assert os.path.exists(temp_file)
230
+ with open(temp_file) as f:
231
+ assert f.read() == "test content"
232
+ ```
233
+
234
+ ### Parametrized Fixtures
235
+
236
+ ```python
237
+ @pytest.fixture(params=["mysql", "postgresql", "sqlite"])
238
+ def database_type(request):
239
+ return request.param
240
+
241
+ def test_database_type(database_type):
242
+ assert database_type in ["mysql", "postgresql", "sqlite"]
243
+ ```
244
+
245
+ ## Common Patterns
246
+
247
+ ### Testing Forms
248
+
249
+ ```python
250
+ def test_form_valid(self):
251
+ form_data = {
252
+ 'title': 'New Article',
253
+ 'content': 'Content',
254
+ 'author': self.author.pk,
255
+ 'status': 'draft',
256
+ }
257
+ form = ArticleForm(data=form_data)
258
+ self.assertTrue(form.is_valid())
259
+
260
+ def test_form_invalid(self):
261
+ form_data = {'title': '', 'content': 'Content'}
262
+ form = ArticleForm(data=form_data)
263
+ self.assertFalse(form.is_valid())
264
+ self.assertIn('title', form.errors)
265
+ ```
266
+
267
+ ### Testing Middleware
268
+
269
+ ```python
270
+ def test_middleware_process_request(self):
271
+ response = self.client.get('/articles/')
272
+ self.assertEqual(response.status_code, 200)
273
+ # Check middleware added expected headers or behavior
274
+ ```
275
+
276
+ ### Testing Signals
277
+
278
+ ```python
279
+ def test_signal_on_save(self):
280
+ article = ArticleFactory()
281
+ # Verify signal handlers executed (e.g., notifications sent)
282
+ ```
283
+
284
+ ## Coverage Verification
285
+
286
+ ```bash
287
+ # Run with coverage
288
+ pytest --cov=myapp --cov-report=html
289
+
290
+ # Minimum 80% coverage required
291
+ ```
292
+
293
+ ## Related Skills
294
+
295
+ - django-patterns
296
+ - python-patterns
297
+ - tdd-workflow
@@ -0,0 +1,152 @@
1
+ # Event-Driven Architecture
2
+
3
+ ## When to Activate
4
+
5
+ Activate when:
6
+ - Designing or implementing message-based communication between services
7
+ - Building systems that require asynchronous processing
8
+ - Decoupling producers from consumers in distributed systems
9
+ - Implementing event sourcing or audit trails
10
+ - Setting up webhooks, message queues, or pub/sub patterns
11
+
12
+ ## Steps
13
+
14
+ ### 1. Identify Event Boundaries
15
+
16
+ Define what constitutes an "event" in your system:
17
+ - Events are **facts** about something that happened (past tense: `OrderPlaced`, `PaymentProcessed`)
18
+ - Commands are **requests** for an action (present tense: `PlaceOrder`, `ProcessPayment`)
19
+ - Events should be **immutable** once emitted
20
+
21
+ ### 2. Choose the Right Messaging Pattern
22
+
23
+ | Pattern | Use Case | Examples |
24
+ |---------|----------|----------|
25
+ | Pub/Sub | One-to-many notification | Notifications, audit logs |
26
+ | Message Queue | Point-to-point processing | Order processing, email sending |
27
+ | Event Streaming | Durable, replayable event log | Event sourcing, analytics |
28
+ | Webhooks | External system integration | HTTP callbacks |
29
+
30
+ ### 3. Design Event Schema
31
+
32
+ ```typescript
33
+ interface Event<T> {
34
+ id: string; // Unique event identifier (UUID)
35
+ type: string; // Event type (e.g., "ORDER_PLACED")
36
+ version: string; // Schema version for evolution
37
+ timestamp: string; // ISO 8601 timestamp
38
+ source: string; // Origin service name
39
+ data: T; // Event payload
40
+ metadata?: Record<string, unknown>; // Optional tracing/correlation
41
+ }
42
+ ```
43
+
44
+ ### 4. Handle Eventual Consistency
45
+
46
+ - Design consumers to be **idempotent** (safe to process twice)
47
+ - Use **correlation IDs** to track event chains
48
+ - Implement **dead letter queues** for failed processing
49
+ - Set **retry policies** with exponential backoff
50
+
51
+ ### 5. Ensure Durability
52
+
53
+ - Use persistent message storage (not in-memory)
54
+ - Acknowledge messages only after successful processing
55
+ - Implement **at-least-once** delivery semantics
56
+
57
+ ## Examples
58
+
59
+ ### TypeScript Event Emitter
60
+
61
+ ```typescript
62
+ interface OrderEvent {
63
+ orderId: string;
64
+ customerId: string;
65
+ total: number;
66
+ items: OrderItem[];
67
+ }
68
+
69
+ class OrderEventPublisher {
70
+ private emitter: EventEmitter;
71
+
72
+ async publishOrderPlaced(event: OrderEvent): Promise<void> {
73
+ const message: Event<OrderEvent> = {
74
+ id: crypto.randomUUID(),
75
+ type: 'ORDER_PLACED',
76
+ version: '1.0',
77
+ timestamp: new Date().toISOString(),
78
+ source: 'order-service',
79
+ data: event,
80
+ metadata: {
81
+ correlationId: event.orderId,
82
+ partitionKey: event.customerId
83
+ }
84
+ };
85
+
86
+ await this.messageBroker.publish('orders.placed', message);
87
+ }
88
+ }
89
+ ```
90
+
91
+ ### Message Consumer with Retry
92
+
93
+ ```typescript
94
+ class OrderEventConsumer {
95
+ async handleOrderPlaced(event: Event<OrderEvent>): Promise<void> {
96
+ try {
97
+ // Idempotent processing
98
+ const existingOrder = await this.orderRepo.findById(event.data.orderId);
99
+ if (existingOrder) {
100
+ logger.info('Order already processed, skipping', { orderId: event.data.orderId });
101
+ return;
102
+ }
103
+
104
+ await this.orderService.processOrder(event.data);
105
+ await this.messageBroker.ack(event.id);
106
+ } catch (error) {
107
+ if (error instanceof TransientError) {
108
+ // Requeue with delay for retry
109
+ await this.messageBroker.requeue(event.id, { delay: 5000 });
110
+ } else {
111
+ // Send to dead letter queue
112
+ await this.messageBroker.sendToDlq(event, error);
113
+ }
114
+ }
115
+ }
116
+ }
117
+ ```
118
+
119
+ ### Event Schema Registry
120
+
121
+ ```typescript
122
+ // contracts/order-events.ts
123
+ export const OrderPlacedEventSchema = {
124
+ type: 'object',
125
+ required: ['orderId', 'customerId', 'total', 'items'],
126
+ properties: {
127
+ orderId: { type: 'string', format: 'uuid' },
128
+ customerId: { type: 'string', format: 'uuid' },
129
+ total: { type: 'number', minimum: 0 },
130
+ items: {
131
+ type: 'array',
132
+ items: {
133
+ type: 'object',
134
+ required: ['productId', 'quantity', 'price'],
135
+ properties: {
136
+ productId: { type: 'string' },
137
+ quantity: { type: 'number', minimum: 1 },
138
+ price: { type: 'number', minimum: 0 }
139
+ }
140
+ }
141
+ }
142
+ }
143
+ };
144
+ ```
145
+
146
+ ## Related Skills
147
+
148
+ - api-design
149
+ - backend-patterns
150
+ - cqrs
151
+ - event-sourcing
152
+ - message-queues
@@ -0,0 +1,159 @@
1
+ ---
2
+ name: frontend-pattern
3
+ description: Frontend development patterns — component composition, state management, URL-as-state, data fetching, and animation best practices for React/TypeScript web applications
4
+ origin: FlowDeck
5
+ ---
6
+
7
+ # Frontend Pattern Skill
8
+
9
+ Implements maintainable, performant frontend patterns using React and TypeScript.
10
+
11
+ ## When to Activate
12
+
13
+ Activate when:
14
+ - Building new UI components
15
+ - Setting up state management
16
+ - Implementing data fetching
17
+ - Adding animations or transitions
18
+ - Structuring a new feature module
19
+
20
+ ## Component Patterns
21
+
22
+ ### Compound Components
23
+
24
+ Use compound components when related UI shares state and interaction semantics:
25
+
26
+ ```tsx
27
+ <Tabs defaultValue="overview">
28
+ <Tabs.List>
29
+ <Tabs.Trigger value="overview">Overview</Tabs.Trigger>
30
+ <Tabs.Trigger value="settings">Settings</Tabs.Trigger>
31
+ </Tabs.List>
32
+ <Tabs.Content value="overview">...</Tabs.Content>
33
+ <Tabs.Content value="settings">...</Tabs.Content>
34
+ </Tabs>
35
+ ```
36
+
37
+ - Parent owns state via `useState` or context
38
+ - Children consume via context — no prop drilling
39
+ - Keeps keyboard handling, ARIA, and focus logic in the headless layer
40
+
41
+ ### Container / Presentational Split
42
+
43
+ ```tsx
44
+ // Container — owns data loading and side effects
45
+ function UserProfileContainer({ userId }: { userId: string }) {
46
+ const { data, isLoading } = useUser(userId);
47
+ if (isLoading) return <Skeleton />;
48
+ return <UserProfileView user={data} />;
49
+ }
50
+
51
+ // Presentational — receives props, renders UI
52
+ function UserProfileView({ user }: { user: User }) {
53
+ return (
54
+ <div>
55
+ <Avatar src={user.avatar} />
56
+ <h1>{user.name}</h1>
57
+ </div>
58
+ );
59
+ }
60
+ ```
61
+
62
+ ## State Management
63
+
64
+ | Concern | Tooling |
65
+ |---------|---------|
66
+ | Server state | TanStack Query, SWR, tRPC |
67
+ | Client state | Zustand, Jotai, signals |
68
+ | URL state | search params, route segments |
69
+ | Form state | React Hook Form or equivalent |
70
+
71
+ **Do not duplicate server state into client stores.** Derive values instead of storing redundant computed state.
72
+
73
+ ## URL As State
74
+
75
+ Persist shareable, bookmarkable state in the URL:
76
+
77
+ ```tsx
78
+ // Good: filters, sort, pagination in URL
79
+ const [searchParams, setSearchParams] = useSearchParams();
80
+ const filter = searchParams.get('filter') ?? 'all';
81
+
82
+ // Usage
83
+ <button onClick={() => setSearchParams({ filter: 'active' })}>
84
+ Active
85
+ </button>
86
+ ```
87
+
88
+ ## Data Fetching Patterns
89
+
90
+ ### Stale-While-Revalidate
91
+
92
+ Return cached data immediately, revalidate in background:
93
+
94
+ ```tsx
95
+ const { data } = useQuery({
96
+ queryKey: ['users', userId],
97
+ queryFn: () => fetchUser(userId),
98
+ staleTime: 5 * 60 * 1000, // 5 minutes
99
+ });
100
+ ```
101
+
102
+ ### Optimistic Updates
103
+
104
+ ```tsx
105
+ const mutation = useMutation({
106
+ mutationFn: updateUser,
107
+ onMutate: async (newData) => {
108
+ await queryClient.cancelQueries({ queryKey: ['user', newData.id] });
109
+ const previous = queryClient.getQueryData(['user', newData.id]);
110
+ queryClient.setQueryData(['user', newData.id], newData);
111
+ return { previous };
112
+ },
113
+ onError: (err, newData, context) => {
114
+ queryClient.setQueryData(['user', newData.id], context.previous);
115
+ },
116
+ });
117
+ ```
118
+
119
+ ## CSS Custom Properties
120
+
121
+ Define design tokens as CSS variables — do not hardcode values:
122
+
123
+ ```css
124
+ :root {
125
+ --color-surface: oklch(98% 0 0);
126
+ --color-text: oklch(18% 0 0);
127
+ --color-accent: oklch(68% 0.21 250);
128
+
129
+ --text-base: clamp(1rem, 0.92rem + 0.4vw, 1.125rem);
130
+ --space-section: clamp(4rem, 3rem + 5vw, 10rem);
131
+
132
+ --duration-fast: 150ms;
133
+ --duration-normal: 300ms;
134
+ --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
135
+ }
136
+ ```
137
+
138
+ ## Animation Guidelines
139
+
140
+ Use compositor-friendly properties only:
141
+
142
+ ```
143
+ ✅ transform, opacity, clip-path, filter
144
+ ❌ width, height, top, left, margin, padding, border, font-size
145
+ ```
146
+
147
+ ```tsx
148
+ // Good
149
+ const style = { opacity: isVisible ? 1 : 0, transform: `translateY(${isVisible ? 0 : 20}px)` };
150
+
151
+ // Bad
152
+ const style = { height: isVisible ? 'auto' : 0 };
153
+ ```
154
+
155
+ ## Related Skills
156
+
157
+ - [code-review](code-review) — Review frontend code for quality
158
+ - [security-scan](security-scan) — Check for XSS and injection vulnerabilities
159
+ - [test-coverage](test-coverage) — Ensure UI component tests exist
@@ -0,0 +1,80 @@
1
+ # hexagonal-architecture
2
+
3
+ ## When to Activate
4
+ When building applications that must remain flexible to changing external systems (databases, APIs, UI frameworks) and need to support multiple entry points (ports) for the same business logic.
5
+
6
+ ## Steps
7
+ 1. **Identify the core domain** - Isolate the pure business logic that makes no references to infrastructure.
8
+ 2. **Define inbound ports** - Create interfaces (ports) for primary/ driving actors (UI, API controllers) that trigger application logic.
9
+ 3. **Define outbound ports** - Create interfaces (ports) for secondary/ driven actors (databases, external services) that the domain calls.
10
+ 4. **Implement primary adapters** - Create adapters for inbound traffic (REST controllers, GraphQL resolvers, CLI commands).
11
+ 5. **Implement secondary adapters** - Create adapters for outbound traffic (Postgres repositories, Redis caches, email gateways).
12
+ 6. **Ensure domain has no external dependencies** - The domain layer should compile and run with no imports from adapters.
13
+ 7. **Wire via dependency injection** - Connect adapters to ports at application startup.
14
+
15
+ ## Examples
16
+ ```typescript
17
+ // Domain Core - Pure business logic, no infrastructure dependencies
18
+ class Transfer {
19
+ constructor(
20
+ public readonly fromAccountId: string,
21
+ public readonly toAccountId: string,
22
+ public readonly amount: number
23
+ ) {}
24
+
25
+ execute(accounts: Map<string, Account>): TransferResult {
26
+ const from = accounts.get(this.fromAccountId)
27
+ const to = accounts.get(this.toAccountId)
28
+
29
+ if (!from || !to) {
30
+ return TransferResult.failed('Account not found')
31
+ }
32
+
33
+ if (!from.canDebit(this.amount)) {
34
+ return TransferResult.failed('Insufficient funds')
35
+ }
36
+
37
+ from.debit(this.amount)
38
+ to.credit(this.amount)
39
+
40
+ return TransferResult.success()
41
+ }
42
+ }
43
+
44
+ // Inbound Port (Primary Port) - Interface for driving operations
45
+ interface TransferUseCase {
46
+ execute(transfer: Transfer): TransferResult
47
+ }
48
+
49
+ // Outbound Port (Secondary Port) - Interface for driven operations
50
+ interface AccountRepository {
51
+ findById(id: string): Promise<Account | null>
52
+ save(account: Account): Promise<void>
53
+ }
54
+
55
+ interface EventBus {
56
+ publish(event: DomainEvent): Promise<void>
57
+ }
58
+
59
+ // Primary Adapter - REST API
60
+ class TransferController implements TransferUseCase {
61
+ constructor(private readonly accounts: AccountRepository) {}
62
+
63
+ async execute(transfer: Transfer): Promise<TransferResult> {
64
+ const allAccounts = await this.accounts.findById(transfer.fromAccountId)
65
+ // ... handle via injected port
66
+ }
67
+ }
68
+
69
+ // Secondary Adapter - PostgreSQL implementation
70
+ class PostgresAccountRepository implements AccountRepository {
71
+ constructor(private readonly db: Database) {}
72
+ // ... implementation
73
+ }
74
+ ```
75
+
76
+ ## Related Skills
77
+ - clean-architecture
78
+ - layered-architecture
79
+ - ddd-architecture
80
+ - backend-patterns