@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,552 @@
1
+ # Database Table Creator - Code Examples
2
+
3
+ This file contains actual code patterns extracted from the codebase. **Follow these patterns exactly**.
4
+
5
+ ---
6
+
7
+ ## Kotlin Table Object Pattern
8
+
9
+ **File**: `app/module-repository/src/main/kotlin/com/yourcompany/postgresql/table/UserTable.kt`
10
+
11
+ ```kotlin
12
+ package com.yourcompany.postgresql.table
13
+
14
+ import org.jetbrains.exposed.dao.id.UUIDTable
15
+ import org.jetbrains.exposed.sql.javatime.timestamp
16
+ import java.time.Instant
17
+
18
+ object UserTable : UUIDTable("users") { // ✓ MUST extend UUIDTable
19
+ // Business columns
20
+ val email = text("email").nullable() // ✓ Use text() not varchar()
21
+ val emailVerified = bool("email_verified").default(false)
22
+ val displayName = text("display_name").nullable()
23
+ val avatarUrl = text("avatar_url").nullable()
24
+ val authId = text("auth_id").nullable()
25
+ val provider = text("provider").nullable()
26
+ val status = text("status").default("active")
27
+ val referralSource = text("referral_source").default("OTHER")
28
+ val password = text("password").nullable()
29
+ val userRoleType = text("user_role_type").nullable()
30
+ val userType = text("user_type").nullable()
31
+ val userStatusType = text("user_status_type").nullable()
32
+ val companyId = uuid("company_id").nullable()
33
+
34
+ // Required timestamp columns
35
+ val createdAt = timestamp("created_at").clientDefault { Instant.now() }
36
+ val updatedAt = timestamp("updated_at").nullable()
37
+ val deletedAt = timestamp("deleted_at").nullable()
38
+ }
39
+ ```
40
+
41
+ **Key points**:
42
+ - Extends `UUIDTable` (provides UUID primary key)
43
+ - Uses `text()` for all string columns (matches SQL TEXT type)
44
+ - Uses `.nullable()` for optional fields
45
+ - Standard timestamps: createdAt (non-null), updatedAt (nullable), deletedAt (nullable)
46
+ - Column names match SQL exactly (snake_case)
47
+
48
+ ---
49
+
50
+ ## Entity Data Class Pattern
51
+
52
+ **File**: `app/module-repository/src/main/kotlin/com/yourcompany/postgresql/entity/UserEntity.kt`
53
+
54
+ ```kotlin
55
+ package com.yourcompany.postgresql.entity
56
+
57
+ import com.yourcompany.postgresql.constant.ReferralSource
58
+ import com.yourcompany.postgresql.constant.UserRoleType
59
+ import com.yourcompany.postgresql.constant.UserStatus
60
+ import com.yourcompany.postgresql.constant.UserStatusType
61
+ import com.yourcompany.postgresql.constant.UserType
62
+ import java.time.Instant
63
+ import java.util.UUID
64
+
65
+ data class UserEntity( // ✓ Use data class
66
+ override val id: UUID = UUID.randomUUID(), // ✓ Override from Entity
67
+ val email: String? = null,
68
+ val emailVerified: Boolean = false,
69
+ val displayName: String? = null,
70
+ val avatarUrl: String? = null,
71
+ val authId: String? = null,
72
+ val provider: String? = null,
73
+ val status: UserStatus? = null, // ✓ Use enum types when appropriate
74
+ val referralSource: ReferralSource? = null,
75
+ val password: String? = null,
76
+ val role: UserRoleType? = null,
77
+ val type: UserType? = null,
78
+ val businessStatus: UserStatusType? = null,
79
+ val companyId: UUID? = null,
80
+ override val createdAt: Instant = Instant.now(), // ✓ Override from Entity
81
+ override val updatedAt: Instant? = null, // ✓ Override from Entity
82
+ override val deletedAt: Instant? = null // ✓ Override from Entity
83
+ ) : Entity<Instant> // ✓ MUST implement Entity<Instant>
84
+ ```
85
+
86
+ **Key points**:
87
+ - Must be a `data class`
88
+ - Must implement `Entity<Instant>`
89
+ - Must override: id, createdAt, updatedAt, deletedAt
90
+ - Use `String` for TEXT columns
91
+ - Use `UUID` for UUID columns
92
+ - Use `Instant` for TIMESTAMP columns
93
+ - Use enums for status/type columns
94
+ - Nullable fields have `? = null` defaults
95
+
96
+ ---
97
+
98
+ ## Repository Interface Pattern
99
+
100
+ ```kotlin
101
+ package com.yourcompany.postgresql.repository
102
+
103
+ import com.yourcompany.postgresql.entity.UserEntity
104
+ import java.util.UUID
105
+
106
+ interface UserRepository {
107
+ // Standard CRUD operations
108
+ fun insert(entity: UserEntity): UserEntity
109
+ fun update(
110
+ id: UUID,
111
+ email: String? = null,
112
+ displayName: String? = null,
113
+ // ... other fields
114
+ ): UserEntity?
115
+
116
+ fun byId(id: UUID): UserEntity?
117
+ fun byIds(ids: List<UUID>): List<UserEntity>
118
+
119
+ // Custom query methods
120
+ fun byEmail(email: String): UserEntity?
121
+ fun byAuthId(authId: String): UserEntity?
122
+
123
+ // Soft delete operations
124
+ fun deleteById(id: UUID): UserEntity?
125
+ fun restoreById(id: UUID): UserEntity?
126
+ }
127
+ ```
128
+
129
+ **Key points**:
130
+ - Use domain-specific method names (`byEmail`, not `findByEmail`)
131
+ - Return entity or null (don't throw exceptions)
132
+ - Update accepts optional fields
133
+ - Include soft delete and restore methods
134
+
135
+ ---
136
+
137
+ ## Repository Implementation Pattern
138
+
139
+ ```kotlin
140
+ package com.yourcompany.postgresql.repository
141
+
142
+ import com.yourcompany.database.DatabaseContext
143
+ import com.yourcompany.postgresql.entity.UserEntity
144
+ import com.yourcompany.postgresql.table.UserTable
145
+ import org.jetbrains.exposed.sql.*
146
+ import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
147
+ import org.jetbrains.exposed.sql.transactions.transaction
148
+ import java.time.Instant
149
+ import java.util.UUID
150
+
151
+ class DefaultUserRepository(
152
+ private val db: DatabaseContext // ✓ Constructor injection
153
+ ) : UserRepository {
154
+
155
+ override fun insert(entity: UserEntity): UserEntity {
156
+ return transaction(db.primary) { // ✓ Use db.primary for writes
157
+ UserTable.insert {
158
+ it[id] = entity.id
159
+ it[email] = entity.email
160
+ it[emailVerified] = entity.emailVerified
161
+ it[displayName] = entity.displayName
162
+ // ... map all fields
163
+ it[createdAt] = entity.createdAt
164
+ it[updatedAt] = entity.updatedAt
165
+ it[deletedAt] = entity.deletedAt
166
+ }
167
+ entity
168
+ }
169
+ }
170
+
171
+ override fun update(
172
+ id: UUID,
173
+ email: String?,
174
+ displayName: String?
175
+ ): UserEntity? {
176
+ return transaction(db.primary) {
177
+ val updated = UserTable.update(
178
+ where = {
179
+ UserTable.id eq id and // ✓ Filter by id
180
+ UserTable.deletedAt.isNull() // ✓ CRITICAL: Filter soft deletes
181
+ }
182
+ ) {
183
+ email?.let { value -> it[UserTable.email] = value }
184
+ displayName?.let { value -> it[UserTable.displayName] = value }
185
+ // updatedAt automatically set by database trigger
186
+ }
187
+ if (updated > 0) byId(id) else null // ✓ Return updated entity or null
188
+ }
189
+ }
190
+
191
+ override fun byId(id: UUID): UserEntity? {
192
+ return transaction(db.replica) { // ✓ Use db.replica for reads
193
+ UserTable
194
+ .selectAll()
195
+ .where { UserTable.id eq id }
196
+ .andWhere { UserTable.deletedAt.isNull() } // ✓ CRITICAL: Filter soft deletes
197
+ .map { convert(it) }
198
+ .singleOrNull() // ✓ Return null if not found
199
+ }
200
+ }
201
+
202
+ override fun byEmail(email: String): UserEntity? {
203
+ return transaction(db.replica) {
204
+ UserTable
205
+ .selectAll()
206
+ .where { UserTable.email eq email }
207
+ .andWhere { UserTable.deletedAt.isNull() } // ✓ ALWAYS filter soft deletes
208
+ .map { convert(it) }
209
+ .singleOrNull()
210
+ }
211
+ }
212
+
213
+ override fun deleteById(id: UUID): UserEntity? {
214
+ return transaction(db.primary) {
215
+ val updated = UserTable.update(
216
+ where = {
217
+ UserTable.id eq id and
218
+ UserTable.deletedAt.isNull() // ✓ Only delete active records
219
+ }
220
+ ) {
221
+ it[deletedAt] = Instant.now() // ✓ Soft delete: set timestamp
222
+ }
223
+ if (updated > 0) {
224
+ // Return the soft-deleted entity (need to query without deletedAt filter)
225
+ UserTable
226
+ .selectAll()
227
+ .where { UserTable.id eq id }
228
+ .map { convert(it) }
229
+ .singleOrNull()
230
+ } else null
231
+ }
232
+ }
233
+
234
+ override fun restoreById(id: UUID): UserEntity? {
235
+ return transaction(db.primary) {
236
+ val updated = UserTable.update(
237
+ where = {
238
+ UserTable.id eq id and
239
+ UserTable.deletedAt.isNotNull() // ✓ Only restore deleted records
240
+ }
241
+ ) {
242
+ it[deletedAt] = null // ✓ Restore: clear timestamp
243
+ }
244
+ if (updated > 0) byId(id) else null
245
+ }
246
+ }
247
+
248
+ // ✓ Private helper for mapping
249
+ private fun convert(row: ResultRow): UserEntity {
250
+ return UserEntity(
251
+ id = row[UserTable.id].value, // ✓ UUID field needs .value
252
+ email = row[UserTable.email],
253
+ emailVerified = row[UserTable.emailVerified],
254
+ displayName = row[UserTable.displayName],
255
+ avatarUrl = row[UserTable.avatarUrl],
256
+ authId = row[UserTable.authId],
257
+ provider = row[UserTable.provider],
258
+ // Map enums properly
259
+ status = row[UserTable.status]?.let { UserStatus.valueOf(it) },
260
+ referralSource = row[UserTable.referralSource]?.let { ReferralSource.valueOf(it) },
261
+ password = row[UserTable.password],
262
+ role = row[UserTable.userRoleType]?.let { UserRoleType.valueOf(it) },
263
+ type = row[UserTable.userType]?.let { UserType.valueOf(it) },
264
+ businessStatus = row[UserTable.userStatusType]?.let { UserStatusType.valueOf(it) },
265
+ companyId = row[UserTable.companyId],
266
+ createdAt = row[UserTable.createdAt],
267
+ updatedAt = row[UserTable.updatedAt],
268
+ deletedAt = row[UserTable.deletedAt]
269
+ )
270
+ }
271
+ }
272
+ ```
273
+
274
+ **Critical patterns**:
275
+ - Use `transaction(db.primary)` for all writes (insert, update, delete)
276
+ - Use `transaction(db.replica)` for all reads
277
+ - **ALWAYS** include `.andWhere { deletedAt.isNull() }` in queries
278
+ - Soft delete sets `deletedAt = Instant.now()`
279
+ - Return null when record not found (don't throw)
280
+ - Private `convert()` method for ResultRow → Entity mapping
281
+ - NO `!!` operators anywhere
282
+
283
+ ---
284
+
285
+ ## Repository Factory Bean Pattern
286
+
287
+ **File**: `app/module-repository/src/main/kotlin/com/yourcompany/runtime/factory/RepositoryFactory.kt`
288
+
289
+ ```kotlin
290
+ package com.yourcompany.runtime.factory
291
+
292
+ import com.yourcompany.database.DatabaseContext
293
+ import com.yourcompany.postgresql.repository.*
294
+ import io.micronaut.context.annotation.Factory
295
+ import jakarta.inject.Singleton
296
+
297
+ @Factory
298
+ class RepositoryFactory {
299
+
300
+ @Singleton
301
+ fun provideUserRepository(db: DatabaseContext): UserRepository {
302
+ return DefaultUserRepository(db)
303
+ }
304
+
305
+ @Singleton
306
+ fun provideContactRepository(db: DatabaseContext): ContactRepository {
307
+ return DefaultContactRepository(db)
308
+ }
309
+
310
+ // Add new repository beans here
311
+ }
312
+ ```
313
+
314
+ **Key points**:
315
+ - Use `@Factory` class annotation
316
+ - Use `@Singleton` method annotation
317
+ - Return interface type, not implementation
318
+ - Accept `DatabaseContext` parameter
319
+ - Method name: `provide{TableName}Repository`
320
+
321
+ ---
322
+
323
+ ## Repository Test Pattern
324
+
325
+ ```kotlin
326
+ package com.yourcompany.postgresql.repository
327
+
328
+ import assertk.assertThat
329
+ import assertk.assertions.*
330
+ import com.yourcompany.postgresql.entity.UserEntity
331
+ import com.yourcompany.postgresql.constant.UserStatus
332
+ import io.micronaut.test.extensions.junit5.annotation.MicronautTest
333
+ import jakarta.inject.Inject
334
+ import org.junit.jupiter.api.BeforeEach
335
+ import org.junit.jupiter.api.Test
336
+ import java.util.UUID
337
+
338
+ @MicronautTest(environments = ["test"]) // ✓ Test environment
339
+ class DefaultUserRepositoryTest : AbstractRepositoryTest() { // ✓ Extend base class
340
+
341
+ @Inject
342
+ lateinit var repository: UserRepository // ✓ Inject interface
343
+
344
+ @BeforeEach
345
+ fun setup() {
346
+ database.primary.truncateAllTables() // ✓ Clean state
347
+ }
348
+
349
+ @Test
350
+ fun `insert - creates user successfully`() {
351
+ val entity = dummyUserEntity()
352
+
353
+ val result = repository.insert(entity)
354
+
355
+ assertThat(result.id).isEqualTo(entity.id)
356
+ assertThat(result.email).isEqualTo(entity.email)
357
+ }
358
+
359
+ @Test
360
+ fun `update - updates selected fields only`() {
361
+ val entity = repository.insert(dummyUserEntity())
362
+ val newEmail = "newemail@example.com"
363
+
364
+ val updated = repository.update(
365
+ id = entity.id,
366
+ email = newEmail
367
+ )
368
+
369
+ assertThat(updated).isNotNull()
370
+ assertThat(updated?.email).isEqualTo(newEmail)
371
+ assertThat(updated?.displayName).isEqualTo(entity.displayName) // Unchanged
372
+ }
373
+
374
+ @Test
375
+ fun `byId - returns entity when exists`() {
376
+ val entity = repository.insert(dummyUserEntity())
377
+
378
+ val result = repository.byId(entity.id)
379
+
380
+ assertThat(result).isNotNull()
381
+ assertThat(result?.id).isEqualTo(entity.id)
382
+ }
383
+
384
+ @Test
385
+ fun `byId - returns null when not exists`() {
386
+ val randomId = UUID.randomUUID()
387
+
388
+ val result = repository.byId(randomId)
389
+
390
+ assertThat(result).isNull()
391
+ }
392
+
393
+ @Test
394
+ fun `byId - returns null when soft deleted`() {
395
+ val entity = repository.insert(dummyUserEntity())
396
+ repository.deleteById(entity.id) // Soft delete
397
+
398
+ val result = repository.byId(entity.id)
399
+
400
+ assertThat(result).isNull() // ✓ Soft deleted records not returned
401
+ }
402
+
403
+ @Test
404
+ fun `deleteById - soft deletes entity`() {
405
+ val entity = repository.insert(dummyUserEntity())
406
+
407
+ val deleted = repository.deleteById(entity.id)
408
+
409
+ assertThat(deleted).isNotNull()
410
+ assertThat(deleted?.deletedAt).isNotNull() // ✓ Has timestamp
411
+ assertThat(repository.byId(entity.id)).isNull() // ✓ Not found in queries
412
+ }
413
+
414
+ @Test
415
+ fun `restoreById - restores soft deleted entity`() {
416
+ val entity = repository.insert(dummyUserEntity())
417
+ repository.deleteById(entity.id)
418
+
419
+ val restored = repository.restoreById(entity.id)
420
+
421
+ assertThat(restored).isNotNull()
422
+ assertThat(restored?.deletedAt).isNull() // ✓ Timestamp cleared
423
+ assertThat(repository.byId(entity.id)).isNotNull() // ✓ Found in queries
424
+ }
425
+
426
+ // ✓ Test helper with defaults
427
+ private fun dummyUserEntity(
428
+ id: UUID = UUID.randomUUID(),
429
+ email: String = "${UUID.randomUUID()}@test.com"
430
+ ) = UserEntity(
431
+ id = id,
432
+ email = email,
433
+ emailVerified = false,
434
+ displayName = "Test User",
435
+ status = UserStatus.ACTIVE
436
+ )
437
+ }
438
+ ```
439
+
440
+ **Required tests** (minimum):
441
+ 1. Insert happy path
442
+ 2. Update modifies fields correctly
443
+ 3. byId returns entity when exists
444
+ 4. byId returns null when not exists
445
+ 5. byId returns null when soft deleted ← **Critical for soft delete verification**
446
+ 6. deleteById soft deletes entity
447
+ 7. restoreById restores soft deleted entity
448
+
449
+ **Test patterns**:
450
+ - Extend `AbstractRepositoryTest`
451
+ - Use `@MicronautTest(environments = ["test"])`
452
+ - Clean database in `@BeforeEach`
453
+ - Create dummy entity helpers with random data
454
+ - Use AssertJ assertions (`assertk.assertThat`)
455
+ - Test soft delete behavior thoroughly
456
+
457
+ ---
458
+
459
+ ## Anti-Patterns to NEVER Use
460
+
461
+ ❌ **Wrong: Using `Table` instead of `UUIDTable`**
462
+ ```kotlin
463
+ object UserTable : Table("users") // WRONG - missing UUID primary key
464
+ ```
465
+
466
+ ✓ **Correct: Use `UUIDTable`**
467
+ ```kotlin
468
+ object UserTable : UUIDTable("users") // CORRECT
469
+ ```
470
+
471
+ ---
472
+
473
+ ❌ **Wrong: Forgetting soft delete filter**
474
+ ```kotlin
475
+ fun byId(id: UUID): UserEntity? {
476
+ return transaction(db.replica) {
477
+ UserTable
478
+ .selectAll()
479
+ .where { UserTable.id eq id } // WRONG - includes soft deleted
480
+ .map { convert(it) }
481
+ .singleOrNull()
482
+ }
483
+ }
484
+ ```
485
+
486
+ ✓ **Correct: Always filter soft deletes**
487
+ ```kotlin
488
+ fun byId(id: UUID): UserEntity? {
489
+ return transaction(db.replica) {
490
+ UserTable
491
+ .selectAll()
492
+ .where { UserTable.id eq id }
493
+ .andWhere { UserTable.deletedAt.isNull() } // CORRECT
494
+ .map { convert(it) }
495
+ .singleOrNull()
496
+ }
497
+ }
498
+ ```
499
+
500
+ ---
501
+
502
+ ❌ **Wrong: Using `!!` operator**
503
+ ```kotlin
504
+ val user = repository.byId(id)!! // FORBIDDEN - will fail pre-commit hook
505
+ ```
506
+
507
+ ✓ **Correct: Use safe calls**
508
+ ```kotlin
509
+ val user = repository.byId(id)
510
+ ?: return ClientError.USER_NOT_FOUND.asException().left()
511
+ ```
512
+
513
+ ---
514
+
515
+ ❌ **Wrong: Hard delete**
516
+ ```kotlin
517
+ fun deleteById(id: UUID): Boolean {
518
+ return transaction(db.primary) {
519
+ UserTable.deleteWhere { id eq userId } > 0 // WRONG - permanent deletion
520
+ }
521
+ }
522
+ ```
523
+
524
+ ✓ **Correct: Soft delete**
525
+ ```kotlin
526
+ fun deleteById(id: UUID): UserEntity? {
527
+ return transaction(db.primary) {
528
+ UserTable.update({ id eq userId and deletedAt.isNull() }) {
529
+ it[deletedAt] = Instant.now() // CORRECT - soft delete
530
+ }
531
+ if (updated > 0) byId(id) else null
532
+ }
533
+ }
534
+ ```
535
+
536
+ ---
537
+
538
+ ## Summary
539
+
540
+ **Always follow these patterns exactly**:
541
+ 1. Table extends `UUIDTable`
542
+ 2. Entity implements `Entity<Instant>`
543
+ 3. Use `text()` not `varchar()`
544
+ 4. Filter `deletedAt.isNull()` in ALL queries
545
+ 5. Use `transaction(db.primary)` for writes
546
+ 6. Use `transaction(db.replica)` for reads
547
+ 7. Never use `!!` operator
548
+ 8. Soft delete only (set deletedAt timestamp)
549
+ 9. Return null when not found
550
+ 10. Test soft delete behavior thoroughly
551
+
552
+ **These patterns are non-negotiable and must be followed exactly.**