@c0x12c/spartan-ai-toolkit 1.5.0 → 1.6.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 (44) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +2 -2
  3. package/VERSION +1 -1
  4. package/commands/spartan/build.md +59 -4
  5. package/package.json +1 -1
  6. package/packs/core.yaml +1 -0
  7. package/packs/packs.compiled.json +2 -1
  8. package/rules/core/SKILL_AUTHORING.md +174 -0
  9. package/skills/article-writing/SKILL.md +14 -0
  10. package/skills/article-writing/examples.md +59 -0
  11. package/skills/backend-api-design/SKILL.md +22 -125
  12. package/skills/backend-api-design/code-patterns.md +138 -0
  13. package/skills/brainstorm/SKILL.md +10 -0
  14. package/skills/browser-qa/SKILL.md +12 -105
  15. package/skills/browser-qa/playwright-snippets.md +110 -0
  16. package/skills/ci-cd-patterns/SKILL.md +108 -0
  17. package/skills/ci-cd-patterns/workflows.md +149 -0
  18. package/skills/competitive-teardown/SKILL.md +14 -0
  19. package/skills/competitive-teardown/example-analysis.md +50 -0
  20. package/skills/content-engine/SKILL.md +30 -0
  21. package/skills/content-engine/examples.md +72 -0
  22. package/skills/database-patterns/SKILL.md +29 -102
  23. package/skills/database-patterns/code-templates.md +114 -0
  24. package/skills/deep-research/SKILL.md +12 -0
  25. package/skills/design-workflow/SKILL.md +5 -45
  26. package/skills/design-workflow/checklists.md +45 -0
  27. package/skills/idea-validation/SKILL.md +14 -0
  28. package/skills/idea-validation/example-report.md +50 -0
  29. package/skills/investor-materials/SKILL.md +13 -6
  30. package/skills/investor-materials/example-outline.md +70 -0
  31. package/skills/investor-outreach/SKILL.md +14 -0
  32. package/skills/investor-outreach/examples.md +76 -0
  33. package/skills/kotlin-best-practices/SKILL.md +27 -114
  34. package/skills/kotlin-best-practices/code-patterns.md +132 -0
  35. package/skills/market-research/SKILL.md +12 -0
  36. package/skills/security-checklist/SKILL.md +12 -97
  37. package/skills/security-checklist/audit-reference.md +95 -0
  38. package/skills/service-debugging/SKILL.md +116 -0
  39. package/skills/service-debugging/common-issues.md +65 -0
  40. package/skills/startup-pipeline/SKILL.md +28 -1
  41. package/skills/testing-strategies/SKILL.md +27 -153
  42. package/skills/testing-strategies/examples.md +103 -0
  43. package/skills/testing-strategies/integration-test-setup.md +71 -0
  44. package/skills/ui-ux-pro-max/SKILL.md +8 -1
@@ -8,9 +8,9 @@
8
8
  "plugins": [
9
9
  {
10
10
  "name": "spartan-ai-toolkit",
11
- "description": "5 workflows, 55 commands, 11 rules, 20 skills, 6 agents — organized in 11 packs with dependencies",
11
+ "description": "5 workflows, 56 commands, 12 rules, 22 skills, 7 agents — organized in 11 packs with dependencies",
12
12
  "source": "./toolkit",
13
- "version": "1.5.0"
13
+ "version": "1.6.0"
14
14
  }
15
15
  ]
16
16
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "spartan-ai-toolkit",
3
- "version": "1.5.0",
4
- "description": "Engineering discipline layer for Claude Code — 5 workflows, 55 commands, 11 rules, 20 skills, 6 agents organized in 11 packs",
3
+ "version": "1.6.0",
4
+ "description": "Engineering discipline layer for Claude Code — 5 workflows, 56 commands, 12 rules, 22 skills, 7 agents organized in 11 packs",
5
5
  "author": {
6
6
  "name": "Khoa Tran",
7
7
  "url": "https://github.com/spartan-stratos"
package/VERSION CHANGED
@@ -1 +1 @@
1
- 1.5.0
1
+ 1.6.0
@@ -224,6 +224,8 @@ Uses skills: `ui-ux-pro-max`, frontend rules
224
224
 
225
225
  **Full-stack mode** — backend tasks first, then frontend tasks. Mark the integration point clearly (where frontend starts depending on backend API).
226
226
 
227
+ **CRITICAL: Full-stack means BOTH layers must complete.** Don't move to Gate 3 after finishing backend only. The plan must include frontend tasks and ALL tasks must be done before review. If the spec mentions UI changes, API responses shown to users, or any user-facing behavior — frontend tasks are mandatory.
228
+
227
229
  ### Create branch
228
230
 
229
231
  ```bash
@@ -299,7 +301,24 @@ Call the right skills based on what you're doing:
299
301
  | React components | `ui-ux-pro-max` |
300
302
  | Security-sensitive code | `security-checklist` |
301
303
 
302
- ### After all tasks
304
+ ### After all tasks — Verify Definition of Done
305
+
306
+ **Before moving to Gate 3, verify ALL layers are complete:**
307
+
308
+ | Mode | Must be done before review |
309
+ |------|---------------------------|
310
+ | **Backend** | Migration applied, entity/table/repo created, manager with business logic, controller with endpoints, all tests passing (`./gradlew test`) |
311
+ | **Frontend** | Types defined, API client updated, components built, page/routing wired, build passes (`yarn build` or `npm run build`) |
312
+ | **Full-stack** | ALL backend items above + ALL frontend items above + frontend calls the new backend API correctly |
313
+
314
+ **Full-stack verification (MANDATORY if mode is full-stack):**
315
+ 1. Backend tests pass: `./gradlew test`
316
+ 2. Frontend builds: `yarn build` or `npm run build`
317
+ 3. Frontend types match backend response DTOs
318
+ 4. API client has methods for all new endpoints
319
+ 5. UI shows the data from the new endpoints
320
+
321
+ **If any layer is incomplete**, go back and finish it. Do NOT proceed to Gate 3 with only backend done.
303
322
 
304
323
  Run the full test suite:
305
324
  ```bash
@@ -307,15 +326,17 @@ Run the full test suite:
307
326
  ./gradlew test
308
327
 
309
328
  # Frontend
310
- npm test
329
+ npm test && npm run build
311
330
 
312
- # Both
313
- ./gradlew test && npm test
331
+ # Both (full-stack)
332
+ ./gradlew test && npm test && npm run build
314
333
  ```
315
334
 
316
335
  **GATE 3 — STOP and ask:**
317
336
  > "All [N] tasks done. [X] tests passing. Ready for review?"
318
337
  >
338
+ > **Full-stack:** "Backend: [N] tasks done, tests passing. Frontend: [N] tasks done, build passing. Ready for review?"
339
+ >
319
340
  > If 3+ tasks were completed: "I'll run a self-review now. For a deeper dual-agent review, say 'gate review'."
320
341
  >
321
342
  > **Auto mode on?** → Continue to Review immediately.
@@ -401,3 +422,37 @@ If a previous session was interrupted (context overflow, user stopped, etc.), th
401
422
  - **Frontend checks for backend needs.** If a new page needs data that doesn't exist yet, say so at Stage 2 and add backend tasks first.
402
423
  - **Don't over-plan.** If the whole thing is 1-2 files and 30 minutes of work, don't force it into this workflow. Just do it. This workflow is for features that need structure — at least 2-3 tasks.
403
424
  - **Save state for big work.** If 5+ tasks, save artifacts to `.planning/` so future sessions can resume.
425
+ - **Full-stack = both layers done.** If the feature touches both backend and frontend, you MUST implement both before creating the PR. Backend-only completion is NOT "done" for a full-stack feature.
426
+
427
+ ---
428
+
429
+ ## Definition of Done
430
+
431
+ A feature is NOT done until every applicable item is checked:
432
+
433
+ ### Backend
434
+ - [ ] Database migration (if new/changed table)
435
+ - [ ] Kotlin entity, table object, repository with tests
436
+ - [ ] Manager with business logic and Either error handling
437
+ - [ ] Controller with proper annotations (@ExecuteOn, @Secured, @Validated)
438
+ - [ ] Integration tests for all endpoints (happy path, 404, 401)
439
+ - [ ] `./gradlew test` passes
440
+
441
+ ### Frontend
442
+ - [ ] TypeScript types/interfaces match backend DTOs
443
+ - [ ] API client methods for all new/changed endpoints
444
+ - [ ] Components built and connected to data
445
+ - [ ] Page routing and navigation working
446
+ - [ ] `yarn build` (or `npm run build`) passes with no errors
447
+ - [ ] Loading, empty, and error states handled
448
+
449
+ ### Full-Stack (ALL of the above, plus)
450
+ - [ ] Frontend calls backend API correctly (snake_case conversion, auth headers)
451
+ - [ ] Response data renders in the UI
452
+ - [ ] End-to-end flow works: user action → API call → backend processing → response → UI update
453
+ - [ ] Both `./gradlew test` AND `yarn build` pass
454
+
455
+ ### Always
456
+ - [ ] Atomic commits (one per task)
457
+ - [ ] Self-review passed (backend review + frontend review if applicable)
458
+ - [ ] PR created with summary and test plan
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c0x12c/spartan-ai-toolkit",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Engineering discipline layer for AI coding agents — commands, rules, skills, agents, and packs for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {
package/packs/core.yaml CHANGED
@@ -26,6 +26,7 @@ commands:
26
26
 
27
27
  rules:
28
28
  - core/NAMING_CONVENTIONS.md
29
+ - core/SKILL_AUTHORING.md
29
30
 
30
31
  skills: []
31
32
  agents:
@@ -28,7 +28,8 @@
28
28
  "gate-review"
29
29
  ],
30
30
  "rules": [
31
- "core/NAMING_CONVENTIONS.md"
31
+ "core/NAMING_CONVENTIONS.md",
32
+ "core/SKILL_AUTHORING.md"
32
33
  ],
33
34
  "skills": [],
34
35
  "agents": [
@@ -0,0 +1,174 @@
1
+ # Skill Authoring Rules
2
+
3
+ Rules for creating and modifying skills in the Spartan AI Toolkit. Follow these when writing new skills or improving existing ones.
4
+
5
+ ## Frontmatter (REQUIRED)
6
+
7
+ Every SKILL.md must have these fields:
8
+
9
+ ```yaml
10
+ ---
11
+ name: skill-name
12
+ description: "What it does. Use when [trigger conditions]."
13
+ allowed_tools:
14
+ - Read
15
+ - Write
16
+ # ... tools the skill needs
17
+ ---
18
+ ```
19
+
20
+ ### Description Must Be a Trigger
21
+
22
+ The description tells the model WHEN to activate the skill, not WHAT the skill is.
23
+
24
+ | Bad (summary) | Good (trigger) |
25
+ |----------------|----------------|
26
+ | "Database design patterns including schemas and migrations" | "Database design patterns. Use when creating tables, writing migrations, or implementing repositories." |
27
+ | "The full startup pipeline from brainstorm to outreach" | "Coordinates the full startup pipeline. Use when the user starts a new idea project or references stages/gates." |
28
+
29
+ **Rule:** Every description must contain "Use when" followed by specific trigger conditions.
30
+
31
+ ### allowed_tools Must Match the Skill's Needs
32
+
33
+ | Skill type | Typical tools |
34
+ |------------|--------------|
35
+ | Code/backend (writes files) | Read, Write, Edit, Glob, Grep, Bash |
36
+ | Research/analysis (web searches) | WebSearch, WebFetch, Read |
37
+ | Content/writing (creates + researches) | Read, Write, WebSearch |
38
+ | Review/audit (reads only) | Read, Glob, Grep |
39
+
40
+ ---
41
+
42
+ ## Folder Structure (Skills Are Folders, Not Files)
43
+
44
+ A skill is a directory, not just a markdown file. Use the file system for progressive disclosure.
45
+
46
+ ```
47
+ toolkit/skills/my-skill/
48
+ SKILL.md # Main definition — short, high-level (required)
49
+ code-patterns.md # Code examples (if code-heavy skill)
50
+ examples.md # Good/bad examples (if teaching a style)
51
+ checklists.md # Review checklists (if audit/review skill)
52
+ workflows.md # Ready-to-use templates (if scaffolding skill)
53
+ ```
54
+
55
+ ### When to Split Into Multiple Files
56
+
57
+ | SKILL.md is... | Action |
58
+ |-----------------|--------|
59
+ | Under 100 lines | One file is fine |
60
+ | 100-150 lines with code blocks | Split code into a reference file |
61
+ | 150+ lines | Must split — too much for one read |
62
+
63
+ ### SKILL.md Should Be the Summary
64
+
65
+ Keep SKILL.md short (60-120 lines). It should have:
66
+ - Frontmatter
67
+ - "When to Use" section
68
+ - Key rules and principles (without detailed code)
69
+ - Gotchas section
70
+ - References to supporting files
71
+
72
+ Move into supporting files:
73
+ - Detailed code templates and examples
74
+ - Long checklists
75
+ - Good/bad comparisons
76
+ - Ready-to-use templates
77
+
78
+ Reference with: `> See code-patterns.md for complete implementation templates.`
79
+
80
+ ---
81
+
82
+ ## Gotchas Section (REQUIRED)
83
+
84
+ Every skill must have a `## Gotchas` section. This is the highest-value content in any skill.
85
+
86
+ ### Format
87
+
88
+ ```markdown
89
+ ## Gotchas
90
+
91
+ - **Bold lead-in sentence.** Explanation of why this matters and what to do instead.
92
+ - **Another gotcha.** Details.
93
+ ```
94
+
95
+ ### What Makes a Good Gotcha
96
+
97
+ - Specific failure patterns Claude hits when using this skill
98
+ - Things that look right but are wrong
99
+ - Common mistakes users make in this domain
100
+ - Counter-intuitive rules that violate defaults
101
+
102
+ ### What is NOT a Gotcha
103
+
104
+ - General best practices (put those in Rules)
105
+ - Obvious things Claude already knows
106
+ - Restating the instructions in negative form
107
+
108
+ **Minimum 3 gotchas per skill. Build this section over time as you find new failure patterns.**
109
+
110
+ ---
111
+
112
+ ## Content Rules
113
+
114
+ ### Don't State the Obvious
115
+
116
+ Claude already knows how to code, research, and write. Focus on information that pushes Claude OUT of its normal patterns.
117
+
118
+ | Bad (obvious) | Good (non-obvious) |
119
+ |----------------|---------------------|
120
+ | "Use proper error handling" | "`!!` is banned — use `?.`, `?:`, or null check" |
121
+ | "Write clean code" | "Don't add docstrings to code you didn't change" |
122
+ | "Research thoroughly" | "Press releases aren't research — cross-check with third-party sources" |
123
+
124
+ ### Give Claude Flexibility
125
+
126
+ Tell Claude WHAT to check and WHY, not exact steps for every situation. Skills are reused across many contexts — being too specific makes them brittle.
127
+
128
+ | Bad (railroading) | Good (flexible) |
129
+ |---------------------|------------------|
130
+ | "Step 1: Open file X. Step 2: Find line Y. Step 3: Change to Z." | "Check the controller for @ExecuteOn annotation. If missing, add it." |
131
+
132
+ ### Use Examples Over Instructions
133
+
134
+ A good/bad example teaches more than a paragraph of rules. When possible, show rather than tell.
135
+
136
+ ---
137
+
138
+ ## Config Pattern (for Stateful Skills)
139
+
140
+ Skills that run repeatedly for the same user should store preferences:
141
+
142
+ ```json
143
+ // content-config.json in the project root
144
+ {
145
+ "defaultPlatforms": ["x", "linkedin"],
146
+ "brandVoice": "direct and technical",
147
+ "audience": "developers"
148
+ }
149
+ ```
150
+
151
+ Read config at skill start. Skip setup questions for configured fields.
152
+
153
+ ---
154
+
155
+ ## Naming
156
+
157
+ | Type | Convention | Example |
158
+ |------|-----------|---------|
159
+ | Skill directory | `kebab-case` | `ci-cd-patterns/` |
160
+ | Main file | `SKILL.md` (always) | `SKILL.md` |
161
+ | Supporting files | `kebab-case.md` | `code-patterns.md` |
162
+
163
+ ---
164
+
165
+ ## Checklist: Before Shipping a Skill
166
+
167
+ - [ ] Frontmatter has `name`, `description` (with trigger), and `allowed_tools`
168
+ - [ ] Description says "Use when..." with specific conditions
169
+ - [ ] SKILL.md is under 120 lines (split if longer)
170
+ - [ ] Has a `## Gotchas` section with 3+ items
171
+ - [ ] Code-heavy content is in supporting files, not inline
172
+ - [ ] Examples show good AND bad patterns where applicable
173
+ - [ ] Doesn't restate things Claude already knows
174
+ - [ ] Gives Claude flexibility — principles over exact steps
@@ -1,6 +1,10 @@
1
1
  ---
2
2
  name: article-writing
3
3
  description: Write blog posts, guides, tutorials, and long-form content. Sounds like a real person, not AI. Use when the user wants polished written content.
4
+ allowed_tools:
5
+ - Read
6
+ - Write
7
+ - WebSearch
4
8
  ---
5
9
 
6
10
  # Article Writing
@@ -14,6 +18,8 @@ Write long-form content that sounds like a person wrote it.
14
18
  - Matching an existing voice from examples
15
19
  - Cleaning up and tightening existing writing
16
20
 
21
+ > See `examples.md` for good vs bad writing examples that show what "sounds human" actually means.
22
+
17
23
  ## Rules
18
24
 
19
25
  1. Start with something concrete: example, number, story, or code block.
@@ -82,6 +88,14 @@ This is a two-way talk:
82
88
  - Skip the "this part is weak" feedback
83
89
  - Write AI-sounding content and call it done
84
90
 
91
+ ## Gotchas
92
+
93
+ - **The intro is where most articles die.** If the first paragraph starts with "In this article, we'll explore..." — delete it and start with a story, stat, or code block.
94
+ - **AI-written articles all sound the same.** They hedge ("it's important to note"), use transition words nobody says out loud ("Moreover"), and avoid strong opinions. Cut all of that.
95
+ - **Don't explain the obvious.** If your audience is developers, don't explain what an API is. Write for the person, not the lowest common denominator.
96
+ - **Long ≠ thorough.** A 3,000-word article with 1,500 words of filler is worse than a 1,500-word article where every sentence earns its spot.
97
+ - **Every section needs evidence.** A claim without a number, example, or code block is just an opinion. Back it up or cut it.
98
+
85
99
  ## Before You Deliver
86
100
 
87
101
  - Facts match sources
@@ -0,0 +1,59 @@
1
+ # Article Writing — Good vs Bad Examples
2
+
3
+ > Read these examples to calibrate your writing style. The "bad" versions are typical AI output. The "good" versions sound like a person wrote them.
4
+
5
+ ## Intro Paragraphs
6
+
7
+ ### Bad (AI-style)
8
+ > In today's rapidly evolving technological landscape, the importance of effective error handling cannot be overstated. This comprehensive guide will walk you through the various aspects of implementing robust error handling strategies in your Kotlin applications, enabling you to build more resilient and maintainable software systems.
9
+
10
+ ### Good (human-style)
11
+ > Last Tuesday, our payment service threw 4,000 unhandled exceptions in 20 minutes. The fix was 3 lines of code. Here's what went wrong and how to make sure it doesn't happen to you.
12
+
13
+ ---
14
+
15
+ ## Explaining a Concept
16
+
17
+ ### Bad (AI-style)
18
+ > Dependency injection is a crucial software design pattern that facilitates the development of loosely coupled, maintainable, and testable code. By leveraging this paradigm, developers can effectively manage the complex dependencies between various components of their application architecture.
19
+
20
+ ### Good (human-style)
21
+ > Dependency injection means your class doesn't create its own dependencies — someone hands them in. Instead of `val db = Database()` inside your class, you write `class UserRepo(val db: Database)` and let the framework wire it up. That's it. The rest is details.
22
+
23
+ ---
24
+
25
+ ## Technical Guide Section
26
+
27
+ ### Bad (AI-style)
28
+ > To implement this feature, you'll want to start by carefully considering the architectural implications. First, create a new service class that will handle the business logic. Then, implement the necessary repository methods. Finally, wire everything together in the controller layer. This approach ensures a clean separation of concerns.
29
+
30
+ ### Good (human-style)
31
+ > Three files to create:
32
+ > 1. `PaymentManager.kt` — validates the amount, calls Stripe, saves the record
33
+ > 2. `PaymentRepository.kt` — insert and soft-delete methods
34
+ > 3. `PaymentController.kt` — one POST endpoint, delegates to the manager
35
+ >
36
+ > Start with the manager. The other two are boilerplate.
37
+
38
+ ---
39
+
40
+ ## Transitions Between Sections
41
+
42
+ ### Bad (AI-style)
43
+ > Now that we've explored the fundamentals of error handling, let's delve into the more advanced aspects of this topic. In the following section, we'll examine how to implement custom error types that can provide more granular control over your application's error handling mechanisms.
44
+
45
+ ### Good (human-style)
46
+ > The basics work for 80% of cases. But when you need different error responses for different callers (API vs webhook vs internal), you need custom error types.
47
+
48
+ ---
49
+
50
+ ## Conclusions
51
+
52
+ ### Bad (AI-style)
53
+ > In conclusion, we've covered a comprehensive overview of error handling patterns in Kotlin. By implementing these strategies, you'll be well-equipped to build robust, maintainable applications. Remember that error handling is not just about catching exceptions — it's about creating a resilient system that gracefully handles the unexpected.
54
+
55
+ ### Good (human-style)
56
+ > Three things to remember:
57
+ > 1. Return `Either`, don't throw. Your callers will thank you.
58
+ > 2. Log the actual error, not a generic message.
59
+ > 3. Test the error paths. They run more often than you think.
@@ -1,6 +1,12 @@
1
1
  ---
2
2
  name: backend-api-design
3
3
  description: Design RPC-style APIs with layered architecture (Controller → Manager → Repository). Use when creating new API endpoints, designing API contracts, or reviewing API patterns.
4
+ allowed_tools:
5
+ - Read
6
+ - Write
7
+ - Edit
8
+ - Glob
9
+ - Grep
4
10
  ---
5
11
 
6
12
  # Backend API Design — Quick Reference
@@ -24,36 +30,6 @@ POST /api/v1/sync/employees # Action
24
30
  - **Plural for collections** — `/employees`
25
31
  - **Verb sub-paths for actions** — `/delete`, `/restore`, `/sync`
26
32
 
27
- ## Controller Template
28
-
29
- ```kotlin
30
- @ExecuteOn(TaskExecutors.IO) // REQUIRED for suspend
31
- @Validated
32
- @Controller("/api/v1/admin")
33
- @Secured(OAuthSecurityRule.ADMIN)
34
- class EmployeeController(
35
- private val employeeManager: EmployeeManager // Managers ONLY
36
- ) {
37
- @Get("/employee")
38
- suspend fun getEmployee(@QueryValue id: UUID): EmployeeResponse {
39
- return employeeManager.findById(id).throwOrValue()
40
- }
41
-
42
- @Get("/employees")
43
- suspend fun listEmployees(
44
- @QueryValue page: Int?,
45
- @QueryValue limit: Int?,
46
- @QueryValue status: String?
47
- ): EmployeeListResponse {
48
- return employeeManager.list(
49
- page = page ?: 1,
50
- limit = (limit ?: 20).coerceAtMost(100),
51
- status = status
52
- ).throwOrValue()
53
- }
54
- }
55
- ```
56
-
57
33
  ## Layered Architecture
58
34
 
59
35
  ```
@@ -81,107 +57,28 @@ Repository → data access only, no business logic
81
57
  - `db.replica` for reads, `db.primary` for writes
82
58
  - Always checks `deletedAt.isNull()`
83
59
 
84
- ## Response Models
60
+ ## Quick Code Reference
85
61
 
86
- All in `module-client/response/{domain}/`:
62
+ The core controller delegation pattern:
87
63
 
88
64
  ```kotlin
89
- data class EmployeeResponse(
90
- val id: UUID,
91
- val name: String,
92
- val email: String,
93
- val status: String,
94
- val createdAt: Instant
95
- ) {
96
- companion object {
97
- fun from(entity: EmployeeEntity) = EmployeeResponse(
98
- id = entity.id,
99
- name = entity.name,
100
- email = entity.email,
101
- status = entity.status,
102
- createdAt = entity.createdAt
103
- )
104
- }
65
+ @Get("/employee")
66
+ suspend fun getEmployee(@QueryValue id: UUID): EmployeeResponse {
67
+ return employeeManager.findById(id).throwOrValue()
105
68
  }
106
-
107
- data class EmployeeListResponse(
108
- val items: List<EmployeeResponse>,
109
- val total: Int,
110
- val page: Int,
111
- val limit: Int,
112
- val hasMore: Boolean
113
- )
114
69
  ```
115
70
 
116
- ## Pagination Pattern
71
+ - **Response models** — `companion object { fun from(entity) }` in `module-client/response/{domain}/`
72
+ - **Pagination** — offset-based, manager returns `EmployeeListResponse` with `items`, `total`, `page`, `limit`, `hasMore`
73
+ - **Errors** — return `ClientError.NOT_FOUND.asException().left()` from managers, never throw
74
+ - **Factory beans** — `@Factory` class with `@Singleton` method, wire repos + db into manager
117
75
 
118
- ```kotlin
119
- override suspend fun list(
120
- page: Int,
121
- limit: Int,
122
- status: String?
123
- ): Either<ClientException, EmployeeListResponse> {
124
- val offset = (page - 1) * limit
125
-
126
- val (items, total) = transaction(db.replica) {
127
- val query = EmployeesTable
128
- .selectAll()
129
- .where { EmployeesTable.deletedAt.isNull() }
130
-
131
- if (status != null) {
132
- query.andWhere { EmployeesTable.status eq status }
133
- }
134
-
135
- val total = query.count().toInt()
136
- val items = query
137
- .orderBy(EmployeesTable.createdAt to SortOrder.DESC)
138
- .limit(limit)
139
- .offset(offset.toLong())
140
- .map { convert(it) }
141
-
142
- items to total
143
- }
144
-
145
- return EmployeeListResponse(
146
- items = items.map { EmployeeResponse.from(it) },
147
- total = total,
148
- page = page,
149
- limit = limit,
150
- hasMore = (page * limit) < total
151
- ).right()
152
- }
153
- ```
76
+ > See code-patterns.md for complete controller, response model, pagination, error handling, and factory bean templates.
154
77
 
155
- ## Error Pattern
78
+ ## Gotchas
156
79
 
157
- ```kotlin
158
- // Not found
159
- val entity = repository.byId(id)
160
- ?: return ClientError.NOT_FOUND.asException().left()
161
-
162
- // Already exists
163
- val existing = repository.byEmail(email)
164
- if (existing != null) {
165
- return ClientError.ALREADY_EXISTS.asException().left()
166
- }
167
-
168
- // Validation
169
- if (request.name.isBlank()) {
170
- return ClientError.INVALID_INPUT.asException("Name is required").left()
171
- }
172
- ```
173
-
174
- ## Factory Bean
175
-
176
- ```kotlin
177
- @Factory
178
- class EmployeeManagerFactory {
179
- @Singleton
180
- fun provideEmployeeManager(
181
- employeeRepository: EmployeeRepository,
182
- db: DatabaseContext
183
- ): EmployeeManager {
184
- return DefaultEmployeeManager(employeeRepository, db)
185
- }
186
- }
187
- ```
80
+ - **Multi-word `@QueryValue` params MUST have explicit snake_case names.** The frontend axios interceptor sends `project_id` but Micronaut matches the literal param name. Write `@QueryValue("project_id") projectId: UUID`, not bare `@QueryValue projectId: UUID`.
81
+ - **Don't use `@Put`, `@Delete`, or `@Patch`.** This is RPC-style — all mutations are `@Post`. The only `@Get` is for reads.
82
+ - **Controllers that inject repositories are a code smell.** If you see `private val fooRepository: FooRepository` in a controller, move it to the manager.
83
+ - **`andWhere {}` not second `.where {}`.** Calling `.where {}` twice replaces the first condition. Use `.andWhere {}` to chain.
84
+ - **Don't forget `@ExecuteOn(TaskExecutors.IO)`.** Without it, suspend functions may hang or run on the wrong thread pool. Every controller needs it.