@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.
- package/.claude-plugin/marketplace.json +16 -0
- package/.claude-plugin/plugin.json +12 -0
- package/README.md +439 -0
- package/VERSION +1 -0
- package/agents/design-critic.md +127 -0
- package/agents/idea-killer.md +72 -0
- package/agents/infrastructure-expert.md +49 -0
- package/agents/micronaut-backend-expert.md +45 -0
- package/agents/phase-reviewer.md +150 -0
- package/agents/research-planner.md +70 -0
- package/agents/solution-architect-cto.md +49 -0
- package/agents/sre-architect.md +49 -0
- package/agents/team-coordinator.md +111 -0
- package/bin/cli.js +780 -0
- package/claude-md/00-header.md +39 -0
- package/claude-md/01-core.md +105 -0
- package/claude-md/05-database.md +20 -0
- package/claude-md/11-backend-micronaut.md +19 -0
- package/claude-md/20-frontend-react.md +44 -0
- package/claude-md/25-ux-design.md +56 -0
- package/claude-md/30-infrastructure.md +24 -0
- package/claude-md/30-project-mgmt.md +119 -0
- package/claude-md/40-product.md +39 -0
- package/claude-md/50-ops.md +34 -0
- package/claude-md/60-research.md +27 -0
- package/claude-md/90-footer.md +21 -0
- package/commands/spartan/brainstorm.md +134 -0
- package/commands/spartan/brownfield.md +157 -0
- package/commands/spartan/build.md +435 -0
- package/commands/spartan/careful.md +94 -0
- package/commands/spartan/commit-message.md +112 -0
- package/commands/spartan/content.md +17 -0
- package/commands/spartan/context-save.md +161 -0
- package/commands/spartan/contribute.md +140 -0
- package/commands/spartan/daily.md +42 -0
- package/commands/spartan/debug.md +308 -0
- package/commands/spartan/deep-dive.md +55 -0
- package/commands/spartan/deploy.md +207 -0
- package/commands/spartan/e2e.md +264 -0
- package/commands/spartan/env-setup.md +166 -0
- package/commands/spartan/epic.md +199 -0
- package/commands/spartan/fe-review.md +181 -0
- package/commands/spartan/figma-to-code.md +260 -0
- package/commands/spartan/forensics.md +46 -0
- package/commands/spartan/freeze.md +84 -0
- package/commands/spartan/fundraise.md +53 -0
- package/commands/spartan/gate-review.md +229 -0
- package/commands/spartan/gsd-upgrade.md +376 -0
- package/commands/spartan/guard.md +42 -0
- package/commands/spartan/init-project.md +178 -0
- package/commands/spartan/init-rules.md +298 -0
- package/commands/spartan/interview.md +154 -0
- package/commands/spartan/kickoff.md +73 -0
- package/commands/spartan/kotlin-service.md +109 -0
- package/commands/spartan/lean-canvas.md +222 -0
- package/commands/spartan/lint-rules.md +122 -0
- package/commands/spartan/map-codebase.md +124 -0
- package/commands/spartan/migration.md +82 -0
- package/commands/spartan/next-app.md +317 -0
- package/commands/spartan/next-feature.md +212 -0
- package/commands/spartan/onboard.md +326 -0
- package/commands/spartan/outreach.md +16 -0
- package/commands/spartan/phase.md +142 -0
- package/commands/spartan/pitch.md +18 -0
- package/commands/spartan/plan.md +210 -0
- package/commands/spartan/pr-ready.md +202 -0
- package/commands/spartan/project.md +106 -0
- package/commands/spartan/qa.md +222 -0
- package/commands/spartan/research.md +254 -0
- package/commands/spartan/review.md +132 -0
- package/commands/spartan/scan-rules.md +173 -0
- package/commands/spartan/sessions.md +143 -0
- package/commands/spartan/spec.md +131 -0
- package/commands/spartan/startup.md +257 -0
- package/commands/spartan/team.md +570 -0
- package/commands/spartan/teardown.md +161 -0
- package/commands/spartan/testcontainer.md +97 -0
- package/commands/spartan/tf-cost.md +123 -0
- package/commands/spartan/tf-deploy.md +116 -0
- package/commands/spartan/tf-drift.md +100 -0
- package/commands/spartan/tf-import.md +107 -0
- package/commands/spartan/tf-module.md +121 -0
- package/commands/spartan/tf-plan.md +100 -0
- package/commands/spartan/tf-review.md +106 -0
- package/commands/spartan/tf-scaffold.md +109 -0
- package/commands/spartan/tf-security.md +147 -0
- package/commands/spartan/think.md +221 -0
- package/commands/spartan/unfreeze.md +13 -0
- package/commands/spartan/update.md +134 -0
- package/commands/spartan/ux.md +1233 -0
- package/commands/spartan/validate.md +193 -0
- package/commands/spartan/web-to-prd.md +706 -0
- package/commands/spartan/workstreams.md +109 -0
- package/commands/spartan/write.md +16 -0
- package/commands/spartan.md +386 -0
- package/frameworks/00-framework-comparison-guide.md +317 -0
- package/frameworks/01-lean-canvas.md +196 -0
- package/frameworks/02-design-sprint.md +304 -0
- package/frameworks/03-foundation-sprint.md +337 -0
- package/frameworks/04-business-model-canvas.md +391 -0
- package/frameworks/05-customer-development.md +426 -0
- package/frameworks/06-jobs-to-be-done.md +358 -0
- package/frameworks/07-mom-test.md +392 -0
- package/frameworks/08-value-proposition-canvas.md +488 -0
- package/frameworks/09-javelin-board.md +428 -0
- package/frameworks/10-build-measure-learn.md +467 -0
- package/frameworks/11-mvp-approaches.md +533 -0
- package/frameworks/think-before-build.md +593 -0
- package/lib/assembler.js +197 -0
- package/lib/assembler.test.js +159 -0
- package/lib/detector.js +166 -0
- package/lib/detector.test.js +221 -0
- package/lib/packs.js +16 -0
- package/lib/resolver.js +272 -0
- package/lib/resolver.test.js +298 -0
- package/lib/worktree.sh +104 -0
- package/package.json +50 -0
- package/packs/backend-micronaut.yaml +35 -0
- package/packs/backend-nodejs.yaml +15 -0
- package/packs/backend-python.yaml +15 -0
- package/packs/core.yaml +37 -0
- package/packs/database.yaml +21 -0
- package/packs/frontend-react.yaml +24 -0
- package/packs/infrastructure.yaml +40 -0
- package/packs/ops.yaml +16 -0
- package/packs/packs.compiled.json +371 -0
- package/packs/product.yaml +22 -0
- package/packs/project-mgmt.yaml +24 -0
- package/packs/research.yaml +39 -0
- package/packs/shared-backend.yaml +14 -0
- package/packs/ux-design.yaml +21 -0
- package/rules/backend-micronaut/API_DESIGN.md +313 -0
- package/rules/backend-micronaut/BATCH_PROCESSING.md +92 -0
- package/rules/backend-micronaut/CONTROLLERS.md +388 -0
- package/rules/backend-micronaut/KOTLIN.md +414 -0
- package/rules/backend-micronaut/RETROFIT_PLACEMENT.md +290 -0
- package/rules/backend-micronaut/SERVICES_AND_BEANS.md +325 -0
- package/rules/core/NAMING_CONVENTIONS.md +208 -0
- package/rules/core/SKILL_AUTHORING.md +174 -0
- package/rules/core/TIMEZONE.md +316 -0
- package/rules/database/ORM_AND_REPO.md +289 -0
- package/rules/database/SCHEMA.md +146 -0
- package/rules/database/TRANSACTIONS.md +311 -0
- package/rules/frontend-react/FRONTEND.md +344 -0
- package/rules/infrastructure/MODULES.md +260 -0
- package/rules/infrastructure/NAMING.md +196 -0
- package/rules/infrastructure/PROVIDERS.md +309 -0
- package/rules/infrastructure/SECURITY.md +310 -0
- package/rules/infrastructure/STATE_AND_BACKEND.md +237 -0
- package/rules/infrastructure/STRUCTURE.md +234 -0
- package/rules/infrastructure/VARIABLES.md +285 -0
- package/rules/shared-backend/ARCHITECTURE.md +46 -0
- package/rules/ux-design/DESIGN_PROCESS.md +176 -0
- package/skills/api-endpoint-creator/SKILL.md +455 -0
- package/skills/api-endpoint-creator/error-handling-guide.md +244 -0
- package/skills/api-endpoint-creator/examples.md +522 -0
- package/skills/api-endpoint-creator/testing-patterns.md +302 -0
- package/skills/article-writing/SKILL.md +109 -0
- package/skills/article-writing/examples.md +59 -0
- package/skills/backend-api-design/SKILL.md +84 -0
- package/skills/backend-api-design/code-patterns.md +138 -0
- package/skills/brainstorm/SKILL.md +95 -0
- package/skills/browser-qa/SKILL.md +87 -0
- package/skills/browser-qa/playwright-snippets.md +110 -0
- package/skills/ci-cd-patterns/SKILL.md +108 -0
- package/skills/ci-cd-patterns/workflows.md +149 -0
- package/skills/competitive-teardown/SKILL.md +93 -0
- package/skills/competitive-teardown/example-analysis.md +50 -0
- package/skills/content-engine/SKILL.md +131 -0
- package/skills/content-engine/examples.md +72 -0
- package/skills/database-patterns/SKILL.md +72 -0
- package/skills/database-patterns/code-templates.md +114 -0
- package/skills/database-table-creator/SKILL.md +141 -0
- package/skills/database-table-creator/examples.md +552 -0
- package/skills/database-table-creator/kotlin-templates.md +400 -0
- package/skills/database-table-creator/migration-template.sql +68 -0
- package/skills/database-table-creator/validation-checklist.md +337 -0
- package/skills/deep-research/SKILL.md +80 -0
- package/skills/design-intelligence/SKILL.md +268 -0
- package/skills/design-workflow/SKILL.md +127 -0
- package/skills/design-workflow/checklists.md +45 -0
- package/skills/idea-validation/SKILL.md +129 -0
- package/skills/idea-validation/example-report.md +50 -0
- package/skills/investor-materials/SKILL.md +122 -0
- package/skills/investor-materials/example-outline.md +70 -0
- package/skills/investor-outreach/SKILL.md +112 -0
- package/skills/investor-outreach/examples.md +76 -0
- package/skills/kotlin-best-practices/SKILL.md +58 -0
- package/skills/kotlin-best-practices/code-patterns.md +132 -0
- package/skills/market-research/SKILL.md +99 -0
- package/skills/security-checklist/SKILL.md +65 -0
- package/skills/security-checklist/audit-reference.md +95 -0
- package/skills/service-debugging/SKILL.md +116 -0
- package/skills/service-debugging/common-issues.md +65 -0
- package/skills/startup-pipeline/SKILL.md +152 -0
- package/skills/terraform-best-practices/SKILL.md +244 -0
- package/skills/terraform-module-creator/SKILL.md +284 -0
- package/skills/terraform-review/SKILL.md +222 -0
- package/skills/terraform-security-audit/SKILL.md +280 -0
- package/skills/terraform-service-scaffold/SKILL.md +574 -0
- package/skills/testing-strategies/SKILL.md +116 -0
- package/skills/testing-strategies/examples.md +103 -0
- package/skills/testing-strategies/integration-test-setup.md +71 -0
- package/skills/ui-ux-pro-max/SKILL.md +238 -0
- package/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/skills/ui-ux-pro-max/data/styles.csv +68 -0
- package/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/skills/ui-ux-pro-max/python-setup.md +146 -0
- package/skills/ui-ux-pro-max/scripts/core.py +253 -0
- package/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
- package/skills/ui-ux-pro-max/scripts/search.py +114 -0
- package/skills/web-to-prd/SKILL.md +478 -0
- package/templates/build-config.yaml +44 -0
- package/templates/commands-config.yaml +55 -0
- package/templates/competitor-analysis.md +60 -0
- package/templates/content/AGENT_TEMPLATE.md +47 -0
- package/templates/content/COMMAND_TEMPLATE.md +27 -0
- package/templates/content/RULE_TEMPLATE.md +40 -0
- package/templates/content/SKILL_TEMPLATE.md +41 -0
- package/templates/design-config.md +105 -0
- package/templates/design-doc.md +207 -0
- package/templates/epic.md +100 -0
- package/templates/feature-spec.md +181 -0
- package/templates/idea-canvas.md +47 -0
- package/templates/implementation-plan.md +159 -0
- package/templates/prd-template.md +86 -0
- package/templates/preamble.md +89 -0
- package/templates/project-readme.md +35 -0
- package/templates/quality-gates.md +230 -0
- package/templates/spartan-config.yaml +164 -0
- package/templates/user-interview.md +69 -0
- package/templates/validation-checklist.md +108 -0
- package/templates/workflow-backend-micronaut.md +409 -0
- package/templates/workflow-frontend-react.md +233 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
# API Design Rules
|
|
2
|
+
|
|
3
|
+
> Full guide: use `/backend-api-design` or `/testing-strategies` skill
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## RPC Style — No REST Verbs
|
|
8
|
+
|
|
9
|
+
All mutations use `@Post`. Never use `@Put`, `@Delete`, or `@Patch`.
|
|
10
|
+
|
|
11
|
+
| Action | HTTP Method | Example |
|
|
12
|
+
|--------|-------------|---------|
|
|
13
|
+
| Read one | `@Get` | `@Get("/item")` |
|
|
14
|
+
| Read list | `@Get` | `@Get("/items")` |
|
|
15
|
+
| Create | `@Post` | `@Post` |
|
|
16
|
+
| Update | `@Post("/update")` | `@Post("/update")` |
|
|
17
|
+
| Delete | `@Post("/delete")` | `@Post("/delete")` |
|
|
18
|
+
| Custom action | `@Post("/close")` | `@Post("/close")` |
|
|
19
|
+
|
|
20
|
+
### Bad — REST Verbs (NEVER use these)
|
|
21
|
+
|
|
22
|
+
```kotlin
|
|
23
|
+
// ❌ BAD — REST verbs
|
|
24
|
+
@Patch("/{id}")
|
|
25
|
+
suspend fun update(@PathVariable id: UUID, @Body request: UpdateRequest): Response
|
|
26
|
+
|
|
27
|
+
@Delete("/{id}")
|
|
28
|
+
suspend fun delete(@PathVariable id: UUID): Boolean
|
|
29
|
+
|
|
30
|
+
@Put("/{id}")
|
|
31
|
+
suspend fun replace(@PathVariable id: UUID, @Body request: ReplaceRequest): Response
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Good — RPC-Style
|
|
35
|
+
|
|
36
|
+
```kotlin
|
|
37
|
+
// ✅ GOOD — RPC-style
|
|
38
|
+
@Post("/update")
|
|
39
|
+
suspend fun update(@QueryValue id: UUID, @Body request: UpdateRequest): Response
|
|
40
|
+
|
|
41
|
+
@Post("/delete")
|
|
42
|
+
suspend fun delete(@QueryValue id: UUID): Boolean
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## URL Design
|
|
48
|
+
|
|
49
|
+
### NEVER Use Path Parameters
|
|
50
|
+
|
|
51
|
+
**Do NOT use path parameters (e.g., `/{id}`, `/{userId}`). Always use query parameters.**
|
|
52
|
+
|
|
53
|
+
Why:
|
|
54
|
+
- Query parameters are more explicit and self-documenting
|
|
55
|
+
- Easier to add optional parameters without breaking API
|
|
56
|
+
- Consistent pattern across all endpoints
|
|
57
|
+
- Simpler routing configuration
|
|
58
|
+
- Better for caching and logging
|
|
59
|
+
|
|
60
|
+
#### Bad - Path Parameters
|
|
61
|
+
```kotlin
|
|
62
|
+
// DON'T DO THIS
|
|
63
|
+
@Get("/employees/{id}")
|
|
64
|
+
suspend fun getEmployee(@PathVariable id: UUID): EmployeeResponse
|
|
65
|
+
|
|
66
|
+
@Get("/employees/{id}/metrics")
|
|
67
|
+
suspend fun getMetrics(@PathVariable id: UUID): MetricsResponse
|
|
68
|
+
|
|
69
|
+
@Delete("/employees/{id}")
|
|
70
|
+
suspend fun deleteEmployee(@PathVariable id: UUID): Boolean
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
#### Good - Query Parameters
|
|
74
|
+
```kotlin
|
|
75
|
+
// DO THIS
|
|
76
|
+
@Get("/employee")
|
|
77
|
+
suspend fun getEmployee(@QueryValue id: UUID): EmployeeResponse
|
|
78
|
+
|
|
79
|
+
@Get("/employee/metrics")
|
|
80
|
+
suspend fun getMetrics(@QueryValue id: UUID): MetricsResponse
|
|
81
|
+
|
|
82
|
+
@Post("/employee/delete")
|
|
83
|
+
suspend fun deleteEmployee(@QueryValue id: UUID): Boolean
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Endpoint Naming
|
|
87
|
+
|
|
88
|
+
Use singular nouns for single resource, plural for collections:
|
|
89
|
+
|
|
90
|
+
```kotlin
|
|
91
|
+
// Collection endpoints (plural)
|
|
92
|
+
@Get("/employees") // List employees
|
|
93
|
+
@Get("/organizations") // List organizations
|
|
94
|
+
|
|
95
|
+
// Single resource endpoints (singular)
|
|
96
|
+
@Get("/employee") // Get one employee (with ?id=xxx)
|
|
97
|
+
@Get("/organization") // Get one organization (with ?id=xxx)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Use verb sub-paths for actions:
|
|
101
|
+
|
|
102
|
+
```kotlin
|
|
103
|
+
// Actions use sub-paths, not HTTP verbs alone
|
|
104
|
+
@Post("/employee/delete") // Delete employee
|
|
105
|
+
@Post("/employee/restore") // Restore employee
|
|
106
|
+
@Post("/sync/employees") // Trigger employee sync
|
|
107
|
+
@Post("/sync/github") // Trigger GitHub sync
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Controller Organization
|
|
111
|
+
|
|
112
|
+
Group related endpoints under common prefixes:
|
|
113
|
+
|
|
114
|
+
```kotlin
|
|
115
|
+
@Controller("/api/v1/admin")
|
|
116
|
+
class AdminController {
|
|
117
|
+
// Sync operations
|
|
118
|
+
@Post("/sync/employees")
|
|
119
|
+
@Post("/sync/github")
|
|
120
|
+
|
|
121
|
+
// Employee operations
|
|
122
|
+
@Get("/employees")
|
|
123
|
+
@Get("/employee")
|
|
124
|
+
@Get("/employee/metrics")
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Request/Response Patterns
|
|
129
|
+
|
|
130
|
+
Required vs optional query parameters:
|
|
131
|
+
|
|
132
|
+
```kotlin
|
|
133
|
+
// Required parameter - no default, will fail if missing
|
|
134
|
+
@Get("/employee")
|
|
135
|
+
suspend fun getEmployee(
|
|
136
|
+
@QueryValue id: UUID // Required - no ? nullable
|
|
137
|
+
): EmployeeResponse
|
|
138
|
+
|
|
139
|
+
// Optional parameters - nullable with defaults
|
|
140
|
+
@Get("/employees")
|
|
141
|
+
suspend fun listEmployees(
|
|
142
|
+
@QueryValue search: String?, // Optional
|
|
143
|
+
@QueryValue status: String?, // Optional
|
|
144
|
+
@QueryValue page: Int?, // Optional, default in code
|
|
145
|
+
@QueryValue limit: Int? // Optional, default in code
|
|
146
|
+
): EmployeeListResponse {
|
|
147
|
+
val effectivePage = page ?: 1
|
|
148
|
+
val effectiveLimit = limit ?: 20
|
|
149
|
+
// ...
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Consistent pagination pattern:
|
|
154
|
+
|
|
155
|
+
```kotlin
|
|
156
|
+
// Request
|
|
157
|
+
@Get("/employees")
|
|
158
|
+
suspend fun listEmployees(
|
|
159
|
+
@QueryValue page: Int?, // 1-based page number
|
|
160
|
+
@QueryValue limit: Int? // Items per page (max 100)
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
// Response
|
|
164
|
+
data class EmployeeListResponse(
|
|
165
|
+
val items: List<EmployeeResponse>,
|
|
166
|
+
val total: Int,
|
|
167
|
+
val page: Int,
|
|
168
|
+
val limit: Int,
|
|
169
|
+
val hasMore: Boolean
|
|
170
|
+
)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Quick Reference
|
|
174
|
+
|
|
175
|
+
| Rule | Example |
|
|
176
|
+
|------|---------|
|
|
177
|
+
| No path params | `@Get("/employee")` with `@QueryValue id` |
|
|
178
|
+
| Plural for lists | `@Get("/employees")` |
|
|
179
|
+
| Singular for single | `@Get("/employee")` |
|
|
180
|
+
| Actions as sub-paths | `@Post("/employee/delete")` |
|
|
181
|
+
| Query params for all IDs | `?id=xxx` not `/{id}` |
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Model Location
|
|
186
|
+
|
|
187
|
+
### Rule: All API models live in `module-client` ONLY
|
|
188
|
+
|
|
189
|
+
**NEVER duplicate request or response models across modules.**
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
module-client/src/main/kotlin/com/yourcompany/client/
|
|
193
|
+
├── request/ # API request models
|
|
194
|
+
│ ├── conversation/
|
|
195
|
+
│ ├── message/
|
|
196
|
+
│ └── {domain}/
|
|
197
|
+
└── response/ # API response models
|
|
198
|
+
├── conversation/
|
|
199
|
+
├── message/
|
|
200
|
+
├── attachment/
|
|
201
|
+
└── {domain}/
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### NEVER create request/response models in module-api or module-impl
|
|
205
|
+
|
|
206
|
+
```kotlin
|
|
207
|
+
// WRONG:
|
|
208
|
+
// module-communication/module-api/model/response/ConversationResponse.kt
|
|
209
|
+
package com.yourcompany.communication.model.response
|
|
210
|
+
data class ConversationResponse(...) // DON'T DO THIS
|
|
211
|
+
|
|
212
|
+
// CORRECT:
|
|
213
|
+
// module-client/response/conversation/ConversationResponse.kt
|
|
214
|
+
package com.yourcompany.client.response.conversation
|
|
215
|
+
data class ConversationResponse(...) // PUT IT HERE
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Import from module-client
|
|
219
|
+
|
|
220
|
+
```kotlin
|
|
221
|
+
// In ConversationManager.kt or any other file
|
|
222
|
+
import com.yourcompany.client.response.conversation.ConversationResponse
|
|
223
|
+
import com.yourcompany.client.request.conversation.CreateConversationRequest
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Add module-client dependency
|
|
227
|
+
|
|
228
|
+
```gradle
|
|
229
|
+
dependencies {
|
|
230
|
+
implementation(project(":module-client"))
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Naming
|
|
235
|
+
|
|
236
|
+
| Type | Pattern | Example |
|
|
237
|
+
|------|---------|---------|
|
|
238
|
+
| Response | `{Entity}Response` | `ConversationResponse` |
|
|
239
|
+
| List response | `{Entity}ListResponse` | `ConversationListResponse` |
|
|
240
|
+
| Item in list | `{Entity}Item` | `ConversationItem` |
|
|
241
|
+
| Brief/summary | `{Entity}Brief` | `ContactBrief` |
|
|
242
|
+
| Request | `{Action}{Entity}Request` | `CreateConversationRequest` |
|
|
243
|
+
|
|
244
|
+
### Request Validation Annotations
|
|
245
|
+
|
|
246
|
+
All required fields in request DTOs MUST have validation annotations:
|
|
247
|
+
|
|
248
|
+
```kotlin
|
|
249
|
+
import jakarta.validation.constraints.NotBlank
|
|
250
|
+
import jakarta.validation.constraints.NotNull
|
|
251
|
+
|
|
252
|
+
data class CreateItemRequest(
|
|
253
|
+
@field:NotBlank(message = "name is required")
|
|
254
|
+
val name: String, // String → @field:NotBlank
|
|
255
|
+
|
|
256
|
+
@field:NotNull(message = "amount is required")
|
|
257
|
+
val amount: BigDecimal, // Non-String → @field:NotNull
|
|
258
|
+
|
|
259
|
+
val note: String? = null // Optional → no annotation
|
|
260
|
+
)
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**Message format:** snake_case field name + " is required" (e.g., `"participant_id is required"`).
|
|
264
|
+
|
|
265
|
+
Response DTOs do NOT need validation annotations.
|
|
266
|
+
|
|
267
|
+
Why this matters:
|
|
268
|
+
- Single source of truth
|
|
269
|
+
- No sync issues between duplicates
|
|
270
|
+
- Clear ownership
|
|
271
|
+
- Easier refactoring
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## Specs Must Include Frontend
|
|
276
|
+
|
|
277
|
+
### Rule: Every spec that touches the UI MUST have a "Frontend Changes" section
|
|
278
|
+
|
|
279
|
+
When writing a spec (`/spec`), if the feature touches anything the user sees, you MUST include:
|
|
280
|
+
|
|
281
|
+
### What to put in the Frontend Changes section
|
|
282
|
+
|
|
283
|
+
1. **Files to change** - List every frontend file that needs edits (components, types, API clients, pages)
|
|
284
|
+
2. **TypeScript type changes** - Show the before/after for any type changes (interfaces, enums)
|
|
285
|
+
3. **Component changes** - Describe what each component gains or loses:
|
|
286
|
+
- New UI elements (inputs, buttons, badges, columns)
|
|
287
|
+
- Where they go in the layout (which card, which section, before/after what)
|
|
288
|
+
- State management (new state? uses existing parent state?)
|
|
289
|
+
4. **API client changes** - New methods on the API client, with function signature
|
|
290
|
+
5. **UI behavior** - How the user interacts with the new stuff:
|
|
291
|
+
- What triggers actions (click, enter key, toggle)
|
|
292
|
+
- Validation (what's rejected, what error shows)
|
|
293
|
+
- Save flow (which button, which API call)
|
|
294
|
+
- Default values for new fields
|
|
295
|
+
|
|
296
|
+
### When does this apply?
|
|
297
|
+
|
|
298
|
+
- New API endpoint that returns data shown in UI
|
|
299
|
+
- Changes to existing API response shape (new fields)
|
|
300
|
+
- New config/settings the admin can change
|
|
301
|
+
- Any feature the user mentioned UI/UX for
|
|
302
|
+
|
|
303
|
+
### When does this NOT apply?
|
|
304
|
+
|
|
305
|
+
- Pure backend changes (scheduler fixes, sync logic, internal refactors)
|
|
306
|
+
- Features with no UI component at all
|
|
307
|
+
|
|
308
|
+
### Why this rule exists
|
|
309
|
+
|
|
310
|
+
Frontend gets forgotten in specs. Then during implementation, the FE work is vague and inconsistent. Specifying FE changes upfront means:
|
|
311
|
+
- The plan phase knows exactly which FE files to touch
|
|
312
|
+
- The implementation team doesn't have to guess UI layout
|
|
313
|
+
- Type changes are designed alongside API changes (no mismatches)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Batch Processing Patterns
|
|
2
|
+
|
|
3
|
+
## Core Rule
|
|
4
|
+
|
|
5
|
+
Any operation that processes a dataset that could grow beyond memory MUST use chunked pagination with safety guards.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## The Pattern
|
|
10
|
+
|
|
11
|
+
```kotlin
|
|
12
|
+
companion object {
|
|
13
|
+
const val CHUNK_SIZE = 500
|
|
14
|
+
const val MAX_CHUNKS = 100 // Prevents infinite loops / OOM
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
suspend fun processAllUsers(action: suspend (User) -> Unit): BatchResult {
|
|
18
|
+
var offset = 0
|
|
19
|
+
var chunksProcessed = 0
|
|
20
|
+
val failures = mutableListOf<BatchFailure>()
|
|
21
|
+
|
|
22
|
+
while (chunksProcessed < MAX_CHUNKS) {
|
|
23
|
+
val chunk = userRepository.findAll(limit = CHUNK_SIZE, offset = offset)
|
|
24
|
+
if (chunk.isEmpty()) break
|
|
25
|
+
|
|
26
|
+
chunk.forEach { user ->
|
|
27
|
+
runCatching { action(user) }
|
|
28
|
+
.onFailure { e ->
|
|
29
|
+
logger.warn("Failed to process user ${user.id}", e)
|
|
30
|
+
failures.add(BatchFailure(userId = user.id, error = e.message))
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (chunk.size < CHUNK_SIZE) break // Last page
|
|
35
|
+
offset += CHUNK_SIZE
|
|
36
|
+
chunksProcessed++
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return BatchResult(processed = offset, failures = failures)
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Rules
|
|
46
|
+
|
|
47
|
+
- Always define `CHUNK_SIZE` and `MAX_CHUNKS` as named constants (see KOTLIN.md § No Magic Numbers)
|
|
48
|
+
- `MAX_CHUNKS` guard prevents runaway loops if the dataset keeps growing
|
|
49
|
+
- Early exit when `chunk.size < CHUNK_SIZE` (last page)
|
|
50
|
+
- Never `findAll()` without limit — always paginate
|
|
51
|
+
- Log + collect failures per item, don't abort the whole batch for one bad record
|
|
52
|
+
- Return a result with failure count so the caller can decide
|
|
53
|
+
- Small fixed-size lists (< 100 items) don't need chunking
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Cancellation Check
|
|
58
|
+
|
|
59
|
+
For long-running jobs, check cancellation inside the loop:
|
|
60
|
+
|
|
61
|
+
```kotlin
|
|
62
|
+
while (chunksProcessed < MAX_CHUNKS) {
|
|
63
|
+
if (context.isCancelled(selfId, initiatorId)) {
|
|
64
|
+
logger.info("Batch cancelled after $chunksProcessed chunks")
|
|
65
|
+
break
|
|
66
|
+
}
|
|
67
|
+
// ... process chunk
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Bad Patterns
|
|
74
|
+
|
|
75
|
+
```kotlin
|
|
76
|
+
// WRONG — loads entire table into memory
|
|
77
|
+
val allUsers = userRepository.findAll() // 2M users → OOM
|
|
78
|
+
allUsers.forEach { sendEmail(it) }
|
|
79
|
+
|
|
80
|
+
// WRONG — no safety limit
|
|
81
|
+
var offset = 0
|
|
82
|
+
while (true) { // Never terminates if new records keep appearing
|
|
83
|
+
val chunk = repo.findAll(limit = 500, offset = offset)
|
|
84
|
+
if (chunk.isEmpty()) break
|
|
85
|
+
offset += 500
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// WRONG — one bad record kills the whole batch
|
|
89
|
+
chunk.forEach { user ->
|
|
90
|
+
action(user) // Throws on user #47 → users #48-500 never processed
|
|
91
|
+
}
|
|
92
|
+
```
|