@c0x12c/ai-toolkit 1.15.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 (255) hide show
  1. package/.claude-plugin/marketplace.json +16 -0
  2. package/.claude-plugin/plugin.json +12 -0
  3. package/README.md +439 -0
  4. package/VERSION +1 -0
  5. package/agents/design-critic.md +127 -0
  6. package/agents/idea-killer.md +72 -0
  7. package/agents/infrastructure-expert.md +49 -0
  8. package/agents/micronaut-backend-expert.md +45 -0
  9. package/agents/phase-reviewer.md +150 -0
  10. package/agents/research-planner.md +70 -0
  11. package/agents/solution-architect-cto.md +49 -0
  12. package/agents/sre-architect.md +49 -0
  13. package/agents/team-coordinator.md +111 -0
  14. package/bin/cli.js +780 -0
  15. package/claude-md/00-header.md +39 -0
  16. package/claude-md/01-core.md +105 -0
  17. package/claude-md/05-database.md +20 -0
  18. package/claude-md/11-backend-micronaut.md +19 -0
  19. package/claude-md/20-frontend-react.md +44 -0
  20. package/claude-md/25-ux-design.md +56 -0
  21. package/claude-md/30-infrastructure.md +24 -0
  22. package/claude-md/30-project-mgmt.md +119 -0
  23. package/claude-md/40-product.md +39 -0
  24. package/claude-md/50-ops.md +34 -0
  25. package/claude-md/60-research.md +27 -0
  26. package/claude-md/90-footer.md +21 -0
  27. package/commands/spartan/brainstorm.md +134 -0
  28. package/commands/spartan/brownfield.md +157 -0
  29. package/commands/spartan/build.md +435 -0
  30. package/commands/spartan/careful.md +94 -0
  31. package/commands/spartan/commit-message.md +112 -0
  32. package/commands/spartan/content.md +17 -0
  33. package/commands/spartan/context-save.md +161 -0
  34. package/commands/spartan/contribute.md +140 -0
  35. package/commands/spartan/daily.md +42 -0
  36. package/commands/spartan/debug.md +308 -0
  37. package/commands/spartan/deep-dive.md +55 -0
  38. package/commands/spartan/deploy.md +207 -0
  39. package/commands/spartan/e2e.md +264 -0
  40. package/commands/spartan/env-setup.md +166 -0
  41. package/commands/spartan/epic.md +199 -0
  42. package/commands/spartan/fe-review.md +181 -0
  43. package/commands/spartan/figma-to-code.md +260 -0
  44. package/commands/spartan/forensics.md +46 -0
  45. package/commands/spartan/freeze.md +84 -0
  46. package/commands/spartan/fundraise.md +53 -0
  47. package/commands/spartan/gate-review.md +229 -0
  48. package/commands/spartan/gsd-upgrade.md +376 -0
  49. package/commands/spartan/guard.md +42 -0
  50. package/commands/spartan/init-project.md +178 -0
  51. package/commands/spartan/init-rules.md +298 -0
  52. package/commands/spartan/interview.md +154 -0
  53. package/commands/spartan/kickoff.md +73 -0
  54. package/commands/spartan/kotlin-service.md +109 -0
  55. package/commands/spartan/lean-canvas.md +222 -0
  56. package/commands/spartan/lint-rules.md +122 -0
  57. package/commands/spartan/map-codebase.md +124 -0
  58. package/commands/spartan/migration.md +82 -0
  59. package/commands/spartan/next-app.md +317 -0
  60. package/commands/spartan/next-feature.md +212 -0
  61. package/commands/spartan/onboard.md +326 -0
  62. package/commands/spartan/outreach.md +16 -0
  63. package/commands/spartan/phase.md +142 -0
  64. package/commands/spartan/pitch.md +18 -0
  65. package/commands/spartan/plan.md +210 -0
  66. package/commands/spartan/pr-ready.md +202 -0
  67. package/commands/spartan/project.md +106 -0
  68. package/commands/spartan/qa.md +222 -0
  69. package/commands/spartan/research.md +254 -0
  70. package/commands/spartan/review.md +132 -0
  71. package/commands/spartan/scan-rules.md +173 -0
  72. package/commands/spartan/sessions.md +143 -0
  73. package/commands/spartan/spec.md +131 -0
  74. package/commands/spartan/startup.md +257 -0
  75. package/commands/spartan/team.md +570 -0
  76. package/commands/spartan/teardown.md +161 -0
  77. package/commands/spartan/testcontainer.md +97 -0
  78. package/commands/spartan/tf-cost.md +123 -0
  79. package/commands/spartan/tf-deploy.md +116 -0
  80. package/commands/spartan/tf-drift.md +100 -0
  81. package/commands/spartan/tf-import.md +107 -0
  82. package/commands/spartan/tf-module.md +121 -0
  83. package/commands/spartan/tf-plan.md +100 -0
  84. package/commands/spartan/tf-review.md +106 -0
  85. package/commands/spartan/tf-scaffold.md +109 -0
  86. package/commands/spartan/tf-security.md +147 -0
  87. package/commands/spartan/think.md +221 -0
  88. package/commands/spartan/unfreeze.md +13 -0
  89. package/commands/spartan/update.md +134 -0
  90. package/commands/spartan/ux.md +1233 -0
  91. package/commands/spartan/validate.md +193 -0
  92. package/commands/spartan/web-to-prd.md +706 -0
  93. package/commands/spartan/workstreams.md +109 -0
  94. package/commands/spartan/write.md +16 -0
  95. package/commands/spartan.md +386 -0
  96. package/frameworks/00-framework-comparison-guide.md +317 -0
  97. package/frameworks/01-lean-canvas.md +196 -0
  98. package/frameworks/02-design-sprint.md +304 -0
  99. package/frameworks/03-foundation-sprint.md +337 -0
  100. package/frameworks/04-business-model-canvas.md +391 -0
  101. package/frameworks/05-customer-development.md +426 -0
  102. package/frameworks/06-jobs-to-be-done.md +358 -0
  103. package/frameworks/07-mom-test.md +392 -0
  104. package/frameworks/08-value-proposition-canvas.md +488 -0
  105. package/frameworks/09-javelin-board.md +428 -0
  106. package/frameworks/10-build-measure-learn.md +467 -0
  107. package/frameworks/11-mvp-approaches.md +533 -0
  108. package/frameworks/think-before-build.md +593 -0
  109. package/lib/assembler.js +197 -0
  110. package/lib/assembler.test.js +159 -0
  111. package/lib/detector.js +166 -0
  112. package/lib/detector.test.js +221 -0
  113. package/lib/packs.js +16 -0
  114. package/lib/resolver.js +272 -0
  115. package/lib/resolver.test.js +298 -0
  116. package/lib/worktree.sh +104 -0
  117. package/package.json +50 -0
  118. package/packs/backend-micronaut.yaml +35 -0
  119. package/packs/backend-nodejs.yaml +15 -0
  120. package/packs/backend-python.yaml +15 -0
  121. package/packs/core.yaml +37 -0
  122. package/packs/database.yaml +21 -0
  123. package/packs/frontend-react.yaml +24 -0
  124. package/packs/infrastructure.yaml +40 -0
  125. package/packs/ops.yaml +16 -0
  126. package/packs/packs.compiled.json +371 -0
  127. package/packs/product.yaml +22 -0
  128. package/packs/project-mgmt.yaml +24 -0
  129. package/packs/research.yaml +39 -0
  130. package/packs/shared-backend.yaml +14 -0
  131. package/packs/ux-design.yaml +21 -0
  132. package/rules/backend-micronaut/API_DESIGN.md +313 -0
  133. package/rules/backend-micronaut/BATCH_PROCESSING.md +92 -0
  134. package/rules/backend-micronaut/CONTROLLERS.md +388 -0
  135. package/rules/backend-micronaut/KOTLIN.md +414 -0
  136. package/rules/backend-micronaut/RETROFIT_PLACEMENT.md +290 -0
  137. package/rules/backend-micronaut/SERVICES_AND_BEANS.md +325 -0
  138. package/rules/core/NAMING_CONVENTIONS.md +208 -0
  139. package/rules/core/SKILL_AUTHORING.md +174 -0
  140. package/rules/core/TIMEZONE.md +316 -0
  141. package/rules/database/ORM_AND_REPO.md +289 -0
  142. package/rules/database/SCHEMA.md +146 -0
  143. package/rules/database/TRANSACTIONS.md +311 -0
  144. package/rules/frontend-react/FRONTEND.md +344 -0
  145. package/rules/infrastructure/MODULES.md +260 -0
  146. package/rules/infrastructure/NAMING.md +196 -0
  147. package/rules/infrastructure/PROVIDERS.md +309 -0
  148. package/rules/infrastructure/SECURITY.md +310 -0
  149. package/rules/infrastructure/STATE_AND_BACKEND.md +237 -0
  150. package/rules/infrastructure/STRUCTURE.md +234 -0
  151. package/rules/infrastructure/VARIABLES.md +285 -0
  152. package/rules/shared-backend/ARCHITECTURE.md +46 -0
  153. package/rules/ux-design/DESIGN_PROCESS.md +176 -0
  154. package/skills/api-endpoint-creator/SKILL.md +455 -0
  155. package/skills/api-endpoint-creator/error-handling-guide.md +244 -0
  156. package/skills/api-endpoint-creator/examples.md +522 -0
  157. package/skills/api-endpoint-creator/testing-patterns.md +302 -0
  158. package/skills/article-writing/SKILL.md +109 -0
  159. package/skills/article-writing/examples.md +59 -0
  160. package/skills/backend-api-design/SKILL.md +84 -0
  161. package/skills/backend-api-design/code-patterns.md +138 -0
  162. package/skills/brainstorm/SKILL.md +95 -0
  163. package/skills/browser-qa/SKILL.md +87 -0
  164. package/skills/browser-qa/playwright-snippets.md +110 -0
  165. package/skills/ci-cd-patterns/SKILL.md +108 -0
  166. package/skills/ci-cd-patterns/workflows.md +149 -0
  167. package/skills/competitive-teardown/SKILL.md +93 -0
  168. package/skills/competitive-teardown/example-analysis.md +50 -0
  169. package/skills/content-engine/SKILL.md +131 -0
  170. package/skills/content-engine/examples.md +72 -0
  171. package/skills/database-patterns/SKILL.md +72 -0
  172. package/skills/database-patterns/code-templates.md +114 -0
  173. package/skills/database-table-creator/SKILL.md +141 -0
  174. package/skills/database-table-creator/examples.md +552 -0
  175. package/skills/database-table-creator/kotlin-templates.md +400 -0
  176. package/skills/database-table-creator/migration-template.sql +68 -0
  177. package/skills/database-table-creator/validation-checklist.md +337 -0
  178. package/skills/deep-research/SKILL.md +80 -0
  179. package/skills/design-intelligence/SKILL.md +268 -0
  180. package/skills/design-workflow/SKILL.md +127 -0
  181. package/skills/design-workflow/checklists.md +45 -0
  182. package/skills/idea-validation/SKILL.md +129 -0
  183. package/skills/idea-validation/example-report.md +50 -0
  184. package/skills/investor-materials/SKILL.md +122 -0
  185. package/skills/investor-materials/example-outline.md +70 -0
  186. package/skills/investor-outreach/SKILL.md +112 -0
  187. package/skills/investor-outreach/examples.md +76 -0
  188. package/skills/kotlin-best-practices/SKILL.md +58 -0
  189. package/skills/kotlin-best-practices/code-patterns.md +132 -0
  190. package/skills/market-research/SKILL.md +99 -0
  191. package/skills/security-checklist/SKILL.md +65 -0
  192. package/skills/security-checklist/audit-reference.md +95 -0
  193. package/skills/service-debugging/SKILL.md +116 -0
  194. package/skills/service-debugging/common-issues.md +65 -0
  195. package/skills/startup-pipeline/SKILL.md +152 -0
  196. package/skills/terraform-best-practices/SKILL.md +244 -0
  197. package/skills/terraform-module-creator/SKILL.md +284 -0
  198. package/skills/terraform-review/SKILL.md +222 -0
  199. package/skills/terraform-security-audit/SKILL.md +280 -0
  200. package/skills/terraform-service-scaffold/SKILL.md +574 -0
  201. package/skills/testing-strategies/SKILL.md +116 -0
  202. package/skills/testing-strategies/examples.md +103 -0
  203. package/skills/testing-strategies/integration-test-setup.md +71 -0
  204. package/skills/ui-ux-pro-max/SKILL.md +238 -0
  205. package/skills/ui-ux-pro-max/data/charts.csv +26 -0
  206. package/skills/ui-ux-pro-max/data/colors.csv +97 -0
  207. package/skills/ui-ux-pro-max/data/icons.csv +101 -0
  208. package/skills/ui-ux-pro-max/data/landing.csv +31 -0
  209. package/skills/ui-ux-pro-max/data/products.csv +97 -0
  210. package/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
  211. package/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
  212. package/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  213. package/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  214. package/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  215. package/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  216. package/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  217. package/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  218. package/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  219. package/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
  220. package/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  221. package/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  222. package/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  223. package/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  224. package/skills/ui-ux-pro-max/data/styles.csv +68 -0
  225. package/skills/ui-ux-pro-max/data/typography.csv +58 -0
  226. package/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  227. package/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  228. package/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
  229. package/skills/ui-ux-pro-max/python-setup.md +146 -0
  230. package/skills/ui-ux-pro-max/scripts/core.py +253 -0
  231. package/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
  232. package/skills/ui-ux-pro-max/scripts/search.py +114 -0
  233. package/skills/web-to-prd/SKILL.md +478 -0
  234. package/templates/build-config.yaml +44 -0
  235. package/templates/commands-config.yaml +55 -0
  236. package/templates/competitor-analysis.md +60 -0
  237. package/templates/content/AGENT_TEMPLATE.md +47 -0
  238. package/templates/content/COMMAND_TEMPLATE.md +27 -0
  239. package/templates/content/RULE_TEMPLATE.md +40 -0
  240. package/templates/content/SKILL_TEMPLATE.md +41 -0
  241. package/templates/design-config.md +105 -0
  242. package/templates/design-doc.md +207 -0
  243. package/templates/epic.md +100 -0
  244. package/templates/feature-spec.md +181 -0
  245. package/templates/idea-canvas.md +47 -0
  246. package/templates/implementation-plan.md +159 -0
  247. package/templates/prd-template.md +86 -0
  248. package/templates/preamble.md +89 -0
  249. package/templates/project-readme.md +35 -0
  250. package/templates/quality-gates.md +230 -0
  251. package/templates/spartan-config.yaml +164 -0
  252. package/templates/user-interview.md +69 -0
  253. package/templates/validation-checklist.md +108 -0
  254. package/templates/workflow-backend-micronaut.md +409 -0
  255. package/templates/workflow-frontend-react.md +233 -0
@@ -0,0 +1,344 @@
1
+ # Frontend Rules
2
+
3
+ > Full guide: use `/ui-ux-pro-max` skill
4
+
5
+ ## Build Check (CRITICAL)
6
+
7
+ Run `yarn build` (or `npm run build`) before committing any `.tsx`/`.ts` changes.
8
+
9
+ ### What This Catches
10
+
11
+ - Unused imports (`TS6133`)
12
+ - Unused variables and functions (`TS6133`)
13
+ - Type errors
14
+ - Missing exports
15
+ - Wrong function signatures
16
+
17
+ ### Common Mistakes: Leftover imports/variables after refactoring
18
+
19
+ When you remove JSX that uses a component, state, or handler, also remove:
20
+ 1. The import statement
21
+ 2. The state declaration (`useState`)
22
+ 3. The handler function
23
+ 4. Any type imports only used by removed code
24
+
25
+ **Example**: If you remove a modal from JSX, also remove:
26
+ - The `showModal` state
27
+ - The `handleOpenModal` / `handleCloseModal` handlers
28
+ - Any state only used inside that modal
29
+ - Component imports only used in that modal
30
+
31
+ ### When to Run
32
+
33
+ - After ANY edit to `.tsx` or `.ts` files
34
+ - Before staging files for commit
35
+ - If build fails, fix ALL errors before committing
36
+
37
+ ---
38
+
39
+ ## API Client Case Conversion (CRITICAL)
40
+
41
+ **Backend uses `snake_case`, frontend uses `camelCase`**. Always convert between them.
42
+
43
+ ### When creating/modifying API client files (`src/api/*.ts`)
44
+
45
+ **Always**:
46
+ ```typescript
47
+ import { keysToSnake, keysToCamel } from '@/utils/caseConvert'
48
+
49
+ export const apiClient = {
50
+ // CORRECT: Convert request to snake_case before sending
51
+ async createResource(request: CreateRequestType): Promise<ResponseType> {
52
+ const response = await httpClient.post(`${BASE}/resource/create`, keysToSnake(request))
53
+ return keysToCamel(response.data) // CORRECT: Convert response from snake_case
54
+ },
55
+
56
+ // WRONG: Not converting
57
+ async wrongCreateResource(request: CreateRequestType): Promise<ResponseType> {
58
+ const response = await httpClient.post(`${BASE}/resource/create`, request)
59
+ return response.data
60
+ },
61
+ }
62
+ ```
63
+
64
+ ### Why This Matters
65
+
66
+ Backend expects `{ "machine_box_id": "..." }` but frontend sends `{ "machineBoxId": "..." }`:
67
+ - **Result**: Micronaut JSON deserializer fails with "parameter is null" error
68
+ - **Root cause**: Field name mismatch (`machineBoxId` != `machine_box_id`)
69
+
70
+ ### Common Mistakes
71
+
72
+ 1. **Missing `keysToSnake` import** - Request fields don't match backend
73
+ 2. **Missing `keysToCamel` on responses** - Response fields don't match TypeScript types
74
+ 3. **Partial conversion** - Some methods converted, others not (inconsistent)
75
+
76
+ ### Reference Example
77
+
78
+ See your API client files for correct pattern.
79
+
80
+ ---
81
+
82
+ ## E2E Patterns
83
+
84
+ ### Selector Ambiguity Handling
85
+
86
+ **Use `.first()` when selectors might match multiple elements**.
87
+
88
+ ```typescript
89
+ // CORRECT: Use .first() when multiple matches possible
90
+ await expect(page.getByRole('table').or(page.getByText('No data found')).first()).toBeVisible();
91
+
92
+ // CORRECT: Explicit for single elements
93
+ await expect(page.getByRole('heading', { name: 'Settings' })).toBeVisible();
94
+ ```
95
+
96
+ Playwright's `or()` and `getByText()` can match multiple elements:
97
+ - **Without `.first()`**: "Ambiguous element locator" or timeout
98
+ - **With `.first()`**: Explicitly chooses first match
99
+
100
+ Common scenarios:
101
+ 1. **Empty states**: Table or "No data found" message
102
+ 2. **Dynamic content**: Multiple elements with similar text
103
+ 3. **Loading states**: Spinner vs loaded content
104
+
105
+ ### Form State Synchronization
106
+
107
+ **Always sync form state after successful save operations**.
108
+
109
+ ```typescript
110
+ const handleSave = async (values: FormValues) => {
111
+ const updated = await apiClient.updateResource(values)
112
+
113
+ // CORRECT: Sync form state with response
114
+ setForm({
115
+ ...updated,
116
+ // Ensure all fields are mapped correctly
117
+ })
118
+
119
+ toast.success('Saved successfully')
120
+ }
121
+
122
+ // WRONG: Not syncing state, form shows old values
123
+ const wrongHandleSave = async (values: FormValues) => {
124
+ await apiClient.updateResource(values)
125
+ toast.success('Saved successfully')
126
+ }
127
+ ```
128
+
129
+ After save, API returns updated data (with timestamps, computed fields, defaults):
130
+ - **Without sync**: Form shows stale values, user thinks save failed
131
+ - **With sync**: Form shows fresh data from API, user sees changes
132
+
133
+ Common issues:
134
+ 1. **Settings not persisting**: Form submit succeeds but UI shows old values
135
+ 2. **Data not refreshing**: Add/edit operations complete but list doesn't update
136
+ 3. **Navigation doesn't refetch**: Moving between tabs doesn't trigger data reload
137
+
138
+ ### E2E Test File Structure
139
+
140
+ Follow consistent structure for Playwright test files.
141
+
142
+ ```typescript
143
+ import { test, expect } from '@playwright/test';
144
+ import { screenshotDir } from '../fixtures/paths';
145
+
146
+ const SCREENSHOT_DIR = screenshotDir('feature-name');
147
+
148
+ // --- Page Load & Navigation ---
149
+
150
+ test('feature-name - page loads with heading and tabs', async ({ page }) => {
151
+ await page.goto('/feature-page');
152
+ await page.waitForLoadState('networkidle', { timeout: 10000 });
153
+
154
+ // Page heading should be visible
155
+ await expect(page.getByRole('heading', { name: 'Feature Name' })).toBeVisible();
156
+
157
+ // Tabs should be visible
158
+ await expect(page.getByRole('tab')).toBeVisible();
159
+
160
+ await page.screenshot({ path: `${SCREENSHOT_DIR}/page-loaded.png`, fullPage: true });
161
+ });
162
+
163
+ // --- CRUD Operations ---
164
+
165
+ test('feature-name - create item and verify', async ({ page }) => {
166
+ await page.goto('/feature-page');
167
+ await page.waitForLoadState('networkidle', { timeout: 10000 });
168
+
169
+ // Click "Create" button
170
+ await page.getByRole('button', { name: 'Create Item' }).click();
171
+
172
+ // Fill form
173
+ await page.getByRole('textbox', { name: 'Name' }).fill('Test Item');
174
+
175
+ // Submit
176
+ await page.getByRole('button', { name: 'Save' }).click();
177
+ await page.waitForLoadState('networkidle', { timeout: 5000 });
178
+
179
+ // Verify item appears in list
180
+ await expect(page.getByText('Test Item')).toBeVisible({ timeout: 5000 });
181
+
182
+ await page.screenshot({ path: `${SCREENSHOT_DIR}/create-item.png`, fullPage: true });
183
+ });
184
+ ```
185
+
186
+ ### Naming Conventions
187
+
188
+ - **Test names**: `feature-name - what it tests` (lowercase, descriptive)
189
+ - **Screenshot paths**: Use `screenshotDir()` helper for consistent paths
190
+ - **Timeouts**: 10s for page load, 5s for operations
191
+
192
+ ---
193
+
194
+ ## Checklist: Before Running E2E Tests
195
+
196
+ - [ ] Backend running and connected to database
197
+ - [ ] All POST requests in API clients use `keysToSnake()`
198
+ - [ ] All response returns use `keysToCamel()`
199
+ - [ ] Form submissions sync state after successful save
200
+ - [ ] Selectors use `.first()` where ambiguity possible
201
+
202
+ ---
203
+
204
+ ## Related Files
205
+
206
+ - API client files: `src/api/*.ts`
207
+ - Case conversion utility: `src/utils/caseConvert.ts`
208
+
209
+ ---
210
+
211
+ ## API Response Null Safety (merged from FRONTEND_CODING_STANDARDS.md)
212
+
213
+ ### ALWAYS Use Optional Chaining + Nullish Coalescing for API Responses
214
+
215
+ API responses may return `undefined` or have missing properties. NEVER access response properties without null safety.
216
+
217
+ ```typescript
218
+ // ✅ CORRECT — safe with fallback
219
+ const response = await teamApi.list(projectId)
220
+ setTeams(response?.teams ?? [])
221
+
222
+ // ❌ WRONG — will crash if response is undefined
223
+ const response = await teamApi.list(projectId)
224
+ setTeams(response.teams)
225
+ ```
226
+
227
+ ### Catch Blocks MUST Reset State to Safe Defaults
228
+
229
+ ```typescript
230
+ // ✅ CORRECT
231
+ const fetchTeams = useCallback(async () => {
232
+ try {
233
+ const response = await teamApi.list(projectId)
234
+ setTeams(response?.teams ?? [])
235
+ } catch {
236
+ setTeams([]) // Reset to safe default
237
+ }
238
+ }, [projectId])
239
+
240
+ // ❌ WRONG — state left stale on error
241
+ const fetchTeams = useCallback(async () => {
242
+ try {
243
+ const response = await teamApi.list(projectId)
244
+ setTeams(response.teams)
245
+ } catch (e) {
246
+ console.error(e) // State not reset!
247
+ }
248
+ }, [projectId])
249
+ ```
250
+
251
+ ### Loading States
252
+ - Always show loading indicators during async operations
253
+ - Disable form submit buttons while requests are in-flight
254
+ - Use skeleton UIs for initial data loads
255
+
256
+ ---
257
+
258
+ ## Optimistic Updates Pattern
259
+
260
+ ### Avoid UI Flash/Reload After Mutations
261
+
262
+ **Any mutation (create, update, delete) should use optimistic updates instead of full query invalidation.**
263
+
264
+ #### Why This Is Critical
265
+ - Prevents jarring UI flash/white screen during refetch
266
+ - Provides instant feedback to users
267
+ - Better perceived performance
268
+
269
+ #### Bad Pattern (UI Flash)
270
+ ```typescript
271
+ // BAD - Triggers full refetch, causing flash
272
+ const mutation = useMutation({
273
+ mutationFn: (data) => api.create(data),
274
+ onSuccess: () => {
275
+ queryClient.invalidateQueries({ queryKey: ['feed'] }) // Flash!
276
+ },
277
+ })
278
+ ```
279
+
280
+ #### Good Pattern (Optimistic Update)
281
+ ```typescript
282
+ // GOOD - Updates cache directly, no flash
283
+ const mutation = useMutation({
284
+ mutationFn: (data) => api.create(data),
285
+ onSuccess: (newItem) => {
286
+ // Update cache directly by prepending new item
287
+ queryClient.setQueryData(['feed'], (oldData: any) => {
288
+ if (!oldData?.pages) return oldData
289
+ return {
290
+ ...oldData,
291
+ pages: oldData.pages.map((page: any, index: number) => {
292
+ if (index === 0) {
293
+ return { ...page, items: [newItem, ...(page.items || [])] }
294
+ }
295
+ return page
296
+ }),
297
+ }
298
+ })
299
+
300
+ // Invalidate related queries silently (no immediate refetch)
301
+ queryClient.invalidateQueries({ queryKey: ['related'], refetchType: 'none' })
302
+ },
303
+ })
304
+ ```
305
+
306
+ ### Use placeholderData for Query Stability
307
+
308
+ ```typescript
309
+ // GOOD - Keep previous data while refetching
310
+ const { data } = useInfiniteQuery({
311
+ queryKey: ['feed'],
312
+ queryFn: fetchFeed,
313
+ placeholderData: (previousData) => previousData, // No flash on refetch
314
+ })
315
+ ```
316
+
317
+ ### Optimistic Delete with Rollback
318
+
319
+ ```typescript
320
+ const deleteMutation = useMutation({
321
+ mutationFn: (id: string) => api.delete(id),
322
+ onMutate: async (id) => {
323
+ // Cancel outgoing refetches
324
+ await queryClient.cancelQueries({ queryKey: ['items'] })
325
+
326
+ // Save current state for rollback
327
+ const previousData = queryClient.getQueryData(['items'])
328
+
329
+ // Optimistically remove item
330
+ queryClient.setQueryData(['items'], (old: any) => ({
331
+ ...old,
332
+ items: old.items.filter((item: any) => item.id !== id),
333
+ }))
334
+
335
+ return { previousData }
336
+ },
337
+ onError: (_err, _id, context) => {
338
+ // Rollback on error
339
+ if (context?.previousData) {
340
+ queryClient.setQueryData(['items'], context.previousData)
341
+ }
342
+ },
343
+ })
344
+ ```
@@ -0,0 +1,260 @@
1
+ ---
2
+ paths:
3
+ - "**/*.tf"
4
+ - "**/*.hcl"
5
+ - "**/*.tfvars"
6
+ ---
7
+ # Module Design and Composition
8
+
9
+ > Full guide: use `/spartan:tf-module` command
10
+
11
+ ## Registry Modules
12
+
13
+ Use c0x12c registry modules with pessimistic version pinning. Always pin to patch level.
14
+
15
+ ```hcl
16
+ module "rds" {
17
+ source = "c0x12c/rds/aws"
18
+ version = "~> 0.6.6"
19
+
20
+ identifier = var.rds_identifier
21
+ instance_class = var.rds_instance_class
22
+ subnet_ids = var.private_subnet_ids
23
+ }
24
+ ```
25
+
26
+ ### WRONG -- Unbounded version constraint
27
+
28
+ ```hcl
29
+ module "rds" {
30
+ source = "c0x12c/rds/aws"
31
+ version = ">= 0.6.0" # Could pull breaking changes
32
+ }
33
+ ```
34
+
35
+ ### WRONG -- No version constraint
36
+
37
+ ```hcl
38
+ module "rds" {
39
+ source = "c0x12c/rds/aws"
40
+ # No version -- pulls latest, unpredictable
41
+ }
42
+ ```
43
+
44
+ ### CORRECT -- Patch-level pessimistic pinning
45
+
46
+ ```hcl
47
+ module "rds" {
48
+ source = "c0x12c/rds/aws"
49
+ version = "~> 0.6.6" # Allows 0.6.x, blocks 0.7.0+
50
+ }
51
+ ```
52
+
53
+ ---
54
+
55
+ ## Local Modules
56
+
57
+ Service-specific modules live in `modules/{service}/` with resource-per-file pattern.
58
+
59
+ ```
60
+ modules/
61
+ └── {service}/
62
+ ├── variables.tf # All inputs declared
63
+ ├── outputs.tf # All outputs declared
64
+ ├── locals.tf # Computed values
65
+ ├── ecr.tf # ECR repositories
66
+ ├── rds.tf # RDS PostgreSQL
67
+ ├── redis.tf # ElastiCache Redis
68
+ ├── s3.tf # S3 buckets
69
+ ├── eks.tf # K8s namespace, IRSA, secrets, configmaps
70
+ └── sqs.tf # SQS queues
71
+ ```
72
+
73
+ ### WRONG -- Multiple resource types in one file
74
+
75
+ ```hcl
76
+ # resources.tf -- mixing ECR, RDS, and Redis
77
+ module "ecr" {
78
+ source = "c0x12c/ecr/aws"
79
+ version = "~> 0.1.0"
80
+ name = var.service_name
81
+ }
82
+
83
+ module "rds" {
84
+ source = "c0x12c/rds/aws"
85
+ version = "~> 0.6.6"
86
+ # ...
87
+ }
88
+
89
+ module "redis" {
90
+ source = "c0x12c/redis/aws"
91
+ version = "~> 0.2.0"
92
+ # ...
93
+ }
94
+ ```
95
+
96
+ ### CORRECT -- One resource type per file
97
+
98
+ ```hcl
99
+ # ecr.tf
100
+ module "ecr" {
101
+ source = "c0x12c/ecr/aws"
102
+ version = "~> 0.1.0"
103
+ name = var.service_name
104
+ }
105
+
106
+ # rds.tf
107
+ module "rds" {
108
+ source = "c0x12c/rds/aws"
109
+ version = "~> 0.6.6"
110
+ # ...
111
+ }
112
+
113
+ # redis.tf
114
+ module "redis" {
115
+ source = "c0x12c/redis/aws"
116
+ version = "~> 0.2.0"
117
+ # ...
118
+ }
119
+ ```
120
+
121
+ ---
122
+
123
+ ## Module Interface Rules
124
+
125
+ Every module must have explicit `variables.tf` and `outputs.tf`. No hardcoded values.
126
+
127
+ ### WRONG -- Hardcoded values in module
128
+
129
+ ```hcl
130
+ # modules/{service}/rds.tf
131
+ module "rds" {
132
+ source = "c0x12c/rds/aws"
133
+ version = "~> 0.6.6"
134
+ instance_class = "db.t3.micro" # Hardcoded
135
+ subnet_ids = ["subnet-abc123"] # Hardcoded
136
+ }
137
+ ```
138
+
139
+ ### CORRECT -- Parameterized module
140
+
141
+ ```hcl
142
+ # modules/{service}/variables.tf
143
+ variable "rds_instance_class" {
144
+ description = "RDS instance class"
145
+ type = string
146
+ }
147
+
148
+ variable "private_subnet_ids" {
149
+ description = "Private subnet IDs for data stores"
150
+ type = list(string)
151
+ }
152
+
153
+ # modules/{service}/rds.tf
154
+ module "rds" {
155
+ source = "c0x12c/rds/aws"
156
+ version = "~> 0.6.6"
157
+ instance_class = var.rds_instance_class
158
+ subnet_ids = var.private_subnet_ids
159
+ }
160
+
161
+ # modules/{service}/outputs.tf
162
+ output "rds_endpoint" {
163
+ description = "RDS instance endpoint"
164
+ value = module.rds.endpoint
165
+ }
166
+
167
+ output "rds_password" {
168
+ description = "RDS auto-generated password"
169
+ value = module.rds.password
170
+ sensitive = true
171
+ }
172
+ ```
173
+
174
+ ---
175
+
176
+ ## Sensitive for_each Gotcha
177
+
178
+ Outputs marked `sensitive = true` in source modules break `for_each` in consumer modules. Terraform cannot use sensitive values as map keys.
179
+
180
+ ### WRONG -- Trying to iterate over sensitive output
181
+
182
+ ```hcl
183
+ # This fails at plan time
184
+ resource "kubernetes_secret" "db_creds" {
185
+ for_each = module.rds.connection_map # Error: sensitive value in for_each
186
+ }
187
+ ```
188
+
189
+ ### CORRECT -- Fix at source module or use nonsensitive()
190
+
191
+ ```hcl
192
+ # Option 1: Fix the source module output to not be sensitive
193
+ output "connection_map" {
194
+ value = { for k, v in local.connections : k => v }
195
+ # Remove sensitive = true if the map keys are not sensitive
196
+ }
197
+
198
+ # Option 2: Use nonsensitive() on non-secret parts only
199
+ resource "kubernetes_secret" "db_creds" {
200
+ for_each = nonsensitive(module.rds.connection_names)
201
+
202
+ metadata {
203
+ name = each.value
204
+ }
205
+ }
206
+ ```
207
+
208
+ ---
209
+
210
+ ## Shared Modules Pattern
211
+
212
+ In multi-root setups, use `live/shared/` for cross-environment reusable compositions.
213
+
214
+ ```
215
+ live/
216
+ ├── shared/
217
+ │ ├── ecr/
218
+ │ │ ├── variables.tf
219
+ │ │ ├── outputs.tf
220
+ │ │ └── main.tf
221
+ │ └── rds/
222
+ │ ├── variables.tf
223
+ │ ├── outputs.tf
224
+ │ └── main.tf
225
+ ├── dev/
226
+ │ └── main.tf # Calls shared modules with dev params
227
+ └── prod/
228
+ └── main.tf # Calls shared modules with prod params
229
+ ```
230
+
231
+ ```hcl
232
+ # live/dev/main.tf
233
+ module "rds" {
234
+ source = "../shared/rds"
235
+ environment = "dev"
236
+ instance_class = "db.t3.micro"
237
+ }
238
+
239
+ # live/prod/main.tf
240
+ module "rds" {
241
+ source = "../shared/rds"
242
+ environment = "prod"
243
+ instance_class = "db.r6g.large"
244
+ }
245
+ ```
246
+
247
+ ---
248
+
249
+ ## Quick Reference
250
+
251
+ | Aspect | Rule |
252
+ |--------|------|
253
+ | Registry modules | c0x12c registry with `~> X.Y.Z` pinning |
254
+ | Version constraint | Pessimistic (`~>`) at patch level, never unbounded (`>=`) |
255
+ | Local modules | `modules/{service}/` with resource-per-file |
256
+ | File pattern | One resource type per `.tf` file |
257
+ | Module interface | Explicit `variables.tf` + `outputs.tf`, no hardcoded values |
258
+ | Sensitive for_each | Fix at source module, not consumer |
259
+ | Shared modules | `live/shared/` for multi-root cross-environment reuse |
260
+ | Variable declarations | Declare all variables, even if sourced from remote state |