takt 0.48.0 → 0.50.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 (901) hide show
  1. package/README.md +15 -0
  2. package/builtins/en/config.yaml +6 -0
  3. package/builtins/en/facets/instructions/ai-antipattern-review.md +5 -0
  4. package/builtins/en/facets/instructions/exec-loop-monitor.md +15 -0
  5. package/builtins/en/facets/instructions/exec-replan.md +5 -0
  6. package/builtins/en/facets/instructions/exec-review.md +10 -0
  7. package/builtins/en/facets/instructions/exec-worker.md +5 -0
  8. package/builtins/en/facets/instructions/findings-manager.md +6 -2
  9. package/builtins/en/facets/instructions/loop-monitor-fix-replan.md +21 -0
  10. package/builtins/en/facets/instructions/loop-monitor-replan.md +10 -0
  11. package/builtins/en/facets/instructions/plan-maintenance.md +10 -7
  12. package/builtins/en/facets/instructions/plan.md +17 -8
  13. package/builtins/en/facets/instructions/review-arch.md +5 -0
  14. package/builtins/en/facets/instructions/review-coding.md +15 -6
  15. package/builtins/en/facets/instructions/review-cqrs-es.md +9 -0
  16. package/builtins/en/facets/instructions/review-implementation-semantics.md +18 -0
  17. package/builtins/en/facets/instructions/review-merge-readiness.md +14 -0
  18. package/builtins/en/facets/instructions/supervise-maintenance.md +2 -2
  19. package/builtins/en/facets/instructions/supervise.md +8 -3
  20. package/builtins/en/facets/instructions/write-tests-first.md +23 -3
  21. package/builtins/en/facets/knowledge/architecture.md +25 -0
  22. package/builtins/en/facets/knowledge/cqrs-es.md +718 -235
  23. package/builtins/en/facets/knowledge/implementation-semantics.md +104 -0
  24. package/builtins/en/facets/knowledge/takt.md +26 -2
  25. package/builtins/en/facets/knowledge/unit-testing.md +14 -0
  26. package/builtins/en/facets/output-contracts/ai-antipattern-review.md +11 -1
  27. package/builtins/en/facets/output-contracts/architecture-review.md +6 -1
  28. package/builtins/en/facets/output-contracts/coding-review.md +11 -1
  29. package/builtins/en/facets/output-contracts/exec-review-result.md +13 -0
  30. package/builtins/en/facets/output-contracts/findings-manager.md +14 -0
  31. package/builtins/en/facets/output-contracts/implementation-semantics-review-finding-contract.md +28 -0
  32. package/builtins/en/facets/output-contracts/implementation-semantics-review.md +50 -0
  33. package/builtins/en/facets/output-contracts/{pure-review-finding-contract.md → merge-readiness-review-finding-contract.md} +14 -8
  34. package/builtins/en/facets/output-contracts/{pure-review.md → merge-readiness-review.md} +17 -11
  35. package/builtins/en/facets/output-contracts/supervisor-validation.md +6 -2
  36. package/builtins/en/facets/output-contracts/test-report.md +25 -0
  37. package/builtins/en/facets/personas/exec-assistant.md +3 -0
  38. package/builtins/en/facets/personas/exec-worker.md +3 -0
  39. package/builtins/en/facets/personas/implementation-semantics-reviewer.md +27 -0
  40. package/builtins/en/facets/personas/merge-readiness-reviewer.md +28 -0
  41. package/builtins/en/facets/personas/supervisor.md +1 -1
  42. package/builtins/en/facets/policies/ai-antipattern.md +18 -0
  43. package/builtins/en/facets/policies/existing-system-respect.md +13 -0
  44. package/builtins/en/facets/policies/review.md +17 -0
  45. package/builtins/en/facets/policies/testing.md +14 -0
  46. package/builtins/en/workflow-categories.yaml +13 -1
  47. package/builtins/en/workflows/audit-architecture-backend.yaml +1 -0
  48. package/builtins/en/workflows/audit-architecture-dual.yaml +1 -0
  49. package/builtins/en/workflows/audit-architecture-frontend.yaml +1 -0
  50. package/builtins/en/workflows/audit-architecture.yaml +1 -0
  51. package/builtins/en/workflows/audit-e2e.yaml +1 -0
  52. package/builtins/en/workflows/audit-security.yaml +1 -0
  53. package/builtins/en/workflows/audit-unit.yaml +1 -0
  54. package/builtins/en/workflows/backend-cqrs-for-local-llm.yaml +288 -0
  55. package/builtins/en/workflows/backend-cqrs-mini.yaml +1 -0
  56. package/builtins/en/workflows/backend-cqrs.yaml +28 -84
  57. package/builtins/en/workflows/backend-for-local-llm.yaml +284 -0
  58. package/builtins/en/workflows/backend-maintenance.yaml +32 -89
  59. package/builtins/en/workflows/backend-mini.yaml +1 -0
  60. package/builtins/en/workflows/backend.yaml +25 -83
  61. package/builtins/en/workflows/{takt-default-with-fc.yaml → cli.yaml} +16 -4
  62. package/builtins/en/workflows/deep-research.yaml +1 -0
  63. package/builtins/en/workflows/default-high.yaml +3 -32
  64. package/builtins/en/workflows/default-peer-review.yaml +16 -52
  65. package/builtins/en/workflows/default.yaml +2 -1
  66. package/builtins/en/workflows/draft.yaml +1 -1
  67. package/builtins/en/workflows/dual-cqrs-mini.yaml +1 -0
  68. package/builtins/en/workflows/dual-cqrs.yaml +28 -87
  69. package/builtins/en/workflows/dual-for-local-llm.yaml +312 -0
  70. package/builtins/en/workflows/dual-mini.yaml +1 -0
  71. package/builtins/en/workflows/dual.yaml +29 -57
  72. package/builtins/en/workflows/frontend-for-local-llm.yaml +289 -0
  73. package/builtins/en/workflows/frontend-maintenance.yaml +34 -91
  74. package/builtins/en/workflows/frontend-mini.yaml +1 -0
  75. package/builtins/en/workflows/frontend.yaml +27 -85
  76. package/builtins/en/workflows/merge-readiness-dual-final-gate.yaml +92 -0
  77. package/builtins/en/workflows/merge-readiness-final-gate.yaml +79 -0
  78. package/builtins/en/workflows/{peer-review-with-fc.yaml → peer-review-for-local-llm.yaml} +49 -38
  79. package/builtins/en/workflows/peer-review.yaml +26 -21
  80. package/builtins/en/workflows/research.yaml +1 -0
  81. package/builtins/en/workflows/review-backend-cqrs.yaml +63 -35
  82. package/builtins/en/workflows/review-backend.yaml +63 -35
  83. package/builtins/en/workflows/review-default.yaml +138 -107
  84. package/builtins/en/workflows/review-dual-cqrs.yaml +63 -35
  85. package/builtins/en/workflows/review-dual.yaml +63 -35
  86. package/builtins/en/workflows/review-fix-backend-cqrs.yaml +27 -87
  87. package/builtins/en/workflows/review-fix-backend.yaml +25 -86
  88. package/builtins/en/workflows/review-fix-default.yaml +17 -87
  89. package/builtins/en/workflows/review-fix-dual-cqrs.yaml +29 -90
  90. package/builtins/en/workflows/review-fix-dual.yaml +27 -89
  91. package/builtins/en/workflows/review-fix-frontend.yaml +25 -87
  92. package/builtins/en/workflows/review-fix-takt-default.yaml +67 -362
  93. package/builtins/en/workflows/review-frontend.yaml +63 -35
  94. package/builtins/en/workflows/review-takt-default.yaml +140 -109
  95. package/builtins/en/workflows/takt-default-for-local-llm.yaml +283 -0
  96. package/builtins/en/workflows/takt-default-refresh-all.yaml +20 -58
  97. package/builtins/en/workflows/takt-default-refresh-fast.yaml +19 -56
  98. package/builtins/en/workflows/takt-default.yaml +3 -25
  99. package/builtins/en/workflows/terraform.yaml +81 -44
  100. package/builtins/exec/presets/backend.yaml +31 -0
  101. package/builtins/exec/presets/dual.yaml +34 -0
  102. package/builtins/exec/presets/frontend.yaml +31 -0
  103. package/builtins/exec/presets/research.yaml +45 -0
  104. package/builtins/ja/config.yaml +6 -0
  105. package/builtins/ja/facets/instructions/ai-antipattern-review.md +5 -0
  106. package/builtins/ja/facets/instructions/exec-loop-monitor.md +15 -0
  107. package/builtins/ja/facets/instructions/exec-replan.md +5 -0
  108. package/builtins/ja/facets/instructions/exec-review.md +10 -0
  109. package/builtins/ja/facets/instructions/exec-worker.md +5 -0
  110. package/builtins/ja/facets/instructions/findings-manager.md +6 -2
  111. package/builtins/ja/facets/instructions/implement-maintenance.md +4 -15
  112. package/builtins/ja/facets/instructions/implement.md +2 -14
  113. package/builtins/ja/facets/instructions/loop-monitor-fix-replan.md +21 -0
  114. package/builtins/ja/facets/instructions/loop-monitor-replan.md +10 -0
  115. package/builtins/ja/facets/instructions/plan-maintenance.md +10 -7
  116. package/builtins/ja/facets/instructions/plan.md +9 -6
  117. package/builtins/ja/facets/instructions/review-arch.md +5 -0
  118. package/builtins/ja/facets/instructions/review-coding.md +15 -6
  119. package/builtins/ja/facets/instructions/review-cqrs-es.md +9 -0
  120. package/builtins/ja/facets/instructions/review-implementation-semantics.md +17 -0
  121. package/builtins/ja/facets/instructions/review-merge-readiness.md +14 -0
  122. package/builtins/ja/facets/instructions/supervise-maintenance.md +2 -2
  123. package/builtins/ja/facets/instructions/supervise.md +8 -3
  124. package/builtins/ja/facets/instructions/write-tests-first.md +23 -3
  125. package/builtins/ja/facets/knowledge/architecture.md +25 -0
  126. package/builtins/ja/facets/knowledge/cqrs-es.md +211 -16
  127. package/builtins/ja/facets/knowledge/implementation-semantics.md +104 -0
  128. package/builtins/ja/facets/knowledge/takt.md +26 -2
  129. package/builtins/ja/facets/knowledge/unit-testing.md +14 -0
  130. package/builtins/ja/facets/output-contracts/ai-antipattern-review.md +11 -1
  131. package/builtins/ja/facets/output-contracts/architecture-review.md +6 -1
  132. package/builtins/ja/facets/output-contracts/coding-review.md +11 -1
  133. package/builtins/ja/facets/output-contracts/exec-review-result.md +13 -0
  134. package/builtins/ja/facets/output-contracts/findings-manager.md +14 -0
  135. package/builtins/ja/facets/output-contracts/implementation-semantics-review-finding-contract.md +24 -0
  136. package/builtins/ja/facets/output-contracts/implementation-semantics-review.md +50 -0
  137. package/builtins/ja/facets/output-contracts/{pure-review-finding-contract.md → merge-readiness-review-finding-contract.md} +14 -8
  138. package/builtins/ja/facets/output-contracts/{pure-review.md → merge-readiness-review.md} +17 -11
  139. package/builtins/ja/facets/output-contracts/supervisor-validation.md +6 -2
  140. package/builtins/ja/facets/output-contracts/test-report.md +25 -0
  141. package/builtins/ja/facets/partials/instructions/implement-common.md +13 -0
  142. package/builtins/ja/facets/personas/exec-assistant.md +3 -0
  143. package/builtins/ja/facets/personas/exec-worker.md +3 -0
  144. package/builtins/ja/facets/personas/implementation-semantics-reviewer.md +27 -0
  145. package/builtins/ja/facets/personas/merge-readiness-reviewer.md +28 -0
  146. package/builtins/ja/facets/personas/supervisor.md +1 -1
  147. package/builtins/ja/facets/policies/ai-antipattern.md +18 -0
  148. package/builtins/ja/facets/policies/coding.md +1 -0
  149. package/builtins/ja/facets/policies/existing-system-respect.md +13 -0
  150. package/builtins/ja/facets/policies/review.md +18 -0
  151. package/builtins/ja/facets/policies/testing.md +19 -2
  152. package/builtins/ja/workflow-categories.yaml +13 -1
  153. package/builtins/ja/workflows/audit-architecture-backend.yaml +1 -0
  154. package/builtins/ja/workflows/audit-architecture-dual.yaml +1 -0
  155. package/builtins/ja/workflows/audit-architecture-frontend.yaml +1 -0
  156. package/builtins/ja/workflows/audit-architecture.yaml +1 -0
  157. package/builtins/ja/workflows/audit-e2e.yaml +1 -0
  158. package/builtins/ja/workflows/audit-security.yaml +1 -0
  159. package/builtins/ja/workflows/audit-unit.yaml +1 -0
  160. package/builtins/ja/workflows/backend-cqrs-for-local-llm.yaml +287 -0
  161. package/builtins/ja/workflows/backend-cqrs-mini.yaml +1 -0
  162. package/builtins/ja/workflows/backend-cqrs.yaml +28 -84
  163. package/builtins/ja/workflows/backend-for-local-llm.yaml +283 -0
  164. package/builtins/ja/workflows/backend-maintenance.yaml +32 -89
  165. package/builtins/ja/workflows/backend-mini.yaml +1 -0
  166. package/builtins/ja/workflows/backend.yaml +25 -83
  167. package/builtins/ja/workflows/{takt-default-with-fc.yaml → cli.yaml} +16 -4
  168. package/builtins/ja/workflows/deep-research.yaml +1 -0
  169. package/builtins/ja/workflows/default-high.yaml +3 -32
  170. package/builtins/ja/workflows/default-peer-review.yaml +16 -52
  171. package/builtins/ja/workflows/default.yaml +2 -1
  172. package/builtins/ja/workflows/draft.yaml +1 -1
  173. package/builtins/ja/workflows/dual-cqrs-mini.yaml +1 -0
  174. package/builtins/ja/workflows/dual-cqrs.yaml +28 -87
  175. package/builtins/ja/workflows/dual-for-local-llm.yaml +311 -0
  176. package/builtins/ja/workflows/dual-mini.yaml +1 -0
  177. package/builtins/ja/workflows/dual.yaml +29 -57
  178. package/builtins/ja/workflows/frontend-for-local-llm.yaml +288 -0
  179. package/builtins/ja/workflows/frontend-maintenance.yaml +34 -91
  180. package/builtins/ja/workflows/frontend-mini.yaml +1 -0
  181. package/builtins/ja/workflows/frontend.yaml +27 -85
  182. package/builtins/ja/workflows/merge-readiness-dual-final-gate.yaml +92 -0
  183. package/builtins/ja/workflows/merge-readiness-final-gate.yaml +79 -0
  184. package/builtins/ja/workflows/{peer-review-with-fc.yaml → peer-review-for-local-llm.yaml} +49 -38
  185. package/builtins/ja/workflows/peer-review.yaml +26 -21
  186. package/builtins/ja/workflows/research.yaml +1 -0
  187. package/builtins/ja/workflows/review-backend-cqrs.yaml +63 -35
  188. package/builtins/ja/workflows/review-backend.yaml +63 -35
  189. package/builtins/ja/workflows/review-default.yaml +111 -79
  190. package/builtins/ja/workflows/review-dual-cqrs.yaml +63 -35
  191. package/builtins/ja/workflows/review-dual.yaml +63 -35
  192. package/builtins/ja/workflows/review-fix-backend-cqrs.yaml +27 -87
  193. package/builtins/ja/workflows/review-fix-backend.yaml +25 -86
  194. package/builtins/ja/workflows/review-fix-default.yaml +17 -87
  195. package/builtins/ja/workflows/review-fix-dual-cqrs.yaml +29 -90
  196. package/builtins/ja/workflows/review-fix-dual.yaml +27 -89
  197. package/builtins/ja/workflows/review-fix-frontend.yaml +25 -87
  198. package/builtins/ja/workflows/review-fix-takt-default.yaml +67 -362
  199. package/builtins/ja/workflows/review-frontend.yaml +63 -35
  200. package/builtins/ja/workflows/review-takt-default.yaml +112 -80
  201. package/builtins/ja/workflows/takt-default-for-local-llm.yaml +282 -0
  202. package/builtins/ja/workflows/takt-default-refresh-all.yaml +20 -58
  203. package/builtins/ja/workflows/takt-default-refresh-fast.yaml +19 -56
  204. package/builtins/ja/workflows/takt-default.yaml +3 -25
  205. package/builtins/ja/workflows/terraform.yaml +81 -44
  206. package/builtins/skill/SKILL.md +1 -1
  207. package/builtins/skill/references/engine.md +1 -15
  208. package/builtins/skill-codex/SKILL.md +1 -1
  209. package/builtins/skill-codex/references/engine.md +0 -2
  210. package/dist/agents/decompose-task-usecase.d.ts +1 -1
  211. package/dist/agents/decompose-task-usecase.d.ts.map +1 -1
  212. package/dist/agents/decompose-task-usecase.js +2 -4
  213. package/dist/agents/decompose-task-usecase.js.map +1 -1
  214. package/dist/agents/judge-utils.d.ts.map +1 -1
  215. package/dist/agents/judge-utils.js +4 -3
  216. package/dist/agents/judge-utils.js.map +1 -1
  217. package/dist/agents/runner.d.ts.map +1 -1
  218. package/dist/agents/runner.js +7 -6
  219. package/dist/agents/runner.js.map +1 -1
  220. package/dist/agents/structured-caller/prompt-based-structured-caller.d.ts.map +1 -1
  221. package/dist/agents/structured-caller/prompt-based-structured-caller.js +2 -3
  222. package/dist/agents/structured-caller/prompt-based-structured-caller.js.map +1 -1
  223. package/dist/agents/structured-caller/shared.d.ts.map +1 -1
  224. package/dist/agents/structured-caller/shared.js +2 -9
  225. package/dist/agents/structured-caller/shared.js.map +1 -1
  226. package/dist/app/acp/agent.d.ts +28 -0
  227. package/dist/app/acp/agent.d.ts.map +1 -0
  228. package/dist/app/acp/agent.js +279 -0
  229. package/dist/app/acp/agent.js.map +1 -0
  230. package/dist/app/acp/confirmationBridge.d.ts +5 -0
  231. package/dist/app/acp/confirmationBridge.d.ts.map +1 -0
  232. package/dist/app/acp/confirmationBridge.js +194 -0
  233. package/dist/app/acp/confirmationBridge.js.map +1 -0
  234. package/dist/app/acp/conversationFactory.d.ts +4 -0
  235. package/dist/app/acp/conversationFactory.d.ts.map +1 -0
  236. package/dist/app/acp/conversationFactory.js +33 -0
  237. package/dist/app/acp/conversationFactory.js.map +1 -0
  238. package/dist/app/acp/enqueue.d.ts +33 -0
  239. package/dist/app/acp/enqueue.d.ts.map +1 -0
  240. package/dist/app/acp/enqueue.js +45 -0
  241. package/dist/app/acp/enqueue.js.map +1 -0
  242. package/dist/app/acp/index.d.ts +7 -0
  243. package/dist/app/acp/index.d.ts.map +1 -0
  244. package/dist/app/acp/index.js +125 -0
  245. package/dist/app/acp/index.js.map +1 -0
  246. package/dist/app/acp/intent.d.ts +10 -0
  247. package/dist/app/acp/intent.d.ts.map +1 -0
  248. package/dist/app/acp/intent.js +210 -0
  249. package/dist/app/acp/intent.js.map +1 -0
  250. package/dist/app/acp/mcpServers.d.ts +4 -0
  251. package/dist/app/acp/mcpServers.d.ts.map +1 -0
  252. package/dist/app/acp/mcpServers.js +51 -0
  253. package/dist/app/acp/mcpServers.js.map +1 -0
  254. package/dist/app/acp/promptContent.d.ts +3 -0
  255. package/dist/app/acp/promptContent.d.ts.map +1 -0
  256. package/dist/app/acp/promptContent.js +23 -0
  257. package/dist/app/acp/promptContent.js.map +1 -0
  258. package/dist/app/acp/sessionStore.d.ts +19 -0
  259. package/dist/app/acp/sessionStore.d.ts.map +1 -0
  260. package/dist/app/acp/sessionStore.js +50 -0
  261. package/dist/app/acp/sessionStore.js.map +1 -0
  262. package/dist/app/acp/sessionUpdates.d.ts +8 -0
  263. package/dist/app/acp/sessionUpdates.d.ts.map +1 -0
  264. package/dist/app/acp/sessionUpdates.js +108 -0
  265. package/dist/app/acp/sessionUpdates.js.map +1 -0
  266. package/dist/app/acp/taskContext.d.ts +17 -0
  267. package/dist/app/acp/taskContext.d.ts.map +1 -0
  268. package/dist/app/acp/taskContext.js +81 -0
  269. package/dist/app/acp/taskContext.js.map +1 -0
  270. package/dist/app/acp/types.d.ts +33 -0
  271. package/dist/app/acp/types.d.ts.map +1 -0
  272. package/dist/app/acp/types.js +2 -0
  273. package/dist/app/acp/types.js.map +1 -0
  274. package/dist/app/cli/commands.js +13 -0
  275. package/dist/app/cli/commands.js.map +1 -1
  276. package/dist/app/cli/program.d.ts.map +1 -1
  277. package/dist/app/cli/program.js +2 -3
  278. package/dist/app/cli/program.js.map +1 -1
  279. package/dist/app/mcp/index.d.ts +3 -0
  280. package/dist/app/mcp/index.d.ts.map +1 -0
  281. package/dist/app/mcp/index.js +16 -0
  282. package/dist/app/mcp/index.js.map +1 -0
  283. package/dist/app/mcp/server.d.ts +2 -0
  284. package/dist/app/mcp/server.d.ts.map +1 -0
  285. package/dist/app/mcp/server.js +2 -0
  286. package/dist/app/mcp/server.js.map +1 -0
  287. package/dist/core/config/provider-resolution.d.ts +1 -0
  288. package/dist/core/config/provider-resolution.d.ts.map +1 -1
  289. package/dist/core/config/provider-resolution.js +19 -0
  290. package/dist/core/config/provider-resolution.js.map +1 -1
  291. package/dist/core/models/finding-schemas.d.ts +129 -10
  292. package/dist/core/models/finding-schemas.d.ts.map +1 -1
  293. package/dist/core/models/finding-schemas.js +79 -9
  294. package/dist/core/models/finding-schemas.js.map +1 -1
  295. package/dist/core/models/finding-types.d.ts +39 -2
  296. package/dist/core/models/finding-types.d.ts.map +1 -1
  297. package/dist/core/models/finding-types.js +3 -2
  298. package/dist/core/models/finding-types.js.map +1 -1
  299. package/dist/core/models/workflow-condition-expression.d.ts +17 -0
  300. package/dist/core/models/workflow-condition-expression.d.ts.map +1 -1
  301. package/dist/core/models/workflow-condition-expression.js +85 -2
  302. package/dist/core/models/workflow-condition-expression.js.map +1 -1
  303. package/dist/core/models/workflow-schemas.d.ts +222 -22
  304. package/dist/core/models/workflow-schemas.d.ts.map +1 -1
  305. package/dist/core/models/workflow-schemas.js +38 -4
  306. package/dist/core/models/workflow-schemas.js.map +1 -1
  307. package/dist/core/models/workflow-types.d.ts +14 -0
  308. package/dist/core/models/workflow-types.d.ts.map +1 -1
  309. package/dist/core/runtime/runtime-environment.d.ts.map +1 -1
  310. package/dist/core/runtime/runtime-environment.js +19 -0
  311. package/dist/core/runtime/runtime-environment.js.map +1 -1
  312. package/dist/core/workflow/engine/LoopMonitorJudgeRunner.d.ts +2 -2
  313. package/dist/core/workflow/engine/LoopMonitorJudgeRunner.d.ts.map +1 -1
  314. package/dist/core/workflow/engine/LoopMonitorJudgeRunner.js +8 -7
  315. package/dist/core/workflow/engine/LoopMonitorJudgeRunner.js.map +1 -1
  316. package/dist/core/workflow/engine/OptionsBuilder.d.ts +15 -5
  317. package/dist/core/workflow/engine/OptionsBuilder.d.ts.map +1 -1
  318. package/dist/core/workflow/engine/OptionsBuilder.js +93 -16
  319. package/dist/core/workflow/engine/OptionsBuilder.js.map +1 -1
  320. package/dist/core/workflow/engine/ParallelRunner.d.ts +2 -0
  321. package/dist/core/workflow/engine/ParallelRunner.d.ts.map +1 -1
  322. package/dist/core/workflow/engine/ParallelRunner.js +131 -23
  323. package/dist/core/workflow/engine/ParallelRunner.js.map +1 -1
  324. package/dist/core/workflow/engine/StepExecutor.d.ts +10 -0
  325. package/dist/core/workflow/engine/StepExecutor.d.ts.map +1 -1
  326. package/dist/core/workflow/engine/StepExecutor.js +90 -38
  327. package/dist/core/workflow/engine/StepExecutor.js.map +1 -1
  328. package/dist/core/workflow/engine/TeamLeaderRunner.d.ts.map +1 -1
  329. package/dist/core/workflow/engine/TeamLeaderRunner.js +3 -0
  330. package/dist/core/workflow/engine/TeamLeaderRunner.js.map +1 -1
  331. package/dist/core/workflow/engine/WorkflowEngine.d.ts.map +1 -1
  332. package/dist/core/workflow/engine/WorkflowEngine.js +8 -3
  333. package/dist/core/workflow/engine/WorkflowEngine.js.map +1 -1
  334. package/dist/core/workflow/engine/WorkflowEngineSetup.d.ts.map +1 -1
  335. package/dist/core/workflow/engine/WorkflowEngineSetup.js +5 -3
  336. package/dist/core/workflow/engine/WorkflowEngineSetup.js.map +1 -1
  337. package/dist/core/workflow/engine/WorkflowRunLoop.d.ts.map +1 -1
  338. package/dist/core/workflow/engine/WorkflowRunLoop.js +31 -0
  339. package/dist/core/workflow/engine/WorkflowRunLoop.js.map +1 -1
  340. package/dist/core/workflow/engine/WorkflowValidator.d.ts.map +1 -1
  341. package/dist/core/workflow/engine/WorkflowValidator.js +25 -12
  342. package/dist/core/workflow/engine/WorkflowValidator.js.map +1 -1
  343. package/dist/core/workflow/engine/engine-provider-options.d.ts +2 -1
  344. package/dist/core/workflow/engine/engine-provider-options.d.ts.map +1 -1
  345. package/dist/core/workflow/engine/engine-provider-options.js +30 -6
  346. package/dist/core/workflow/engine/engine-provider-options.js.map +1 -1
  347. package/dist/core/workflow/engine/team-leader-part-runner.d.ts.map +1 -1
  348. package/dist/core/workflow/engine/team-leader-part-runner.js +3 -1
  349. package/dist/core/workflow/engine/team-leader-part-runner.js.map +1 -1
  350. package/dist/core/workflow/evaluation/RuleEvaluator.d.ts +0 -1
  351. package/dist/core/workflow/evaluation/RuleEvaluator.d.ts.map +1 -1
  352. package/dist/core/workflow/evaluation/RuleEvaluator.js +22 -34
  353. package/dist/core/workflow/evaluation/RuleEvaluator.js.map +1 -1
  354. package/dist/core/workflow/evaluation/rule-utils.d.ts +33 -1
  355. package/dist/core/workflow/evaluation/rule-utils.d.ts.map +1 -1
  356. package/dist/core/workflow/evaluation/rule-utils.js +77 -5
  357. package/dist/core/workflow/evaluation/rule-utils.js.map +1 -1
  358. package/dist/core/workflow/evaluation/when-evaluator.d.ts +1 -0
  359. package/dist/core/workflow/evaluation/when-evaluator.d.ts.map +1 -1
  360. package/dist/core/workflow/evaluation/when-evaluator.js +18 -40
  361. package/dist/core/workflow/evaluation/when-evaluator.js.map +1 -1
  362. package/dist/core/workflow/findings/context.d.ts +4 -0
  363. package/dist/core/workflow/findings/context.d.ts.map +1 -1
  364. package/dist/core/workflow/findings/context.js +27 -0
  365. package/dist/core/workflow/findings/context.js.map +1 -1
  366. package/dist/core/workflow/findings/manager-output-validation.d.ts +2 -0
  367. package/dist/core/workflow/findings/manager-output-validation.d.ts.map +1 -1
  368. package/dist/core/workflow/findings/manager-output-validation.js +145 -8
  369. package/dist/core/workflow/findings/manager-output-validation.js.map +1 -1
  370. package/dist/core/workflow/findings/manager-runner.d.ts +13 -3
  371. package/dist/core/workflow/findings/manager-runner.d.ts.map +1 -1
  372. package/dist/core/workflow/findings/manager-runner.js +28 -27
  373. package/dist/core/workflow/findings/manager-runner.js.map +1 -1
  374. package/dist/core/workflow/findings/reconciler.d.ts +1 -0
  375. package/dist/core/workflow/findings/reconciler.d.ts.map +1 -1
  376. package/dist/core/workflow/findings/reconciler.js +90 -4
  377. package/dist/core/workflow/findings/reconciler.js.map +1 -1
  378. package/dist/core/workflow/instruction/InstructionBuilder.d.ts.map +1 -1
  379. package/dist/core/workflow/instruction/InstructionBuilder.js +18 -2
  380. package/dist/core/workflow/instruction/InstructionBuilder.js.map +1 -1
  381. package/dist/core/workflow/instruction/instruction-context.d.ts +4 -0
  382. package/dist/core/workflow/instruction/instruction-context.d.ts.map +1 -1
  383. package/dist/core/workflow/instruction/instruction-context.js.map +1 -1
  384. package/dist/core/workflow/instruction/status-rules.d.ts.map +1 -1
  385. package/dist/core/workflow/instruction/status-rules.js +7 -2
  386. package/dist/core/workflow/instruction/status-rules.js.map +1 -1
  387. package/dist/core/workflow/phase-runner.d.ts +17 -10
  388. package/dist/core/workflow/phase-runner.d.ts.map +1 -1
  389. package/dist/core/workflow/phase-runner.js +1 -1
  390. package/dist/core/workflow/phase-runner.js.map +1 -1
  391. package/dist/core/workflow/provider-model-requirements.d.ts +6 -0
  392. package/dist/core/workflow/provider-model-requirements.d.ts.map +1 -0
  393. package/dist/core/workflow/provider-model-requirements.js +15 -0
  394. package/dist/core/workflow/provider-model-requirements.js.map +1 -0
  395. package/dist/core/workflow/provider-resolution.d.ts +4 -6
  396. package/dist/core/workflow/provider-resolution.d.ts.map +1 -1
  397. package/dist/core/workflow/provider-resolution.js +19 -11
  398. package/dist/core/workflow/provider-resolution.js.map +1 -1
  399. package/dist/core/workflow/report-phase-runner.d.ts +5 -2
  400. package/dist/core/workflow/report-phase-runner.d.ts.map +1 -1
  401. package/dist/core/workflow/report-phase-runner.js +135 -43
  402. package/dist/core/workflow/report-phase-runner.js.map +1 -1
  403. package/dist/core/workflow/session-key.d.ts +4 -1
  404. package/dist/core/workflow/session-key.d.ts.map +1 -1
  405. package/dist/core/workflow/session-key.js +5 -2
  406. package/dist/core/workflow/session-key.js.map +1 -1
  407. package/dist/core/workflow/status-judgment-phase.d.ts +2 -2
  408. package/dist/core/workflow/status-judgment-phase.d.ts.map +1 -1
  409. package/dist/core/workflow/status-judgment-phase.js +1 -7
  410. package/dist/core/workflow/status-judgment-phase.js.map +1 -1
  411. package/dist/core/workflow/system/system-step-effect-runner.js +2 -2
  412. package/dist/core/workflow/system/system-step-effect-runner.js.map +1 -1
  413. package/dist/core/workflow/system/system-step-services.d.ts +101 -0
  414. package/dist/core/workflow/system/system-step-services.d.ts.map +1 -1
  415. package/dist/core/workflow/types.d.ts +7 -3
  416. package/dist/core/workflow/types.d.ts.map +1 -1
  417. package/dist/features/analytics/events.d.ts +2 -1
  418. package/dist/features/analytics/events.d.ts.map +1 -1
  419. package/dist/features/catalog/catalogFacets.d.ts +11 -1
  420. package/dist/features/catalog/catalogFacets.d.ts.map +1 -1
  421. package/dist/features/catalog/catalogFacets.js +36 -10
  422. package/dist/features/catalog/catalogFacets.js.map +1 -1
  423. package/dist/features/exec/assistantSession.d.ts +18 -0
  424. package/dist/features/exec/assistantSession.d.ts.map +1 -0
  425. package/dist/features/exec/assistantSession.js +51 -0
  426. package/dist/features/exec/assistantSession.js.map +1 -0
  427. package/dist/features/exec/command.d.ts +9 -0
  428. package/dist/features/exec/command.d.ts.map +1 -0
  429. package/dist/features/exec/command.js +143 -0
  430. package/dist/features/exec/command.js.map +1 -0
  431. package/dist/features/exec/commandAvailability.d.ts +4 -0
  432. package/dist/features/exec/commandAvailability.d.ts.map +1 -0
  433. package/dist/features/exec/commandAvailability.js +10 -0
  434. package/dist/features/exec/commandAvailability.js.map +1 -0
  435. package/dist/features/exec/configOps.d.ts +13 -0
  436. package/dist/features/exec/configOps.d.ts.map +1 -0
  437. package/dist/features/exec/configOps.js +129 -0
  438. package/dist/features/exec/configOps.js.map +1 -0
  439. package/dist/features/exec/configValidation.d.ts +15 -0
  440. package/dist/features/exec/configValidation.d.ts.map +1 -0
  441. package/dist/features/exec/configValidation.js +146 -0
  442. package/dist/features/exec/configValidation.js.map +1 -0
  443. package/dist/features/exec/defaults.d.ts +3 -0
  444. package/dist/features/exec/defaults.d.ts.map +1 -0
  445. package/dist/features/exec/defaults.js +30 -0
  446. package/dist/features/exec/defaults.js.map +1 -0
  447. package/dist/features/exec/facetEditor.d.ts +5 -0
  448. package/dist/features/exec/facetEditor.d.ts.map +1 -0
  449. package/dist/features/exec/facetEditor.js +293 -0
  450. package/dist/features/exec/facetEditor.js.map +1 -0
  451. package/dist/features/exec/index.d.ts +2 -0
  452. package/dist/features/exec/index.d.ts.map +1 -0
  453. package/dist/features/exec/index.js +2 -0
  454. package/dist/features/exec/index.js.map +1 -0
  455. package/dist/features/exec/labels.d.ts +11 -0
  456. package/dist/features/exec/labels.d.ts.map +1 -0
  457. package/dist/features/exec/labels.js +20 -0
  458. package/dist/features/exec/labels.js.map +1 -0
  459. package/dist/features/exec/presetSelection.d.ts +3 -0
  460. package/dist/features/exec/presetSelection.d.ts.map +1 -0
  461. package/dist/features/exec/presetSelection.js +16 -0
  462. package/dist/features/exec/presetSelection.js.map +1 -0
  463. package/dist/features/exec/presetSetup.d.ts +7 -0
  464. package/dist/features/exec/presetSetup.d.ts.map +1 -0
  465. package/dist/features/exec/presetSetup.js +120 -0
  466. package/dist/features/exec/presetSetup.js.map +1 -0
  467. package/dist/features/exec/presetStore.d.ts +25 -0
  468. package/dist/features/exec/presetStore.d.ts.map +1 -0
  469. package/dist/features/exec/presetStore.js +326 -0
  470. package/dist/features/exec/presetStore.js.map +1 -0
  471. package/dist/features/exec/projectLocalFiles.d.ts +9 -0
  472. package/dist/features/exec/projectLocalFiles.d.ts.map +1 -0
  473. package/dist/features/exec/projectLocalFiles.js +111 -0
  474. package/dist/features/exec/projectLocalFiles.js.map +1 -0
  475. package/dist/features/exec/promptUtils.d.ts +8 -0
  476. package/dist/features/exec/promptUtils.d.ts.map +1 -0
  477. package/dist/features/exec/promptUtils.js +32 -0
  478. package/dist/features/exec/promptUtils.js.map +1 -0
  479. package/dist/features/exec/runtimeConfig.d.ts +13 -0
  480. package/dist/features/exec/runtimeConfig.d.ts.map +1 -0
  481. package/dist/features/exec/runtimeConfig.js +59 -0
  482. package/dist/features/exec/runtimeConfig.js.map +1 -0
  483. package/dist/features/exec/setupMenu.d.ts +5 -0
  484. package/dist/features/exec/setupMenu.d.ts.map +1 -0
  485. package/dist/features/exec/setupMenu.js +431 -0
  486. package/dist/features/exec/setupMenu.js.map +1 -0
  487. package/dist/features/exec/types.d.ts +52 -0
  488. package/dist/features/exec/types.d.ts.map +1 -0
  489. package/dist/features/exec/types.js +2 -0
  490. package/dist/features/exec/types.js.map +1 -0
  491. package/dist/features/exec/workflowRunner.d.ts +9 -0
  492. package/dist/features/exec/workflowRunner.d.ts.map +1 -0
  493. package/dist/features/exec/workflowRunner.js +96 -0
  494. package/dist/features/exec/workflowRunner.js.map +1 -0
  495. package/dist/features/exec/workflowTemplate.d.ts +9 -0
  496. package/dist/features/exec/workflowTemplate.d.ts.map +1 -0
  497. package/dist/features/exec/workflowTemplate.js +159 -0
  498. package/dist/features/exec/workflowTemplate.js.map +1 -0
  499. package/dist/features/interactive/aiCaller.d.ts +5 -0
  500. package/dist/features/interactive/aiCaller.d.ts.map +1 -1
  501. package/dist/features/interactive/aiCaller.js +35 -10
  502. package/dist/features/interactive/aiCaller.js.map +1 -1
  503. package/dist/features/interactive/commandMatcher.d.ts +2 -1
  504. package/dist/features/interactive/commandMatcher.d.ts.map +1 -1
  505. package/dist/features/interactive/commandMatcher.js +13 -2
  506. package/dist/features/interactive/commandMatcher.js.map +1 -1
  507. package/dist/features/interactive/conversationSession.d.ts +44 -0
  508. package/dist/features/interactive/conversationSession.d.ts.map +1 -0
  509. package/dist/features/interactive/conversationSession.js +133 -0
  510. package/dist/features/interactive/conversationSession.js.map +1 -0
  511. package/dist/features/interactive/index.d.ts +1 -0
  512. package/dist/features/interactive/index.d.ts.map +1 -1
  513. package/dist/features/interactive/index.js +1 -0
  514. package/dist/features/interactive/index.js.map +1 -1
  515. package/dist/features/interactive/interactive-summary.d.ts.map +1 -1
  516. package/dist/features/interactive/interactive-summary.js +45 -23
  517. package/dist/features/interactive/interactive-summary.js.map +1 -1
  518. package/dist/features/interactive/interactive.d.ts +5 -4
  519. package/dist/features/interactive/interactive.d.ts.map +1 -1
  520. package/dist/features/interactive/interactive.js +12 -6
  521. package/dist/features/interactive/interactive.js.map +1 -1
  522. package/dist/features/interactive/interactiveApplication.d.ts +5 -0
  523. package/dist/features/interactive/interactiveApplication.d.ts.map +1 -0
  524. package/dist/features/interactive/interactiveApplication.js +10 -0
  525. package/dist/features/interactive/interactiveApplication.js.map +1 -0
  526. package/dist/features/interactive/promptSections.d.ts.map +1 -1
  527. package/dist/features/interactive/promptSections.js +3 -6
  528. package/dist/features/interactive/promptSections.js.map +1 -1
  529. package/dist/features/interactive/retryMode.d.ts.map +1 -1
  530. package/dist/features/interactive/retryMode.js +3 -3
  531. package/dist/features/interactive/retryMode.js.map +1 -1
  532. package/dist/features/interactive/runSessionReader.d.ts +4 -37
  533. package/dist/features/interactive/runSessionReader.d.ts.map +1 -1
  534. package/dist/features/interactive/runSessionReader.js +130 -49
  535. package/dist/features/interactive/runSessionReader.js.map +1 -1
  536. package/dist/features/interactive/slashCommandRegistry.d.ts +2 -0
  537. package/dist/features/interactive/slashCommandRegistry.d.ts.map +1 -1
  538. package/dist/features/interactive/slashCommandRegistry.js +5 -0
  539. package/dist/features/interactive/slashCommandRegistry.js.map +1 -1
  540. package/dist/features/mcp/operations.d.ts +25 -0
  541. package/dist/features/mcp/operations.d.ts.map +1 -0
  542. package/dist/features/mcp/operations.js +150 -0
  543. package/dist/features/mcp/operations.js.map +1 -0
  544. package/dist/features/mcp/schemas.d.ts +50 -0
  545. package/dist/features/mcp/schemas.d.ts.map +1 -0
  546. package/dist/features/mcp/schemas.js +59 -0
  547. package/dist/features/mcp/schemas.js.map +1 -0
  548. package/dist/features/mcp/server.d.ts +7 -0
  549. package/dist/features/mcp/server.d.ts.map +1 -0
  550. package/dist/features/mcp/server.js +36 -0
  551. package/dist/features/mcp/server.js.map +1 -0
  552. package/dist/features/prompt/preview.d.ts.map +1 -1
  553. package/dist/features/prompt/preview.js +58 -32
  554. package/dist/features/prompt/preview.js.map +1 -1
  555. package/dist/features/tasks/add/index.d.ts +8 -20
  556. package/dist/features/tasks/add/index.d.ts.map +1 -1
  557. package/dist/features/tasks/add/index.js +42 -61
  558. package/dist/features/tasks/add/index.js.map +1 -1
  559. package/dist/features/tasks/attachments.d.ts +2 -5
  560. package/dist/features/tasks/attachments.d.ts.map +1 -1
  561. package/dist/features/tasks/attachments.js +11 -35
  562. package/dist/features/tasks/attachments.js.map +1 -1
  563. package/dist/features/tasks/execute/outputFns.d.ts +1 -1
  564. package/dist/features/tasks/execute/outputFns.d.ts.map +1 -1
  565. package/dist/features/tasks/execute/outputFns.js +17 -1
  566. package/dist/features/tasks/execute/outputFns.js.map +1 -1
  567. package/dist/features/tasks/execute/postExecution.d.ts +4 -1
  568. package/dist/features/tasks/execute/postExecution.d.ts.map +1 -1
  569. package/dist/features/tasks/execute/postExecution.js +33 -14
  570. package/dist/features/tasks/execute/postExecution.js.map +1 -1
  571. package/dist/features/tasks/execute/resolveTask.d.ts +9 -3
  572. package/dist/features/tasks/execute/resolveTask.d.ts.map +1 -1
  573. package/dist/features/tasks/execute/resolveTask.js +45 -13
  574. package/dist/features/tasks/execute/resolveTask.js.map +1 -1
  575. package/dist/features/tasks/execute/runTaskExecution.d.ts +7 -1
  576. package/dist/features/tasks/execute/runTaskExecution.d.ts.map +1 -1
  577. package/dist/features/tasks/execute/runTaskExecution.js +11 -8
  578. package/dist/features/tasks/execute/runTaskExecution.js.map +1 -1
  579. package/dist/features/tasks/execute/selectAndExecute.d.ts.map +1 -1
  580. package/dist/features/tasks/execute/selectAndExecute.js +6 -2
  581. package/dist/features/tasks/execute/selectAndExecute.js.map +1 -1
  582. package/dist/features/tasks/execute/taskExecution.d.ts +12 -3
  583. package/dist/features/tasks/execute/taskExecution.d.ts.map +1 -1
  584. package/dist/features/tasks/execute/taskExecution.js +63 -16
  585. package/dist/features/tasks/execute/taskExecution.js.map +1 -1
  586. package/dist/features/tasks/execute/taskResultHandler.d.ts +2 -2
  587. package/dist/features/tasks/execute/taskResultHandler.d.ts.map +1 -1
  588. package/dist/features/tasks/execute/taskResultHandler.js +8 -4
  589. package/dist/features/tasks/execute/taskResultHandler.js.map +1 -1
  590. package/dist/features/tasks/execute/taskWorkflowExecution.d.ts.map +1 -1
  591. package/dist/features/tasks/execute/taskWorkflowExecution.js +101 -10
  592. package/dist/features/tasks/execute/taskWorkflowExecution.js.map +1 -1
  593. package/dist/features/tasks/execute/traceTaskMetadata.js +3 -6
  594. package/dist/features/tasks/execute/traceTaskMetadata.js.map +1 -1
  595. package/dist/features/tasks/execute/types.d.ts +103 -2
  596. package/dist/features/tasks/execute/types.d.ts.map +1 -1
  597. package/dist/features/tasks/execute/workflowExecution.d.ts +3 -1
  598. package/dist/features/tasks/execute/workflowExecution.d.ts.map +1 -1
  599. package/dist/features/tasks/execute/workflowExecution.js +41 -3
  600. package/dist/features/tasks/execute/workflowExecution.js.map +1 -1
  601. package/dist/features/tasks/execute/workflowExecutionApi.d.ts +6 -0
  602. package/dist/features/tasks/execute/workflowExecutionApi.d.ts.map +1 -0
  603. package/dist/features/tasks/execute/workflowExecutionApi.js +25 -0
  604. package/dist/features/tasks/execute/workflowExecutionApi.js.map +1 -0
  605. package/dist/features/tasks/execute/workflowExecutionBootstrap.d.ts +2 -1
  606. package/dist/features/tasks/execute/workflowExecutionBootstrap.d.ts.map +1 -1
  607. package/dist/features/tasks/execute/workflowExecutionBootstrap.js +18 -14
  608. package/dist/features/tasks/execute/workflowExecutionBootstrap.js.map +1 -1
  609. package/dist/features/tasks/execute/workflowExecutionEvents.d.ts +14 -1
  610. package/dist/features/tasks/execute/workflowExecutionEvents.d.ts.map +1 -1
  611. package/dist/features/tasks/execute/workflowExecutionEvents.js +244 -3
  612. package/dist/features/tasks/execute/workflowExecutionEvents.js.map +1 -1
  613. package/dist/features/tasks/list/instructMode.d.ts.map +1 -1
  614. package/dist/features/tasks/list/instructMode.js +3 -3
  615. package/dist/features/tasks/list/instructMode.js.map +1 -1
  616. package/dist/features/tasks/list/requeueHelpers.d.ts +1 -0
  617. package/dist/features/tasks/list/requeueHelpers.d.ts.map +1 -1
  618. package/dist/features/tasks/list/requeueHelpers.js +3 -0
  619. package/dist/features/tasks/list/requeueHelpers.js.map +1 -1
  620. package/dist/features/tasks/list/taskInstructionActions.d.ts.map +1 -1
  621. package/dist/features/tasks/list/taskInstructionActions.js +2 -2
  622. package/dist/features/tasks/list/taskInstructionActions.js.map +1 -1
  623. package/dist/features/tasks/list/taskRetryActions.d.ts.map +1 -1
  624. package/dist/features/tasks/list/taskRetryActions.js +1 -4
  625. package/dist/features/tasks/list/taskRetryActions.js.map +1 -1
  626. package/dist/features/tasks/resume/directInstructMode.d.ts.map +1 -1
  627. package/dist/features/tasks/resume/directInstructMode.js +3 -3
  628. package/dist/features/tasks/resume/directInstructMode.js.map +1 -1
  629. package/dist/features/tasks/taskContextValidation.d.ts +10 -0
  630. package/dist/features/tasks/taskContextValidation.d.ts.map +1 -0
  631. package/dist/features/tasks/taskContextValidation.js +73 -0
  632. package/dist/features/tasks/taskContextValidation.js.map +1 -0
  633. package/dist/infra/codex/client.d.ts.map +1 -1
  634. package/dist/infra/codex/client.js +5 -0
  635. package/dist/infra/codex/client.js.map +1 -1
  636. package/dist/infra/config/configNormalizers.d.ts.map +1 -1
  637. package/dist/infra/config/configNormalizers.js +14 -11
  638. package/dist/infra/config/configNormalizers.js.map +1 -1
  639. package/dist/infra/config/global/globalConfigCore.js +2 -2
  640. package/dist/infra/config/global/globalConfigCore.js.map +1 -1
  641. package/dist/infra/config/loaders/resource-resolver.d.ts +3 -33
  642. package/dist/infra/config/loaders/resource-resolver.d.ts.map +1 -1
  643. package/dist/infra/config/loaders/resource-resolver.js +140 -52
  644. package/dist/infra/config/loaders/resource-resolver.js.map +1 -1
  645. package/dist/infra/config/loaders/workflowCallContracts.d.ts.map +1 -1
  646. package/dist/infra/config/loaders/workflowCallContracts.js +5 -0
  647. package/dist/infra/config/loaders/workflowCallContracts.js.map +1 -1
  648. package/dist/infra/config/loaders/workflowCallableArgResolver.d.ts.map +1 -1
  649. package/dist/infra/config/loaders/workflowCallableArgResolver.js +42 -7
  650. package/dist/infra/config/loaders/workflowCallableArgResolver.js.map +1 -1
  651. package/dist/infra/config/loaders/workflowCallableRuleValidation.d.ts.map +1 -1
  652. package/dist/infra/config/loaders/workflowCallableRuleValidation.js +5 -0
  653. package/dist/infra/config/loaders/workflowCallableRuleValidation.js.map +1 -1
  654. package/dist/infra/config/loaders/workflowDoctor.d.ts.map +1 -1
  655. package/dist/infra/config/loaders/workflowDoctor.js +19 -7
  656. package/dist/infra/config/loaders/workflowDoctor.js.map +1 -1
  657. package/dist/infra/config/loaders/workflowLoopMonitorNormalizer.d.ts +3 -1
  658. package/dist/infra/config/loaders/workflowLoopMonitorNormalizer.d.ts.map +1 -1
  659. package/dist/infra/config/loaders/workflowLoopMonitorNormalizer.js +12 -2
  660. package/dist/infra/config/loaders/workflowLoopMonitorNormalizer.js.map +1 -1
  661. package/dist/infra/config/loaders/workflowPackageScope.d.ts +1 -0
  662. package/dist/infra/config/loaders/workflowPackageScope.d.ts.map +1 -1
  663. package/dist/infra/config/loaders/workflowPackageScope.js +20 -2
  664. package/dist/infra/config/loaders/workflowPackageScope.js.map +1 -1
  665. package/dist/infra/config/loaders/workflowParser.d.ts.map +1 -1
  666. package/dist/infra/config/loaders/workflowParser.js +17 -14
  667. package/dist/infra/config/loaders/workflowParser.js.map +1 -1
  668. package/dist/infra/config/loaders/workflowPreview.d.ts +3 -0
  669. package/dist/infra/config/loaders/workflowPreview.d.ts.map +1 -1
  670. package/dist/infra/config/loaders/workflowPreview.js +17 -9
  671. package/dist/infra/config/loaders/workflowPreview.js.map +1 -1
  672. package/dist/infra/config/loaders/workflowRuleNormalizer.d.ts +10 -0
  673. package/dist/infra/config/loaders/workflowRuleNormalizer.d.ts.map +1 -1
  674. package/dist/infra/config/loaders/workflowRuleNormalizer.js +70 -1
  675. package/dist/infra/config/loaders/workflowRuleNormalizer.js.map +1 -1
  676. package/dist/infra/config/loaders/workflowStepNormalizer.d.ts +1 -1
  677. package/dist/infra/config/loaders/workflowStepNormalizer.d.ts.map +1 -1
  678. package/dist/infra/config/loaders/workflowStepNormalizer.js +33 -11
  679. package/dist/infra/config/loaders/workflowStepNormalizer.js.map +1 -1
  680. package/dist/infra/config/project/sessionStore.d.ts +2 -2
  681. package/dist/infra/config/project/sessionStore.d.ts.map +1 -1
  682. package/dist/infra/config/project/sessionStore.js +12 -4
  683. package/dist/infra/config/project/sessionStore.js.map +1 -1
  684. package/dist/infra/config/providerModelRequirements.d.ts +2 -0
  685. package/dist/infra/config/providerModelRequirements.d.ts.map +1 -0
  686. package/dist/infra/config/providerModelRequirements.js +2 -0
  687. package/dist/infra/config/providerModelRequirements.js.map +1 -0
  688. package/dist/infra/git/format.d.ts +1 -0
  689. package/dist/infra/git/format.d.ts.map +1 -1
  690. package/dist/infra/git/format.js +21 -0
  691. package/dist/infra/git/format.js.map +1 -1
  692. package/dist/infra/git/index.d.ts +1 -1
  693. package/dist/infra/git/index.d.ts.map +1 -1
  694. package/dist/infra/git/types.d.ts +16 -4
  695. package/dist/infra/git/types.d.ts.map +1 -1
  696. package/dist/infra/github/GitHubProvider.d.ts +2 -1
  697. package/dist/infra/github/GitHubProvider.d.ts.map +1 -1
  698. package/dist/infra/github/GitHubProvider.js +4 -1
  699. package/dist/infra/github/GitHubProvider.js.map +1 -1
  700. package/dist/infra/github/issue.d.ts +2 -1
  701. package/dist/infra/github/issue.d.ts.map +1 -1
  702. package/dist/infra/github/issue.js +23 -2
  703. package/dist/infra/github/issue.js.map +1 -1
  704. package/dist/infra/gitlab/GitLabProvider.d.ts +2 -1
  705. package/dist/infra/gitlab/GitLabProvider.d.ts.map +1 -1
  706. package/dist/infra/gitlab/GitLabProvider.js +4 -1
  707. package/dist/infra/gitlab/GitLabProvider.js.map +1 -1
  708. package/dist/infra/gitlab/issue.d.ts +2 -1
  709. package/dist/infra/gitlab/issue.d.ts.map +1 -1
  710. package/dist/infra/gitlab/issue.js +30 -2
  711. package/dist/infra/gitlab/issue.js.map +1 -1
  712. package/dist/infra/opencode/OpenCodeStreamHandler.d.ts +4 -0
  713. package/dist/infra/opencode/OpenCodeStreamHandler.d.ts.map +1 -1
  714. package/dist/infra/opencode/OpenCodeStreamHandler.js.map +1 -1
  715. package/dist/infra/opencode/allowedTools.d.ts +1 -0
  716. package/dist/infra/opencode/allowedTools.d.ts.map +1 -1
  717. package/dist/infra/opencode/allowedTools.js +6 -0
  718. package/dist/infra/opencode/allowedTools.js.map +1 -1
  719. package/dist/infra/opencode/client.d.ts +2 -0
  720. package/dist/infra/opencode/client.d.ts.map +1 -1
  721. package/dist/infra/opencode/client.js +392 -169
  722. package/dist/infra/opencode/client.js.map +1 -1
  723. package/dist/infra/opencode/types.d.ts +35 -0
  724. package/dist/infra/opencode/types.d.ts.map +1 -1
  725. package/dist/infra/opencode/types.js +98 -11
  726. package/dist/infra/opencode/types.js.map +1 -1
  727. package/dist/infra/opencode/unavailable-tool-loop.d.ts +13 -0
  728. package/dist/infra/opencode/unavailable-tool-loop.d.ts.map +1 -1
  729. package/dist/infra/opencode/unavailable-tool-loop.js +46 -0
  730. package/dist/infra/opencode/unavailable-tool-loop.js.map +1 -1
  731. package/dist/infra/providers/allowed-tool-edit-policy.d.ts +1 -0
  732. package/dist/infra/providers/allowed-tool-edit-policy.d.ts.map +1 -1
  733. package/dist/infra/providers/allowed-tool-edit-policy.js +35 -1
  734. package/dist/infra/providers/allowed-tool-edit-policy.js.map +1 -1
  735. package/dist/infra/providers/claude-headless.d.ts.map +1 -1
  736. package/dist/infra/providers/claude-headless.js +0 -2
  737. package/dist/infra/providers/claude-headless.js.map +1 -1
  738. package/dist/infra/providers/claude-terminal.d.ts.map +1 -1
  739. package/dist/infra/providers/claude-terminal.js +0 -2
  740. package/dist/infra/providers/claude-terminal.js.map +1 -1
  741. package/dist/infra/providers/claude.d.ts.map +1 -1
  742. package/dist/infra/providers/claude.js +0 -2
  743. package/dist/infra/providers/claude.js.map +1 -1
  744. package/dist/infra/providers/opencode.d.ts +3 -2
  745. package/dist/infra/providers/opencode.d.ts.map +1 -1
  746. package/dist/infra/providers/opencode.js +19 -8
  747. package/dist/infra/providers/opencode.js.map +1 -1
  748. package/dist/infra/providers/types.d.ts +1 -1
  749. package/dist/infra/providers/types.d.ts.map +1 -1
  750. package/dist/infra/task/clone-exec.d.ts.map +1 -1
  751. package/dist/infra/task/clone-exec.js +31 -0
  752. package/dist/infra/task/clone-exec.js.map +1 -1
  753. package/dist/infra/task/enqueueService.d.ts +115 -0
  754. package/dist/infra/task/enqueueService.d.ts.map +1 -0
  755. package/dist/infra/task/enqueueService.js +191 -0
  756. package/dist/infra/task/enqueueService.js.map +1 -0
  757. package/dist/infra/task/enqueuedTaskFile.d.ts +6 -0
  758. package/dist/infra/task/enqueuedTaskFile.d.ts.map +1 -0
  759. package/dist/infra/task/enqueuedTaskFile.js +55 -0
  760. package/dist/infra/task/enqueuedTaskFile.js.map +1 -0
  761. package/dist/infra/task/issueTask.d.ts +20 -0
  762. package/dist/infra/task/issueTask.d.ts.map +1 -0
  763. package/dist/{features/tasks/add → infra/task}/issueTask.js +61 -43
  764. package/dist/infra/task/issueTask.js.map +1 -0
  765. package/dist/infra/task/listSerializer.d.ts +1 -0
  766. package/dist/infra/task/listSerializer.d.ts.map +1 -1
  767. package/dist/infra/task/listSerializer.js +1 -0
  768. package/dist/infra/task/listSerializer.js.map +1 -1
  769. package/dist/infra/task/mapper.d.ts.map +1 -1
  770. package/dist/infra/task/mapper.js +1 -0
  771. package/dist/infra/task/mapper.js.map +1 -1
  772. package/dist/infra/task/taskExecutionSchemas.d.ts +3 -0
  773. package/dist/infra/task/taskExecutionSchemas.d.ts.map +1 -1
  774. package/dist/infra/task/taskExecutionSchemas.js +4 -2
  775. package/dist/infra/task/taskExecutionSchemas.js.map +1 -1
  776. package/dist/infra/task/taskRecordSchemas.d.ts +2 -0
  777. package/dist/infra/task/taskRecordSchemas.d.ts.map +1 -1
  778. package/dist/infra/workflow/system/DefaultSystemStepServices.d.ts.map +1 -1
  779. package/dist/infra/workflow/system/DefaultSystemStepServices.js +7 -7
  780. package/dist/infra/workflow/system/DefaultSystemStepServices.js.map +1 -1
  781. package/dist/infra/workflow/system/system-enqueue-effect.d.ts.map +1 -1
  782. package/dist/infra/workflow/system/system-enqueue-effect.js +78 -20
  783. package/dist/infra/workflow/system/system-enqueue-effect.js.map +1 -1
  784. package/dist/infra/workflow/system/system-git-context.d.ts +6 -6
  785. package/dist/infra/workflow/system/system-git-context.d.ts.map +1 -1
  786. package/dist/infra/workflow/system/system-git-context.js +13 -13
  787. package/dist/infra/workflow/system/system-git-context.js.map +1 -1
  788. package/dist/infra/workflow/system/system-issue-input-resolver.d.ts +3 -3
  789. package/dist/infra/workflow/system/system-issue-input-resolver.d.ts.map +1 -1
  790. package/dist/infra/workflow/system/system-issue-input-resolver.js +8 -8
  791. package/dist/infra/workflow/system/system-issue-input-resolver.js.map +1 -1
  792. package/dist/infra/workflow/system/system-pr-effects.d.ts.map +1 -1
  793. package/dist/infra/workflow/system/system-pr-effects.js +6 -3
  794. package/dist/infra/workflow/system/system-pr-effects.js.map +1 -1
  795. package/dist/infra/workflow/system/system-pr-input-resolver.d.ts +3 -3
  796. package/dist/infra/workflow/system/system-pr-input-resolver.d.ts.map +1 -1
  797. package/dist/infra/workflow/system/system-pr-input-resolver.js +8 -8
  798. package/dist/infra/workflow/system/system-pr-input-resolver.js.map +1 -1
  799. package/dist/infra/workflow/system/system-sync-effects.js +2 -2
  800. package/dist/infra/workflow/system/system-sync-effects.js.map +1 -1
  801. package/dist/shared/constants.d.ts +1 -0
  802. package/dist/shared/constants.d.ts.map +1 -1
  803. package/dist/shared/constants.js +1 -0
  804. package/dist/shared/constants.js.map +1 -1
  805. package/dist/shared/i18n/labels_en.yaml +115 -0
  806. package/dist/shared/i18n/labels_ja.yaml +115 -0
  807. package/dist/shared/package-info.d.ts +2 -0
  808. package/dist/shared/package-info.d.ts.map +1 -0
  809. package/dist/shared/package-info.js +5 -0
  810. package/dist/shared/package-info.js.map +1 -0
  811. package/dist/shared/prompts/en/exec_assistant_clarify.md +14 -0
  812. package/dist/shared/prompts/en/exec_assistant_instruct.md +14 -0
  813. package/dist/shared/prompts/en/exec_assistant_summary.md +12 -0
  814. package/dist/shared/prompts/en/exec_facet_create.md +8 -0
  815. package/dist/shared/prompts/en/exec_facet_edit.md +8 -0
  816. package/dist/shared/prompts/en/finding_manager_instruction.md +35 -0
  817. package/dist/shared/prompts/en/opencode_agent_prompt.md +52 -0
  818. package/dist/shared/prompts/en/opencode_report_agent_prompt.md +8 -0
  819. package/dist/shared/prompts/en/opencode_review_agent_prompt.md +32 -0
  820. package/dist/shared/prompts/en/perform_phase1_message.md +1 -4
  821. package/dist/shared/prompts/en/perform_phase2_message.md +1 -0
  822. package/dist/shared/prompts/en/perform_phase3_message.md +1 -0
  823. package/dist/shared/prompts/en/provider_runtime_system_prompt.md +1 -0
  824. package/dist/shared/prompts/en/score_slug_system_prompt.md +1 -0
  825. package/dist/shared/prompts/en/score_slug_user_prompt.md +1 -0
  826. package/dist/shared/prompts/en/score_summary_system_prompt.md +1 -0
  827. package/dist/shared/prompts/en/source_context_section_guidance.md +6 -0
  828. package/dist/shared/prompts/en/source_context_system_guard.md +8 -0
  829. package/dist/shared/prompts/en/structured_json_schema_instruction.md +12 -0
  830. package/dist/shared/prompts/en/structured_json_step_instruction.md +12 -0
  831. package/dist/shared/prompts/en/sync_conflict_resolver_message.md +1 -0
  832. package/dist/shared/prompts/en/sync_conflict_resolver_system_prompt.md +1 -0
  833. package/dist/shared/prompts/index.d.ts.map +1 -1
  834. package/dist/shared/prompts/index.js +3 -1
  835. package/dist/shared/prompts/index.js.map +1 -1
  836. package/dist/shared/prompts/ja/exec_assistant_clarify.md +14 -0
  837. package/dist/shared/prompts/ja/exec_assistant_instruct.md +14 -0
  838. package/dist/shared/prompts/ja/exec_assistant_summary.md +12 -0
  839. package/dist/shared/prompts/ja/exec_facet_create.md +8 -0
  840. package/dist/shared/prompts/ja/exec_facet_edit.md +8 -0
  841. package/dist/shared/prompts/ja/finding_manager_instruction.md +35 -0
  842. package/dist/shared/prompts/ja/opencode_agent_prompt.md +52 -0
  843. package/dist/shared/prompts/ja/opencode_report_agent_prompt.md +8 -0
  844. package/dist/shared/prompts/ja/opencode_review_agent_prompt.md +32 -0
  845. package/dist/shared/prompts/ja/perform_phase1_message.md +1 -4
  846. package/dist/shared/prompts/ja/perform_phase2_message.md +1 -0
  847. package/dist/shared/prompts/ja/perform_phase3_message.md +1 -0
  848. package/dist/shared/prompts/ja/provider_runtime_system_prompt.md +1 -0
  849. package/dist/shared/prompts/ja/score_slug_system_prompt.md +1 -0
  850. package/dist/shared/prompts/ja/score_slug_user_prompt.md +1 -0
  851. package/dist/shared/prompts/ja/score_summary_system_prompt.md +1 -0
  852. package/dist/shared/prompts/ja/source_context_section_guidance.md +6 -0
  853. package/dist/shared/prompts/ja/source_context_system_guard.md +8 -0
  854. package/dist/shared/prompts/ja/structured_json_schema_instruction.md +12 -0
  855. package/dist/shared/prompts/ja/structured_json_step_instruction.md +12 -0
  856. package/dist/shared/prompts/ja/sync_conflict_resolver_message.md +1 -0
  857. package/dist/shared/prompts/ja/sync_conflict_resolver_system_prompt.md +1 -0
  858. package/dist/shared/utils/entrypoint.d.ts +2 -0
  859. package/dist/shared/utils/entrypoint.d.ts.map +1 -0
  860. package/dist/shared/utils/entrypoint.js +15 -0
  861. package/dist/shared/utils/entrypoint.js.map +1 -0
  862. package/dist/shared/utils/index.d.ts +1 -0
  863. package/dist/shared/utils/index.d.ts.map +1 -1
  864. package/dist/shared/utils/index.js +1 -0
  865. package/dist/shared/utils/index.js.map +1 -1
  866. package/dist/shared/utils/pathBoundary.d.ts +6 -0
  867. package/dist/shared/utils/pathBoundary.d.ts.map +1 -1
  868. package/dist/shared/utils/pathBoundary.js +38 -1
  869. package/dist/shared/utils/pathBoundary.js.map +1 -1
  870. package/dist/shared/utils/safeExternalErrorMessage.d.ts +2 -0
  871. package/dist/shared/utils/safeExternalErrorMessage.d.ts.map +1 -0
  872. package/dist/shared/utils/safeExternalErrorMessage.js +22 -0
  873. package/dist/shared/utils/safeExternalErrorMessage.js.map +1 -0
  874. package/package.json +29 -10
  875. package/builtins/en/facets/instructions/review-pure.md +0 -9
  876. package/builtins/en/facets/personas/pure-reviewer.md +0 -22
  877. package/builtins/en/provider-options/review-web.yaml +0 -14
  878. package/builtins/ja/facets/instructions/review-pure.md +0 -9
  879. package/builtins/ja/facets/personas/pure-reviewer.md +0 -22
  880. package/builtins/ja/provider-options/review-web.yaml +0 -14
  881. package/dist/core/workflow/claude-effort-compatibility.d.ts +0 -3
  882. package/dist/core/workflow/claude-effort-compatibility.d.ts.map +0 -1
  883. package/dist/core/workflow/claude-effort-compatibility.js +0 -28
  884. package/dist/core/workflow/claude-effort-compatibility.js.map +0 -1
  885. package/dist/core/workflow/provider-model-compatibility.d.ts +0 -7
  886. package/dist/core/workflow/provider-model-compatibility.d.ts.map +0 -1
  887. package/dist/core/workflow/provider-model-compatibility.js +0 -20
  888. package/dist/core/workflow/provider-model-compatibility.js.map +0 -1
  889. package/dist/features/interactive/policyPrompt.d.ts +0 -2
  890. package/dist/features/interactive/policyPrompt.d.ts.map +0 -1
  891. package/dist/features/interactive/policyPrompt.js +0 -18
  892. package/dist/features/interactive/policyPrompt.js.map +0 -1
  893. package/dist/features/tasks/add/issueTask.d.ts +0 -7
  894. package/dist/features/tasks/add/issueTask.d.ts.map +0 -1
  895. package/dist/features/tasks/add/issueTask.js.map +0 -1
  896. package/dist/infra/config/providerModelCompatibility.d.ts +0 -2
  897. package/dist/infra/config/providerModelCompatibility.d.ts.map +0 -1
  898. package/dist/infra/config/providerModelCompatibility.js +0 -2
  899. package/dist/infra/config/providerModelCompatibility.js.map +0 -1
  900. package/dist/shared/prompts/en/score_interactive_policy.md +0 -83
  901. package/dist/shared/prompts/ja/score_interactive_policy.md +0 -83
@@ -1,29 +1,125 @@
1
1
  # CQRS+ES Knowledge
2
2
 
3
+ ## CQRS+ES Adoption Decision
4
+
5
+ CQRS+ES is a design in which state changes are stored as domain events, and current state and Read Models are derived from them. Even when the backend as a whole or the workflow handles CQRS+ES, not every new feature needs to be implemented with event sourcing.
6
+
7
+ | Criteria | Judgment |
8
+ |----------|----------|
9
+ | User request, design material, or existing boundaries explicitly require CQRS+ES | Adopt CQRS+ES |
10
+ | State transitions, lifecycle, and business invariants are central to the feature | Consider CQRS+ES |
11
+ | Change events trigger other Aggregates, Sagas, or downstream processes | Consider CQRS+ES |
12
+ | Restoring state at a past point, event replay, or audit evidence itself is a requirement | Consider CQRS+ES |
13
+ | Read Models need to be projected asynchronously for multiple uses | Consider CQRS+ES |
14
+ | The feature is complete with current-value reference and updates only | Prefer CRUD |
15
+ | Security settings, feature flags, allowlists, thresholds, or similar values require immediate reflection | Prefer CRUD |
16
+ | There is no domain vocabulary beyond "create/update/delete" | Prefer CRUD |
17
+ | The task is merely being implemented in a CQRS+ES workflow | Do not use as adoption rationale |
18
+ | Adding CQRS+ES requirements to a task specification when they were absent from the original task | REJECT |
19
+
20
+ Adopt CQRS+ES based on requirements. An existing system containing CQRS+ES can justify aligning dependencies and boundaries, but it does not justify event-sourcing simple settings tables.
21
+
22
+ ### Requirement Transformation
23
+
24
+ If the original task or user request states only CRUD-equivalent business requirements, do not add "commands, events, and projections" as new requirements in the task specification. If it is unclear whether CQRS+ES is needed, state the adoption rationale or leave it as an open question.
25
+
26
+ | Original Request | How to Convert into a Specification |
27
+ |------------------|-------------------------------------|
28
+ | "Manage allowed IPs per facility" | Treat as CRUD-managed settings. The domain vocabulary is only "add/delete" and has no business rules |
29
+ | "Manage order approval, cancellation, and returns, and trigger billing or inventory depending on state" | Candidate for CQRS+ES. Complex state transitions and business invariants, with multiple Aggregates interacting |
30
+ | "For insurance contract changes, review rules differ by change type, and past assessment history affects future decisions" | Candidate for CQRS+ES. Business rules are complex and change over time; history itself is input to business decisions |
31
+ | "Show who changed what and when on the screen" | Check whether CRUD + audit logs is enough. If the requirement is only displaying change history, audit columns are often sufficient |
32
+ | "Toggle notification settings on/off" | Treat as CRUD-managed settings. It only references and updates current values |
33
+
34
+ CQRS+ES is most valuable in complex business domains, such as finance, insurance, or healthcare, where business rules are complex and change over time. Simple audit requirements or technical asynchronous processing alone are not sufficient conditions for CQRS+ES. The decision axis is business-logic complexity.
35
+
3
36
  ## Aggregate Design
4
37
 
5
38
  Aggregates hold only fields necessary for decision-making.
6
39
 
7
- Command Model (Aggregate) role is to "receive commands, make decisions, and emit events". Query data is handled by Read Model (Projection).
40
+ The Command Model (Aggregate) role is to "receive commands, make decisions, and emit events". Query data belongs to the Read Model (Projection).
8
41
 
9
- "Necessary for decision" means:
10
- - Used in `if`/`require` conditional branches
11
- - Field value referenced when emitting events in instance methods
42
+ "Necessary for decision-making" means:
43
+ - Used in `if` / `require` conditional branches
44
+ - Field values are referenced by instance methods when emitting events
12
45
 
13
46
  | Criteria | Judgment |
14
47
  |----------|----------|
15
48
  | Aggregate spans multiple transaction boundaries | REJECT |
16
- | Direct references between Aggregates (not ID references) | REJECT |
49
+ | Direct references between Aggregates, not ID references | REJECT |
17
50
  | Aggregate exceeds 100 lines | Consider splitting |
18
- | Business invariants exist outside Aggregate | REJECT |
51
+ | Business invariants exist outside the Aggregate | REJECT |
19
52
  | Holding fields not used for decisions | REJECT |
53
+ | Branching state transitions with origin metadata such as `source` / `input` / `origin` / `channel` / `type` | REJECT by default |
54
+ | Rejecting, only for a specific input source, a state allowed by the existing Aggregate's normal lifecycle | REJECT |
55
+
56
+ Being used in an `if` / `require` branch is not enough to justify keeping a field in Aggregate state. First verify that the branch or validation is an essential invariant of the whole Aggregate.
57
+
58
+ ### Origin Metadata and Invariants
59
+
60
+ Origin metadata such as input source, channel, producer, or integration source can be needed for display, search, audit, or integration tracing. That need alone does not justify restoring it as Aggregate state.
61
+
62
+ | Criteria | Judgment |
63
+ |----------|----------|
64
+ | Origin metadata is needed only for display, search, audit, or integration tracing | Keep it in the Event payload or Read Model |
65
+ | A branch based on origin metadata creates constraints that differ from the existing Aggregate's normal lifecycle | REJECT |
66
+ | A field that is normally optional becomes required only for one input source | REJECT |
67
+ | Invariants truly differ by input source | Consider a separate Aggregate, Command, or UseCase boundary |
68
+ | Adding origin metadata to Aggregate state only to support a `require` | REJECT |
69
+
70
+ ```kotlin
71
+ // NG - Origin metadata narrows the normal lifecycle of an existing Aggregate
72
+ data class Note(
73
+ val noteId: String,
74
+ val sourceType: SourceType?,
75
+ val targetIds: List<String>,
76
+ ) {
77
+ fun update(text: String, targetIds: List<String>): NoteUpdatedEvent {
78
+ if (sourceType == SourceType.EXTERNAL_IMPORT) {
79
+ require(targetIds.isNotEmpty())
80
+ }
81
+ return NoteUpdatedEvent(noteId, text, targetIds)
82
+ }
83
+ }
84
+
85
+ // OK - Track origin in events/read models and align Aggregate invariants with the normal lifecycle
86
+ data class Note(
87
+ val noteId: String,
88
+ val confirmed: Boolean,
89
+ ) {
90
+ fun update(text: String, targetIds: List<String>): NoteUpdatedEvent {
91
+ check(!confirmed)
92
+ return NoteUpdatedEvent(noteId, text, targetIds)
93
+ }
94
+ }
95
+
96
+ data class NoteCreatedEvent(
97
+ val noteId: String,
98
+ val text: String,
99
+ val targetIds: List<String>,
100
+ val sourceType: SourceType?, // Origin fact used by projections or audit
101
+ )
102
+ ```
103
+
104
+ ### Existing Lifecycle Priority
105
+
106
+ When integrating a new input flow into an existing Aggregate, prefer the existing normal lifecycle. Do not add input-source-specific commands, wrappers, services, or deletion paths merely because the input source differs.
107
+
108
+ | Criteria | Judgment |
109
+ |----------|----------|
110
+ | The existing Aggregate's normal command / event can represent the same fact | Use the existing lifecycle |
111
+ | An input-source-specific wrapper only thinly delegates to a normal command | REJECT |
112
+ | An input-source-specific command adds stricter required fields than the normal lifecycle | REJECT |
113
+ | Existing Aggregate deletion or update events can trigger derived processing | Separate into EventHandler |
114
+ | Display or search fields needed only by a specialized flow | Keep them in the Read Model |
115
+ | State transitions or invariants truly differ by input source | Consider a separate Aggregate / bounded context |
20
116
 
21
117
  Good Aggregate:
22
118
  ```kotlin
23
119
  // Only fields necessary for decisions
24
120
  data class Order(
25
121
  val orderId: String, // Used when emitting events
26
- val status: OrderStatus // Used for state checking
122
+ val status: OrderStatus // Used for state checks
27
123
  ) {
28
124
  fun confirm(confirmedBy: String): OrderConfirmedEvent {
29
125
  require(status == OrderStatus.PENDING) { "Cannot confirm in this state" }
@@ -38,15 +134,15 @@ data class Order(
38
134
  // Holding fields not used for decisions (NG)
39
135
  data class Order(
40
136
  val orderId: String,
41
- val customerId: String, // Not used for decisions
137
+ val customerId: String, // Not used for decisions
42
138
  val shippingAddress: Address, // Not used for decisions
43
139
  val status: OrderStatus
44
140
  )
45
141
  ```
46
142
 
47
- Aggregates with no additional operations have ID only:
143
+ Aggregates with no additional operations have only an ID:
48
144
  ```kotlin
49
- // When only creation, no additional operations
145
+ // Creation only, no additional operations
50
146
  data class Notification(val notificationId: String) {
51
147
  companion object {
52
148
  fun create(customerId: String, message: String): NotificationCreatedEvent {
@@ -60,61 +156,182 @@ data class Notification(val notificationId: String) {
60
156
  }
61
157
  ```
62
158
 
159
+ ### Adapter Pattern: Separating Domain from Framework
160
+
161
+ Do not put framework annotations such as `@Aggregate` or `@CommandHandler` directly on domain models. Adapter classes handle framework integration, and domain models focus on business logic.
162
+
163
+ ```kotlin
164
+ // Domain model: framework-independent business logic only
165
+ data class Order(
166
+ val orderId: String,
167
+ val status: OrderStatus = OrderStatus.PENDING
168
+ ) {
169
+ companion object {
170
+ fun place(orderId: String, customerId: String): OrderPlacedEvent {
171
+ require(customerId.isNotBlank()) { "Customer ID cannot be blank" }
172
+ return OrderPlacedEvent(orderId, customerId)
173
+ }
174
+
175
+ fun from(event: OrderPlacedEvent): Order {
176
+ return Order(orderId = event.orderId, status = OrderStatus.PENDING)
177
+ }
178
+ }
179
+
180
+ fun confirm(confirmedBy: String): OrderConfirmedEvent {
181
+ require(status == OrderStatus.PENDING) { "Cannot confirm in this state" }
182
+ return OrderConfirmedEvent(orderId, confirmedBy, LocalDateTime.now())
183
+ }
184
+
185
+ fun apply(event: OrderEvent): Order = when (event) {
186
+ is OrderPlacedEvent -> from(event)
187
+ is OrderConfirmedEvent -> copy(status = OrderStatus.CONFIRMED)
188
+ is OrderCancelledEvent -> copy(status = OrderStatus.CANCELLED)
189
+ }
190
+ }
191
+
192
+ // Adapter: framework integration. Domain call -> event publication bridge
193
+ @Aggregate
194
+ class OrderAggregateAdapter() {
195
+ private var order: Order? = null
196
+
197
+ @AggregateIdentifier
198
+ fun orderId(): String? = order?.orderId
199
+
200
+ @CommandHandler
201
+ constructor(command: PlaceOrderCommand) : this() {
202
+ val event = Order.place(command.orderId, command.customerId)
203
+ AggregateLifecycle.apply(event)
204
+ }
205
+
206
+ @CommandHandler
207
+ fun handle(command: ConfirmOrderCommand) {
208
+ val event = order!!.confirm(command.confirmedBy)
209
+ AggregateLifecycle.apply(event)
210
+ }
211
+
212
+ @EventSourcingHandler
213
+ fun on(event: OrderEvent) {
214
+ this.order = when (event) {
215
+ is OrderPlacedEvent -> Order.from(event)
216
+ else -> order?.apply(event)
217
+ }
218
+ }
219
+ }
220
+ ```
221
+
222
+ Benefits of separation:
223
+ - Domain models can be unit-tested without a framework
224
+ - Domain models do not need to change when the framework changes
225
+ - Adapters are boilerplate for receiving commands, calling the domain, and publishing events
226
+
227
+ ### apply/from Pattern: Event Replay
228
+
229
+ A pattern in which a domain model rebuilds its own state from events.
230
+
231
+ - `from(event)`: factory that builds initial state from a creation event
232
+ - `apply(event)`: returns new state from an event, using immutable updates with `copy()`
233
+ - `when` expressions plus sealed interfaces let the compiler guarantee exhaustiveness over event types
234
+
235
+ ```kotlin
236
+ fun apply(event: OrderEvent): Order = when (event) {
237
+ is OrderPlacedEvent -> from(event)
238
+ is OrderConfirmedEvent -> copy(status = OrderStatus.CONFIRMED)
239
+ is OrderShippedEvent -> copy(status = OrderStatus.SHIPPED)
240
+ // Because the interface is sealed, adding an event type without handling it is a compile error
241
+ }
242
+ ```
243
+
244
+ | Criteria | Judgment |
245
+ |----------|----------|
246
+ | Business logic such as validation inside `apply` | REJECT. `apply` is state restoration only |
247
+ | `apply` has side effects such as DB operations or event emission | REJECT |
248
+ | `apply` throws exceptions | REJECT. Replay failures are not acceptable |
249
+
63
250
  ## Event Design
64
251
 
65
252
  | Criteria | Judgment |
66
253
  |----------|----------|
67
- | Event not in past tense (Created Create) | REJECT |
254
+ | Event is not in past tense, such as Created -> Create | REJECT |
68
255
  | Event contains logic | REJECT |
69
- | Event contains internal state of other Aggregates | REJECT |
70
- | Event schema not version controlled | Warning |
71
- | CRUD-style events (Updated, Deleted) | Needs review |
256
+ | Event contains internal state of another Aggregate | REJECT |
257
+ | Event schema is not versioned | Warning |
258
+ | CRUD-style events such as Updated or Deleted | Needs review |
72
259
 
73
- Good Events:
260
+ Good events:
74
261
  ```kotlin
75
- // Good: Domain intent is clear
262
+ // Good: domain intent is clear
76
263
  OrderPlaced, PaymentReceived, ItemShipped
77
264
 
78
265
  // Bad: CRUD style
79
266
  OrderUpdated, OrderDeleted
80
267
  ```
81
268
 
82
- Event Granularity:
83
- - Too fine: `OrderFieldChanged` → Domain intent unclear
84
- - Appropriate: `ShippingAddressChanged` Intent is clear
85
- - Too coarse: `OrderModified` → What changed is unclear
269
+ ### Event Type Hierarchy with sealed interface
270
+
271
+ Aggregate events should use a sealed interface type hierarchy. The Aggregate root ID should be required as a common field, enabling exhaustive `when` checks.
272
+
273
+ ```kotlin
274
+ sealed interface OrderEvent {
275
+ val orderId: String // Required on every event
276
+ }
277
+
278
+ data class OrderPlacedEvent(
279
+ override val orderId: String,
280
+ val customerId: String
281
+ ) : OrderEvent
282
+
283
+ data class OrderConfirmedEvent(
284
+ override val orderId: String,
285
+ val approvalInfo: ApprovalInfo
286
+ ) : OrderEvent
287
+
288
+ data class OrderCancelledEvent(
289
+ override val orderId: String,
290
+ val cancellationInfo: CancellationInfo
291
+ ) : OrderEvent
292
+ ```
293
+
294
+ Benefits:
295
+ - A `when (event)` expression must list every event type, otherwise compilation fails. This is especially important in `apply`
296
+ - The compiler guarantees that the Aggregate root ID exists
297
+ - Event-handler branching by type is safer
298
+
299
+ Event granularity:
300
+ - Too fine: `OrderFieldChanged` -> domain intent is unclear
301
+ - Appropriate: `ShippingAddressChanged` -> intent is clear
302
+ - Too coarse: `OrderModified` -> unclear what changed
86
303
 
87
304
  ## Event Evolution
88
305
 
89
- Events are persisted contracts. When the current event type changes, old events must still be replayable. Translation of old events belongs in the upcaster / migration layer at the event-store boundary, not in the event type itself or in domain logic.
306
+ Events are persisted contracts. If the current event type changes, old events must still be replayable. Translating old events belongs in the upcaster / migration layer at the event-store restoration boundary, not in event classes or domain logic.
90
307
 
91
308
  | Criteria | Judgment |
92
309
  |----------|----------|
93
- | Persisted event type or fields changed with no translation path | REJECT |
94
- | Current event type keeps aliases or compatibility-only properties for old field names | REJECT. Keep history compatibility in upcasters |
95
- | Aggregate or apply directly interprets old event shapes | REJECT. Convert to current events before replay |
96
- | Event carries "previous value" only for compatibility | REJECT. Events represent the fact after it happened |
97
- | Upcaster converts old payloads to the current event meaning | OK |
98
- | Tests verify old payloads deserialize into current events through the upcaster | OK |
310
+ | Persisted event type or fields changed without a translation path | REJECT |
311
+ | Current event type keeps old field aliases or compatibility-only properties | REJECT. Keep history compatibility in upcasters |
312
+ | Aggregate or `apply` directly interprets old event shapes | REJECT. Convert to current events before replay |
313
+ | Event adds "previous value" only for compatibility | REJECT. Events represent the fact after it happened |
314
+ | Upcaster converts old payloads into current event meaning | OK |
315
+ | Tests verify conversion from old payloads to current events | OK |
99
316
 
100
- Responsibility split for event evolution:
317
+ Responsibilities in event evolution:
101
318
 
102
- | Responsibility | Place |
103
- |----------------|-------|
319
+ | Responsibility | Location |
320
+ |----------------|----------|
104
321
  | Current event meaning and fields | Event type |
105
- | Translation of old payloads | Upcaster / migration layer |
106
- | State restoration by event replay | Aggregate `apply` |
107
- | Guarantee that old events can become current events | Upcaster tests |
322
+ | Reading old payloads | Upcaster / migration layer |
323
+ | State restoration from event replay | Aggregate `apply` |
324
+ | Guarantee that old events can be converted to current events | Upcaster tests |
108
325
 
109
326
  ```kotlin
110
- // NG - mixing old-field compatibility into the current event type
327
+ // NG - Mixing old field compatibility into the current event type
111
328
  data class OrderAssignedEvent(
112
329
  val orderId: String,
113
330
  @JsonAlias("assigneeId")
114
331
  val assigneeIds: List<String>
115
332
  )
116
333
 
117
- // OK - current event type represents only the current contract
334
+ // OK - Current event type represents only the current contract
118
335
  data class OrderAssignedEvent(
119
336
  val orderId: String,
120
337
  val assigneeIds: List<String>
@@ -122,7 +339,7 @@ data class OrderAssignedEvent(
122
339
  ```
123
340
 
124
341
  ```kotlin
125
- // OK - convert old payloads to current payloads in the upcaster
342
+ // OK - Convert old payloads to current payloads in an upcaster
126
343
  when (eventType) {
127
344
  OrderAssignedEvent::class.java.typeName -> {
128
345
  event.moveTextFieldToArray("assigneeId", "assigneeIds")
@@ -130,135 +347,312 @@ when (eventType) {
130
347
  }
131
348
  ```
132
349
 
133
- Whether to keep old event classes depends on the framework and operations policy. In general, do not treat old classes as normal domain events; treat old serialized type names and payloads as the upcaster input contract and cover them with tests.
350
+ Whether to keep old event types in application code depends on the framework and operational policy. In general, it is better not to treat old types as normal domain events. Instead, test the old serialized type and payload as input contracts for the upcaster, keeping the current model clean.
351
+
352
+ ### Migration Scope Decomposition
353
+
354
+ CQRS+ES migration must distinguish DB schema migration, data migration, event upcasters, Read Model rebuilds, and API compatibility work. Do not treat "migrate / do not migrate" as a single decision.
355
+
356
+ | Criteria | Judgment |
357
+ |----------|----------|
358
+ | The user says migration is unnecessary, but the migration type is not decomposed | REJECT |
359
+ | DB schema migration or data migration is added while the scope is unclear | REJECT |
360
+ | An upcaster is added even though no persisted old events exist | REJECT |
361
+ | Data migration is created even though the Read Model can be rebuilt from events | REJECT. Check whether rebuild is enough |
362
+ | Add an upcaster only when event payload compatibility is required | OK |
363
+ | Treat API compatibility and event compatibility as separate decisions | OK |
134
364
 
135
365
  ## Command Handlers
136
366
 
137
367
  | Criteria | Judgment |
138
368
  |----------|----------|
139
- | Handler directly manipulates DB | REJECT |
140
- | Handler modifies multiple Aggregates | REJECT |
141
- | No command validation | REJECT |
369
+ | Handler directly manipulates the DB | REJECT |
370
+ | Handler changes multiple Aggregates | REJECT |
371
+ | Command has no validation | REJECT |
142
372
  | Handler executes queries to make decisions | Needs review |
143
373
 
144
- Good Command Handler:
374
+ Good command handler:
145
375
  ```
146
- 1. Receive command
147
- 2. Restore Aggregate from event store
148
- 3. Apply command to Aggregate
149
- 4. Save emitted events
376
+ 1. Receive a command
377
+ 2. Restore the Aggregate from the event store
378
+ 3. Apply the command to the Aggregate
379
+ 4. Store the emitted events
150
380
  ```
151
381
 
382
+ ### Multi-layer Validation
383
+
384
+ Validation responsibilities differ by layer. Do not collect every validation in one place.
385
+
386
+ | Layer | Responsibility | Means | Example |
387
+ |-------|----------------|-------|---------|
388
+ | API layer | Structural validation | `@NotBlank`, `init` block | Required fields, type, format |
389
+ | UseCase layer | Business-rule validation | Querying Read Models | Duplicate checks, existence of prerequisites |
390
+ | Domain layer | State-transition invariants | `require` | "Can only approve when PENDING" |
391
+
152
392
  ### Aggregate Decision Boundary
153
393
 
154
- Aggregates make decisions only from state that can be restored from their event history and facts explicitly carried by commands. They are not the place to interpret, normalize, or authorize boundary-originated inputs.
394
+ Aggregates make decisions only from state restored from their own event history and facts explicitly supplied as commands. They are not the place to interpret, normalize, or verify ownership of boundary-originated input.
395
+
396
+ Validation inside an Aggregate must be limited to state that can be reproduced solely by event replay. Other validation should be resolved at the boundary before command dispatch, and resolved facts should be passed to the Aggregate.
397
+
398
+ | Decision Target | Location |
399
+ |-----------------|----------|
400
+ | Whether the operation is possible in the current state | Aggregate |
401
+ | Whether command executor matches Aggregate owner | Aggregate |
402
+ | HTTP/API input shape is valid | API layer |
403
+ | Interpreting formats of external identifiers such as object keys, URLs, paths | UseCase layer or boundary policy/verifier |
404
+ | External identifier belongs to the current user/tenant | UseCase layer or boundary policy/verifier |
405
+ | Checking another Aggregate's Read Model or external facts | UseCase layer |
406
+ | State-transition decisions based on the same Aggregate's current state | Aggregate |
407
+ | Entity exists in an external service | Application-layer external-service integration |
408
+
409
+ Example: in an upload-completion command, the Aggregate decides whether the session owner matches the executor and whether the current state allows completion. The string shape of the object key and whether that key belongs to the current user/tenant area are verified in the UseCase layer before the command is sent.
410
+
411
+ ### Command Intent and Pre-querying
412
+
413
+ A Command represents what the user or external process intends to do, not which command should be selected after reading the current state. Decisions such as Add / Update / Delete / Noop based on current state should be pushed into the restored Aggregate, not decided from the same Aggregate's Read Model.
414
+
415
+ | Criteria | Judgment |
416
+ |----------|----------|
417
+ | Check existence or scope of other Aggregates or external facts, then pass resolved facts to the command | OK |
418
+ | Read the same Aggregate's Read Model to choose the command type | REJECT |
419
+ | The UseCase decides "update if it exists, add if it does not" from Query results | REJECT. Send an intent command such as Set / Attach / Upsert to the Aggregate |
420
+ | Aggregate or AggregateAdapter decides existence and transition validity from restored state | OK |
421
+ | EventHandler or UseCase suppresses duplicate commands with a pre-query even though the Aggregate could ignore them idempotently | REJECT. Protect with Aggregate state transitions |
422
+
423
+ ```kotlin
424
+ // NG - Query result chooses the command type
425
+ if (readService.exists(orderId)) {
426
+ commandGateway.send(UpdateOrderCommand(orderId, value))
427
+ } else {
428
+ commandGateway.send(AddOrderCommand(orderId, value))
429
+ }
430
+
431
+ // OK - Send an intent command; the Aggregate decides from restored state
432
+ commandGateway.send(SetOrderValueCommand(orderId, value))
433
+ ```
434
+
435
+ ```kotlin
436
+ // API layer: structural validation
437
+ data class OrderPostRequest(
438
+ @field:NotBlank val customerId: String,
439
+ @field:NotNull val items: List<OrderItemRequest>
440
+ ) {
441
+ init {
442
+ require(items.isNotEmpty()) { "An order must have at least one item" }
443
+ }
444
+ }
155
445
 
156
- Validation inside an Aggregate should be limited to facts reproducible by event replay. Other validation should be resolved before command dispatch, and the Aggregate should receive already-resolved facts.
446
+ // UseCase layer: business-rule validation by Read Model reference
447
+ @Service
448
+ class PlaceOrderUseCase(
449
+ private val commandGateway: CommandGateway,
450
+ private val customerRepository: CustomerRepository,
451
+ private val inventoryRepository: InventoryRepository
452
+ ) {
453
+ fun execute(input: PlaceOrderInput): Mono<PlaceOrderOutput> {
454
+ return Mono.fromCallable {
455
+ // Customer existence check
456
+ customerRepository.findById(input.customerId)
457
+ ?: throw CustomerNotFoundException("Customer does not exist")
458
+ // Inventory precheck
459
+ validateInventory(input.items)
460
+ // Command dispatch
461
+ val orderId = UUID.randomUUID().toString()
462
+ commandGateway.send<Any>(PlaceOrderCommand(orderId, input.customerId, input.items))
463
+ PlaceOrderOutput(orderId)
464
+ }
465
+ }
466
+ }
157
467
 
158
- | Decision target | Place |
159
- |-----------------|-------|
160
- | Whether the current state allows the operation | Aggregate |
161
- | Whether the command requester matches the Aggregate owner | Aggregate |
162
- | Whether HTTP/API input shape is valid | API layer |
163
- | Parsing external identifiers such as object keys, URLs, or paths | UseCase layer or boundary-side Policy/Verifier |
164
- | Whether an external identifier belongs to the current user/tenant | UseCase layer or boundary-side Policy/Verifier |
165
- | Checking Read Models or other Aggregate state | UseCase layer |
166
- | Checking that an external resource exists | Application-layer integration with the external service |
468
+ // Domain layer: state-transition invariant
469
+ fun confirm(confirmedBy: String): OrderConfirmedEvent {
470
+ require(status == OrderStatus.PENDING) { "Cannot confirm in this state" }
471
+ return OrderConfirmedEvent(orderId, confirmedBy, LocalDateTime.now())
472
+ }
473
+ ```
167
474
 
168
- Example: for an upload-completed command, the Aggregate decides whether the session owner matches the requester and whether the current state can be completed. The storage object key format and whether the key belongs to the current user/tenant are validated in the UseCase layer before sending the command.
475
+ | Criteria | Judgment |
476
+ |----------|----------|
477
+ | Domain-layer validation exists in API layer | REJECT. State-transition rules belong in the domain |
478
+ | UseCase-layer validation exists in Controller | REJECT. Separate into UseCase layer |
479
+ | API-layer validation such as `@NotBlank` exists in domain | REJECT. Structural validation belongs in API layer |
169
480
 
170
- ## UseCase Layer (Orchestration)
481
+ ## UseCase Layer: Orchestration
171
482
 
172
- UseCases sit between Controllers and command dispatch when orchestration is needed. They validate preconditions from Read Models across aggregates and perform required preparation before sending commands.
483
+ Place a UseCase layer between Controller and CommandGateway. The UseCase layer gathers facts that must be resolved at the boundary and normally sends one intent command. Subsequent state changes are driven by EventHandlers for committed events.
173
484
 
174
485
  ```
175
- Controller UseCase CommandGateway Aggregate
176
-
177
- QueryGateway / Repository (Read Model lookup)
486
+ Controller -> UseCase -> CommandGateway -> Aggregate
487
+ |
488
+ QueryGateway / Repository (Read Model reference)
178
489
  ```
179
490
 
180
- Cases where UseCase is needed:
181
- - Read Model checks from multiple aggregates before command dispatch
182
- - Multiple validations executed in sequence
183
- - Result consistency waiting after command dispatch
184
- - External integration or multiple command dispatches
491
+ Cases that need a UseCase:
492
+ - Checking another Aggregate's Read Model or external facts before command dispatch
493
+ - Running multiple validations sequentially
494
+ - Waiting for eventual consistency after command dispatch only for a synchronous API contract
185
495
 
186
- Cases where UseCase is unnecessary:
187
- - Simple operation completed by sending one command from Controller
188
- - Simple read that only queries the query side and converts to response
189
- - Operation that checks resource existence/scope and then sends one command
496
+ Cases that do not need a UseCase:
497
+ - A simple operation where the Controller sends one command and is done
498
+ - A simple read where the Controller queries the Query side and converts to a response
499
+ - An operation that only checks existence/scope of an existing resource and then sends one command
190
500
 
191
501
  | Criteria | Judgment |
192
502
  |----------|----------|
193
503
  | Controller directly references Repository for validation | Separate into UseCase layer |
194
- | UseCase depends on HTTP requests/responses | REJECT. UseCase must be protocol-independent |
195
- | UseCase directly modifies Aggregate internal state | REJECT. Use CommandGateway |
196
- | UseCase waits for results via Subscription Query | REJECT. Does not work in distributed environments. Use reactive polling |
197
- | UseCase only delegates to a query boundary or command dispatch | Consider deleting |
504
+ | UseCase depends on HTTP request/response | REJECT. UseCase must be protocol-independent |
505
+ | UseCase directly changes Aggregate internal state | REJECT. Use CommandGateway |
506
+ | UseCase sends multiple commands sequentially for the same state transition | REJECT. Separate into EventHandlers for committed events |
507
+ | UseCase queries the same Aggregate's state to choose the command type | REJECT. Push the decision into the Aggregate |
508
+ | UseCase validates another Aggregate or external facts and passes resolved facts to one command | OK |
509
+ | EventHandler receives a committed event and sends a command to another Aggregate | OK |
510
+ | processStore / ProcessStore / operationProcess / completeStep stores projection completion or procedural progress | REJECT. Model it with Projection and EventHandlers |
511
+ | There is an explicit long-running business process, retry, compensation, or user-visible progress | Consider Saga / Process Manager |
512
+ | UseCase only thinly delegates to another query layer or command dispatch | Consider removing |
513
+
514
+ ## Event-driven Chaining
515
+
516
+ In CQRS+ES, chains of state changes start from committed events. Application Services, UseCases, and Controllers must not synchronously control the order of multiple Aggregate changes by sending commands sequentially for the same state transition.
517
+
518
+ Basic shape:
519
+
520
+ ```text
521
+ UseCase -> Command -> Aggregate -> Event
522
+ |
523
+ EventHandler -> Command -> another Aggregate
524
+ |
525
+ Projection -> Read Model
526
+ ```
527
+
528
+ | Criteria | Judgment |
529
+ |----------|----------|
530
+ | UseCase sends command B immediately after command A for the same state transition | REJECT. Let an EventHandler receive A's event and send B |
531
+ | Another command is sent after `sendAndWait` returns to create consistency | REJECT. Separate into event chaining |
532
+ | A normal event from an existing Aggregate becomes the trigger for derived processing | OK |
533
+ | EventHandler receives a committed event and sends an idempotent command to another Aggregate | OK |
534
+ | Projection update and next-command dispatch are mixed in the same handler | REJECT. Separate Projection from EventHandler |
535
+ | There is contention, compensation, long-running retry, or user-visible progress | Consider Saga / Process Manager |
536
+ | processStore is created only to remember intermediate progress | REJECT. Split responsibilities into Aggregate events, Projections, or Saga |
198
537
 
199
538
  ## Projection Design
200
539
 
201
540
  | Criteria | Judgment |
202
541
  |----------|----------|
203
- | Projection issues commands | REJECT |
204
- | Projection references Write model | REJECT |
205
- | Single projection serves multiple use cases | Needs review |
206
- | Design that cannot be rebuilt | REJECT |
542
+ | Projection dispatches commands | REJECT |
543
+ | Projection references Write Model | REJECT |
544
+ | One projection supports multiple use cases | Needs review |
545
+ | Cannot be rebuilt | REJECT |
546
+
547
+ Good projections:
548
+ - Optimized for a specific read use case
549
+ - Rebuildable idempotently from events
550
+ - Completely independent from Write Model
551
+
552
+ ### Distinguishing Projections from EventHandlers for Side Effects
553
+
554
+ Both use `@EventHandler`, but their responsibilities differ. Do not confuse them.
555
+
556
+ | Type | Responsibility | Does | Does Not Do |
557
+ |------|----------------|------|-------------|
558
+ | Projection | Read Model update | Save/update Entity | Dispatch commands, call external APIs |
559
+ | EventHandler | Side effect | Dispatch commands to other Aggregates | Update Read Models |
560
+
561
+ ```kotlin
562
+ // Projection: Read Model update only
563
+ @Component
564
+ class OrderProjection(private val orderRepository: OrderRepository) {
565
+ @EventHandler
566
+ fun on(event: OrderPlacedEvent) {
567
+ val entity = OrderEntity(
568
+ orderId = event.orderId,
569
+ customerId = event.customerId,
570
+ status = OrderStatus.PENDING
571
+ )
572
+ orderRepository.save(entity)
573
+ }
574
+
575
+ @EventHandler
576
+ fun on(event: OrderConfirmedEvent) {
577
+ orderRepository.findById(event.orderId).ifPresent { entity ->
578
+ entity.status = OrderStatus.CONFIRMED
579
+ orderRepository.save(entity)
580
+ }
581
+ }
582
+ }
207
583
 
208
- Good Projection:
209
- - Optimized for specific read use case
210
- - Idempotently reconstructible from events
211
- - Completely independent from Write model
584
+ // EventHandler: side effect, command dispatch to another Aggregate
585
+ @Component
586
+ class InventoryReleaseHandler(private val commandGateway: CommandGateway) {
587
+ @EventHandler
588
+ fun on(event: OrderCancelledEvent) {
589
+ val command = ReleaseInventoryCommand(
590
+ productId = event.productId,
591
+ quantity = event.quantity
592
+ )
593
+ commandGateway.send<Any>(command)
594
+ }
595
+ }
596
+ ```
212
597
 
213
- ### External Work Triggers
598
+ | Criteria | Judgment |
599
+ |----------|----------|
600
+ | Projection uses CommandGateway | REJECT. Separate into EventHandler |
601
+ | EventHandler saves with Repository | REJECT. Separate into Projection |
602
+ | One class mixes Projection and EventHandler responsibilities | REJECT. Split classes |
214
603
 
215
- External workers and asynchronous work should start from domain events confirmed by the Aggregate. Application Services and Coordinators must not bundle command dispatch and external side effects in the same control flow.
604
+ ### Starting External Processing
605
+
606
+ Starting external workers or asynchronous processing should be triggered by a domain event that an Aggregate has committed. An Application Service or Coordinator must not bundle command dispatch and external side effects in the same control flow for the same state transition.
216
607
 
217
608
  | Criteria | Judgment |
218
609
  |----------|----------|
219
- | Application Service or Coordinator dispatches a command, then starts external work for the same state transition | REJECT. Separate into an EventHandler for the confirmed event |
220
- | Aggregate emits an event that represents generation or processing start, and an EventHandler starts external work | OK |
221
- | EventHandler converts external start failure into a failure command back to the Aggregate | OK |
222
- | Inputs needed by external work are represented in the event or reloadable through stable identifiers | OK |
223
- | Inputs needed by external work exist only as local variables during command handling | REJECT. Move them to events or reloadable references |
224
- | Saga is used only to start simple external work without contention or compensation | REJECT. EventHandler is sufficient |
610
+ | Application Service or Coordinator starts external processing immediately after command dispatch for the same state transition | REJECT. Separate into EventHandler for committed events |
611
+ | Aggregate emits an event that represents generation start or processing start, and EventHandler starts external processing | OK |
612
+ | EventHandler reports external-processing start failure back to the Aggregate with a failure command | OK |
613
+ | Input needed for external processing is represented by the event or stable IDs that can be reloaded | OK |
614
+ | External-processing input exists only in local variables during command processing | REJECT. Move to events or reloadable references |
615
+ | Saga is used for simple external processing with no contention or compensation | REJECT. EventHandler is enough |
225
616
 
226
- ## Query Side Design
617
+ ## Query-side Design
227
618
 
228
- Query side operates on an event-driven PubSub model. Projections update Read Models via EventHandler, and queries read from Read Models.
619
+ The Query side operates as an event-driven PubSub model. Projections update Read Models with EventHandlers, and the Query side references those Read Models.
229
620
 
230
- Event distribution uses PubSub (via message broker) to deliver events to all instances. Do not use mechanisms that assume delivery to the same instance.
621
+ Event delivery should be PubSub, through a message broker, to all instances. Do not rely on mechanisms that deliver only to the same instance unless delivery guarantees are confirmed.
231
622
 
232
- - **Subscription Query** (e.g., Axon's `subscriptionQuery()`): delivers change notifications back to the subscribing instance, but in distributed environments or when using third-party event store plugins, the subscribing instance and the notified instance may differ, making it impossible to return the response on the same machine. When synchronous response is needed, use reactive polling to wait for Read Model updates.
233
- - **Subscribing event processor** (e.g., Axon's `SubscribingEventProcessor`): relies on local event bus subscription, so only the instance that emitted the event receives it. In distributed environments, other instances' Projections are not updated. Use PubSub to distribute events to all instances.
623
+ - **Subscription Query** (for example Axon's `subscriptionQuery()`): a mechanism that returns change notifications for query results to the subscriber. Use it only when it is already adopted as infrastructure and delivery to subscribers is guaranteed. In systems based on tracking processors or trackers, do not introduce subscription query only for a feature implementation.
624
+ - **Subscribing event processor** (for example Axon's `SubscribingEventProcessor`): depends on direct subscription from the local event bus, so only the instance that published the event receives it. In distributed environments, projections on other instances are not updated. Configure PubSub delivery to all instances.
234
625
 
235
626
  | Criteria | Judgment |
236
627
  |----------|----------|
237
- | Using Subscription Query (e.g., Axon's `subscriptionQuery()`) | REJECT. Does not work in distributed environments. Use reactive polling |
238
- | Using Subscribing event processor (e.g., Axon's `SubscribingEventProcessor`) | REJECT. Local delivery only. Other instances not updated in distributed environments |
239
- | Controller directly referencing Repository | REJECT. Must go through UseCase layer |
240
- | Query side referencing Command Model | REJECT |
241
- | QueryHandler issuing commands | REJECT |
628
+ | Use of Subscription Query already adopted as infrastructure with confirmed delivery guarantee, such as Axon Server `subscriptionQuery()` | OK |
629
+ | Introducing Subscription Query only for a feature implementation | REJECT. Use the existing tracker / Read Model polling |
630
+ | Use of Subscription Query with unknown delivery guarantee, such as Axon `subscriptionQuery()` | REJECT. Use the existing tracker / Read Model polling |
631
+ | Use of Subscribing event processor, such as Axon `SubscribingEventProcessor` | REJECT. Local delivery only; other instances are not updated in distributed environments |
632
+ | Controller directly references Repository | REJECT. Go through UseCase layer |
633
+ | Query side references Command Model | REJECT |
634
+ | QueryHandler dispatches commands | REJECT |
242
635
  | Query-side service or handler saves, deletes, or calls external APIs | REJECT |
243
- | Command and Query responsibilities mixed in the same service | REJECT. Separate responsibility and naming |
244
- | Query side checks existence or scope and caller dispatches command | OK |
636
+ | Command and Query are mixed in the same service | REJECT. Separate responsibilities and naming |
637
+ | Query side or ReadService reads Query results to choose the command type for the same Aggregate | REJECT |
638
+ | Query side checks existence/scope of another Aggregate or external facts, and the caller dispatches one command | OK |
245
639
 
246
640
  ### QueryHandler and ApplicationService Naming
247
641
 
248
- In CQRS, the component that receives a query is the QueryHandler, and the entrypoint for dispatching queries is the QueryGateway / QueryBus. A facade called by Controllers for read use cases should be named ApplicationService or ReadService so it is not confused with a QueryHandler.
642
+ In CQRS, the component that receives queries is called a QueryHandler, and the entry point that sends queries is treated as QueryGateway / QueryBus. A facade called from a Controller to coordinate read use cases should be named ApplicationService or ReadService so it is not confused with QueryHandler.
249
643
 
250
644
  | Criteria | Judgment |
251
645
  |----------|----------|
252
- | Receives a Query, reads the Read Model, and returns a query result type | QueryHandler |
253
- | Coordinates multiple Queries, authorization boundaries, pagination, or DTO assembly for Controllers | ApplicationService or ReadService |
254
- | Class that only dispatches queries or coordinates reads is named QueryService | Warning. Easy to confuse with QueryHandler |
255
- | QueryHandler knows HTTP requests/responses or Controller-specific error translation | REJECT |
256
- | Simple read wrapper with no additional decision-making | Consider deleting. Controller may call QueryGateway directly |
646
+ | Receives a Query, references Read Model, and returns a query-result type | QueryHandler |
647
+ | Coordinates multiple Queries, authorization boundary, paging, and DTO assembly from Controller | ApplicationService or ReadService |
648
+ | A class that only sends queries or coordinates reads is called QueryService | Warning. Easy to confuse with QueryHandler |
649
+ | QueryHandler knows HTTP request/response or Controller-specific error conversion | REJECT |
650
+ | Adds a simple read wrapper with no additional decision | Consider removing. Controller may call QueryGateway directly |
257
651
 
258
652
  Types between layers:
259
- - `application/query/` - Query result types (e.g., `OrderDetail`)
260
- - `adapter/protocol/` - REST response types (e.g., `OrderDetailResponse`)
261
- - QueryHandler returns application layer types, Controller converts to adapter layer types
653
+ - `application/query/` - query-result type, for example `OrderDetail`
654
+ - `adapter/protocol/` - REST response type, for example `OrderDetailResponse`
655
+ - QueryHandler returns application-layer types; Controller converts them to adapter-layer types
262
656
 
263
657
  ```kotlin
264
658
  // application/query/OrderDetail.kt
@@ -275,14 +669,14 @@ data class OrderDetailResponse(...) {
275
669
  }
276
670
  }
277
671
 
278
- // QueryHandler - returns application layer type
672
+ // QueryHandler - returns application-layer type
279
673
  @QueryHandler
280
674
  fun handle(query: GetOrderDetailQuery): OrderDetail? {
281
675
  val entity = repository.findById(query.id) ?: return null
282
676
  return OrderDetail(...)
283
677
  }
284
678
 
285
- // Controller - synchronous return is fine for simple reads
679
+ // Controller - simple reference can return synchronously
286
680
  @GetMapping("/{id}")
287
681
  fun getById(@PathVariable id: String): ResponseEntity<OrderDetailResponse> {
288
682
  val detail = queryGateway.query(
@@ -296,45 +690,57 @@ fun getById(@PathVariable id: String): ResponseEntity<OrderDetailResponse> {
296
690
 
297
691
  Structure:
298
692
  ```
299
- Controller (adapter) QueryGateway QueryHandler (application) Repository
300
-
693
+ Controller (adapter) -> QueryGateway -> QueryHandler (application) -> Repository
694
+ | |
301
695
  Response.from(detail) OrderDetail
302
696
 
303
697
  Event flow (PubSub):
304
- Aggregate Event Bus Projection(@EventHandler) Repository(Read Model)
305
-
306
- QueryHandler reads from here
698
+ Aggregate -> Event Bus -> Projection(@EventHandler) -> Repository(Read Model)
699
+ ^
700
+ QueryHandler references this
307
701
  ```
308
702
 
309
- ### Async Callbacks and Concurrency Control
703
+ ### Asynchronous Callbacks and Concurrency Control
310
704
 
311
- Completion notifications for asynchronous work must assume duplicates, delays, and reordering. Protect the workflow with Aggregate state transitions and command idempotency, not Controller-level or single-process locks.
705
+ Design asynchronous completion callbacks assuming duplicates, delays, and ordering inversions. Protect with Aggregate state transitions and command idempotency, not Controller or single-process locks.
312
706
 
313
707
  | Criteria | Judgment |
314
708
  |----------|----------|
315
- | Controller or application-process lock prevents duplicate callbacks | REJECT. It does not work across multiple instances |
316
- | Aggregate state decides whether work is processing | OK |
317
- | Aggregate verifies callback attempt/generation identifiers | OK |
318
- | Stale or duplicate callbacks are idempotently ignored by state transition | OK |
709
+ | Prevent duplicate callbacks with Controller or application-process locks | REJECT. Does not work across instances |
710
+ | Determine processing state from Aggregate state | OK |
711
+ | Aggregate verifies callback attempt ID or generation | OK |
712
+ | Idempotently ignore old or duplicate callbacks by state transition | OK |
319
713
  | Concurrency control is duplicated across Controller, UseCase, and Aggregate | REJECT |
320
714
 
321
715
  ## Eventual Consistency
322
716
 
323
- When synchronous response is needed after command dispatch, use reactive polling to wait for Projection updates.
717
+ Wait for Projection updates after command dispatch only when there is an explicit synchronous contract to return the updated Read Model in the same API response. If the client can keep the input values or generated ID, the server should not wait; Read Model convergence is handled through normal read APIs.
324
718
 
325
719
  | Criteria | Judgment |
326
720
  |----------|----------|
327
- | Using Subscription Query to wait for Projection updates | REJECT. Does not work in distributed environments. Use reactive polling |
328
- | UI expects immediate updates | Polling or WebSocket |
329
- | Consistency delay exceeds tolerance | Reconsider architecture |
330
- | Compensating transactions undefined | Request failure scenario review |
721
+ | No explicit contract to return the updated Read Model in the same response | Do not wait |
722
+ | The client or caller can keep the command input or generated ID | Do not wait |
723
+ | There is infrastructure guaranteeing Projection update notification delivery to the waiting process | OK. Notification-driven waiting is acceptable |
724
+ | Existing infrastructure such as Subscription Query confirms update notifications reach subscribers | OK |
725
+ | Kafka or similar guarantees destination, redelivery, and missing-message handling operationally | OK |
726
+ | Subscription Query or event notification destination assumes single process/single instance, or guarantee is unknown | REJECT. Use the existing tracker / Read Model polling |
727
+ | `Thread.sleep` or equivalent blocks request threads while waiting for Projection updates | REJECT. Causes thread starvation under high concurrency |
728
+ | `delayedExecutor` / `CompletableFuture` is used to implement custom Projection-wait retry | REJECT. Use a reactive HTTP stack or the existing tracker |
729
+ | processStore / ProcessStore / materialStore / completeStep manages Projection update progress | REJECT. Projections should update idempotently from events |
730
+ | Updated state must be returned in the same HTTP response | Wait non-blockingly on a reactive HTTP stack |
731
+ | Same response does not need to wait | `202 Accepted` plus frontend long polling, normal polling, SSE, or WebSocket |
732
+ | UI expects immediate update | Frontend polling, SSE, or WebSocket. Server-side waiting only for a synchronous API contract |
733
+ | Consistency delay exceeds acceptable range | Reconsider architecture |
734
+ | Compensation transaction is undefined | Require failure-scenario review |
331
735
 
332
736
  ### Reactive Polling
333
737
 
334
- Pattern: dispatch command poll for Projection update completion.
738
+ Reactive polling is the pattern of dispatching a command and then waiting non-blockingly for Projection update completion. It does not occupy a request thread and is not a synchronous `while` loop with `Thread.sleep`.
739
+
740
+ The polling condition should be checked by re-fetching the Read Model and testing whether it has reached the expected state, not by event notifications. Re-fetch at a fixed interval until the condition is met, timeout occurs, or max attempts are reached.
335
741
 
336
742
  ```kotlin
337
- // UseCase: send command poll for completion
743
+ // UseCase: command dispatch -> wait for completion with polling
338
744
  fun execute(input: PlaceOrderInput): Mono<PlaceOrderOutput> {
339
745
  val orderId = UUID.randomUUID().toString()
340
746
  return Mono.fromCallable { validatePreConditions(input) }
@@ -359,40 +765,56 @@ private fun pollForCompletion(orderId: String): Mono<Void> {
359
765
  }
360
766
  ```
361
767
 
362
- When polling is appropriate:
363
- - Need to wait for Saga completion before returning response
364
- - Need to return created resource ID after command dispatch
768
+ Avoid blocking waits:
365
769
 
366
- When polling is not needed:
367
- - Simple operations that complete with just command dispatch (no result waiting)
368
- - UI does not require real-time updates
770
+ ```kotlin
771
+ // NG - Occupies request threads and causes thread starvation under load
772
+ while (Instant.now().isBefore(deadline)) {
773
+ val order = orderRepository.findById(orderId).orElse(null)
774
+ if (order?.status == OrderStatus.CONFIRMED) return PlaceOrderOutput(orderId)
775
+ Thread.sleep(100)
776
+ }
777
+
778
+ // OK - If the same response must wait, put it on reactive waiting
779
+ return pollForCompletion(orderId).thenReturn(PlaceOrderOutput(orderId))
780
+ ```
781
+
782
+ Cases where polling is appropriate:
783
+ - The response should not return until Saga completion
784
+ - The command dispatch creates a resource ID and the response needs to return it
785
+
786
+ Cases where polling is unnecessary:
787
+ - A simple operation where command dispatch alone completes the work and the result is not waited on
788
+ - The UI does not need real-time update
789
+
790
+ If the server does not wait, return `202 Accepted` with a tracking ID after accepting the command, and let the frontend use long polling or normal polling on the read API. SSE or WebSocket can also be considered if the user experience requires immediacy.
369
791
 
370
792
  ## Saga vs EventHandler
371
793
 
372
- Saga is used only for "operations between multiple aggregates where contention occurs".
794
+ Use Saga only for operations involving contention between multiple Aggregates.
373
795
 
374
- Cases where Saga is needed:
796
+ Cases that need Saga:
375
797
  ```
376
- When multiple actors compete for the same resource
377
- Example: Inventory reservation (10 people ordering the same product simultaneously)
798
+ Multiple actors compete for the same resource
799
+ Example: inventory reservation, where 10 people order the same product at the same time
378
800
 
379
801
  OrderPlacedEvent
380
- InventoryReservationSaga
381
- ReserveInventoryCommand Inventory aggregate (serializes concurrent execution)
382
-
383
- InventoryReservedEvent ConfirmOrderCommand
384
- InventoryReservationFailedEvent CancelOrderCommand
802
+ -> InventoryReservationSaga
803
+ ReserveInventoryCommand -> Inventory Aggregate (serializes concurrency)
804
+ ->
805
+ InventoryReservedEvent -> ConfirmOrderCommand
806
+ InventoryReservationFailedEvent -> CancelOrderCommand
385
807
  ```
386
808
 
387
- Cases where Saga is not needed:
809
+ Cases that do not need Saga:
388
810
  ```
389
- Non-competing operations
390
- Example: Inventory release on order cancellation
811
+ Operation with no contention
812
+ Example: releasing inventory on order cancellation
391
813
 
392
814
  OrderCancelledEvent
393
- InventoryReleaseHandler (simple EventHandler)
815
+ -> InventoryReleaseHandler (simple EventHandler)
394
816
  ReleaseInventoryCommand
395
-
817
+ ->
396
818
  InventoryReleasedEvent
397
819
  ```
398
820
 
@@ -401,35 +823,35 @@ Decision criteria:
401
823
  | Situation | Saga | EventHandler |
402
824
  |-----------|------|--------------|
403
825
  | Resource contention exists | Use | - |
404
- | Compensating transaction needed | Use | - |
405
- | Non-competing simple coordination | - | Use |
406
- | Retry on failure is sufficient | - | Use |
826
+ | Compensation transaction is needed | Use | - |
827
+ | Simple integration with no contention | - | Use |
828
+ | Retry is enough on failure | - | Use |
407
829
 
408
830
  Anti-pattern:
409
831
  ```kotlin
410
832
  // NG - Using Saga for lifecycle management
411
833
  @Saga
412
834
  class OrderLifecycleSaga {
413
- // Tracking all order state transitions in Saga
414
- // PLACED CONFIRMED SHIPPED DELIVERED
835
+ // Tracks every order state transition
836
+ // PLACED -> CONFIRMED -> SHIPPED -> DELIVERED
415
837
  }
416
838
 
417
- // OK - Saga only for operations requiring eventual consistency
839
+ // OK - Saga only for operations that need eventual consistency
418
840
  @Saga
419
841
  class InventoryReservationSaga {
420
- // Only for inventory reservation concurrency control
842
+ // Concurrency control for inventory reservation only
421
843
  }
422
844
  ```
423
845
 
424
- Saga is not a lifecycle management tool. Create it per "operation" that requires eventual consistency.
846
+ Saga is not a lifecycle-management tool. Create it for an operation that needs eventual consistency.
425
847
 
426
- ## Exception vs Event (Failure Handling)
848
+ ## Exception vs Event: Failure Choice
427
849
 
428
- Failures not requiring audit use exceptions, failures requiring audit use events.
850
+ Failures that do not require audit are exceptions; failures that require audit are events.
429
851
 
430
- Exception approach (recommended: most cases):
852
+ Exception approach, recommended in most cases:
431
853
  ```kotlin
432
- // Domain model: Throws exception on validation failure
854
+ // Domain model: throw an exception on validation failure
433
855
  fun reserveInventory(orderId: String, quantity: Int): InventoryReservedEvent {
434
856
  if (availableQuantity < quantity) {
435
857
  throw InsufficientInventoryException("Insufficient inventory")
@@ -437,7 +859,7 @@ fun reserveInventory(orderId: String, quantity: Int): InventoryReservedEvent {
437
859
  return InventoryReservedEvent(productId, orderId, quantity)
438
860
  }
439
861
 
440
- // Saga: Catch with exceptionally and perform compensating action
862
+ // Saga: catch with exceptionally and issue compensation action
441
863
  commandGateway.send<Any>(command)
442
864
  .exceptionally { ex ->
443
865
  commandGateway.send<Any>(CancelOrderCommand(
@@ -448,7 +870,7 @@ commandGateway.send<Any>(command)
448
870
  }
449
871
  ```
450
872
 
451
- Event approach (rare cases):
873
+ Event approach, rare cases:
452
874
  ```kotlin
453
875
  // Only when audit is required
454
876
  data class PaymentFailedEvent(
@@ -462,37 +884,37 @@ Decision criteria:
462
884
 
463
885
  | Question | Exception | Event |
464
886
  |----------|-----------|-------|
465
- | Need to check this failure later? | No | Yes |
466
- | Required by regulations/compliance? | No | Yes |
467
- | Only Saga cares about the failure? | Yes | No |
468
- | Is there value in keeping it in Event Store? | No | Yes |
887
+ | Does this failure need to be reviewed later? | No | Yes |
888
+ | Is a record required by regulation or compliance? | No | Yes |
889
+ | Is only the Saga interested in the failure? | Yes | No |
890
+ | Is there value in storing it in the Event Store? | No | Yes |
469
891
 
470
- Default is exception approach. Consider events only when audit requirements exist.
892
+ Default to the exception approach. Consider events only when there is an audit requirement.
471
893
 
472
- ## Abstraction Level Evaluation
894
+ ## Abstraction-level Evaluation
473
895
 
474
- **Conditional branch proliferation detection:**
896
+ **Detecting bloated conditional branches**
475
897
 
476
898
  | Pattern | Judgment |
477
899
  |---------|----------|
478
- | Same if-else pattern in 3+ places | Abstract with polymorphism REJECT |
479
- | switch/case with 5+ branches | Consider Strategy/Map pattern |
480
- | Event type branching proliferating | Separate event handlers REJECT |
481
- | Complex state branching in Aggregate | Consider State Pattern |
900
+ | Same if-else pattern appears in 3 or more places | Abstract with polymorphism -> REJECT |
901
+ | switch/case has 5 or more branches | Consider Strategy/Map pattern |
902
+ | Branching by event type grows repeatedly | Split EventHandlers -> REJECT |
903
+ | State branching inside Aggregate is complex | Consider State Pattern |
482
904
 
483
- **Abstraction level mismatch detection:**
905
+ **Detecting mismatched abstraction levels**
484
906
 
485
907
  | Pattern | Problem | Fix |
486
908
  |---------|---------|-----|
487
- | DB operation details in CommandHandler | Responsibility violation | Separate to Repository layer |
909
+ | DB operation details in CommandHandler | Responsibility violation | Separate into Repository layer |
488
910
  | Business logic in EventHandler | Responsibility violation | Extract to domain service |
489
- | Persistence in Aggregate | Layer violation | Change to EventStore route |
911
+ | Persistence processing in Aggregate | Layer violation | Move behind EventStore |
490
912
  | Calculation logic in Projection | Hard to maintain | Extract to dedicated service |
491
913
 
492
914
  Good abstraction examples:
493
915
 
494
916
  ```kotlin
495
- // Event type branching proliferation (NG)
917
+ // Event-type branching grows repeatedly (NG)
496
918
  @EventHandler
497
919
  fun on(event: DomainEvent) {
498
920
  when (event) {
@@ -503,7 +925,7 @@ fun on(event: DomainEvent) {
503
925
  }
504
926
  }
505
927
 
506
- // Separate handlers per event (OK)
928
+ // Split by event (OK)
507
929
  @EventHandler
508
930
  fun on(event: OrderPlacedEvent) { ... }
509
931
 
@@ -520,11 +942,11 @@ fun process(command: ProcessCommand) {
520
942
  when (status) {
521
943
  PENDING -> if (command.type == "approve") { ... } else if (command.type == "reject") { ... }
522
944
  APPROVED -> if (command.type == "ship") { ... }
523
- // ...gets complex
945
+ // ...complexity grows
524
946
  }
525
947
  }
526
948
 
527
- // Abstracted with State Pattern (OK)
949
+ // State Pattern (OK)
528
950
  sealed class OrderState {
529
951
  abstract fun handle(command: ProcessCommand): List<DomainEvent>
530
952
  }
@@ -539,37 +961,37 @@ class PendingState : OrderState() {
539
961
 
540
962
  ## Anti-pattern Detection
541
963
 
542
- REJECT if found:
964
+ Reject when any of the following is found:
543
965
 
544
966
  | Anti-pattern | Problem |
545
967
  |--------------|---------|
546
- | CRUD Disguise | Just splitting CRUD into Command/Query |
547
- | Anemic Domain Model | Aggregate is just a data structure |
548
- | Event Soup | Meaningless events proliferate |
968
+ | CRUD disguise | Only mimics the shape of CQRS while implementing CRUD |
969
+ | Anemic Domain Model | Aggregate is only a data structure |
970
+ | Event Soup | Meaningless events are emitted repeatedly |
549
971
  | Temporal Coupling | Implicit dependency on event order |
550
972
  | Missing Events | Important domain events are missing |
551
- | God Aggregate | All responsibilities in one Aggregate |
973
+ | God Aggregate | One Aggregate concentrates all responsibilities |
552
974
 
553
975
  ## Test Strategy
554
976
 
555
- Separate test strategies by layer.
977
+ Separate test strategy by layer.
556
978
 
557
- Test Pyramid:
979
+ Test pyramid:
558
980
  ```
559
- ┌─────────────┐
560
- E2E Test Few: Overall flow confirmation
561
- ├─────────────┤
562
- Integration CommandEventProjectionQuery coordination
563
- ├─────────────┤
564
- Unit Test Many: Each layer tested independently
565
- └─────────────┘
981
+ +-------------+
982
+ | E2E Test | <- Few: full-flow confirmation
983
+ +-------------+
984
+ | Integration | <- Command -> Event -> Projection -> Query integration
985
+ +-------------+
986
+ | Unit Test | <- Many: each layer isolated
987
+ +-------------+
566
988
  ```
567
989
 
568
990
  Command side (Aggregate):
569
991
  ```kotlin
570
992
  // Using AggregateTestFixture
571
993
  @Test
572
- fun `confirm command emits event`() {
994
+ fun `confirmation command emits event`() {
573
995
  fixture
574
996
  .given(OrderPlacedEvent(...))
575
997
  .`when`(ConfirmOrderCommand(orderId, confirmedBy))
@@ -582,11 +1004,11 @@ Query side:
582
1004
  ```kotlin
583
1005
  // Direct Read Model setup + QueryGateway
584
1006
  @Test
585
- fun `can get order details`() {
586
- // Given: Setup Read Model directly
1007
+ fun `order detail is returned`() {
1008
+ // Given: set up Read Model directly
587
1009
  orderRepository.save(OrderEntity(...))
588
1010
 
589
- // When: Execute query via QueryGateway
1011
+ // When: execute query through QueryGateway
590
1012
  val detail = queryGateway.query(GetOrderDetailQuery(orderId), ...).join()
591
1013
 
592
1014
  // Then
@@ -596,41 +1018,102 @@ fun `can get order details`() {
596
1018
 
597
1019
  Checklist:
598
1020
 
599
- | Aspect | Judgment |
600
- |--------|----------|
601
- | Aggregate tests verify events not state | Required |
602
- | Query side tests don't create data via Command | Recommended |
603
- | Integration tests consider Axon async processing | Required |
1021
+ | Perspective | Judgment |
1022
+ |-------------|----------|
1023
+ | Aggregate tests verify events, not state | Required |
1024
+ | Query-side tests do not create data through commands | Recommended |
1025
+ | Integration tests account for Axon asynchronous processing | Required |
1026
+
1027
+ ## Value Object Design
1028
+
1029
+ Use value objects as Aggregate and event components. Do not rely only on primitive types such as String or Int.
1030
+
1031
+ ```kotlin
1032
+ // NG - primitives only
1033
+ data class OrderPlacedEvent(
1034
+ val orderId: String,
1035
+ val categoryId: String, // Just a string
1036
+ val from: LocalDateTime, // Meaning is unclear
1037
+ val to: LocalDateTime
1038
+ )
1039
+
1040
+ // OK - Value objects express meaning and constraints
1041
+ data class OrderPlacedEvent(
1042
+ val orderId: String,
1043
+ val categoryId: CategoryId,
1044
+ val period: OrderPeriod
1045
+ )
1046
+ ```
1047
+
1048
+ Value object design rules:
1049
+ - Use `data class` to auto-generate equals/hashCode, comparing by value
1050
+ - Guarantee invariants in `init` blocks, validating at creation
1051
+ - Do not include domain logic such as state transitions; keep them as pure data holders
1052
+ - Use `@JsonValue` to control serialization
1053
+
1054
+ ```kotlin
1055
+ // ID type: single-value wrapper
1056
+ data class CategoryId(@get:JsonValue val value: String) {
1057
+ init {
1058
+ require(value.isNotBlank()) { "Category ID cannot be blank" }
1059
+ }
1060
+ override fun toString(): String = value
1061
+ }
1062
+
1063
+ // Range type: invariant over multiple values
1064
+ data class OrderPeriod(
1065
+ val from: LocalDateTime,
1066
+ val to: LocalDateTime
1067
+ ) {
1068
+ init {
1069
+ require(!to.isBefore(from)) { "End date must be on or after start date" }
1070
+ }
1071
+ }
604
1072
 
605
- ## Master Data and CRUD
1073
+ // Metadata type: associated data in event payload
1074
+ data class ApprovalInfo(
1075
+ val approvedBy: String,
1076
+ val approvalTime: LocalDateTime
1077
+ )
1078
+ ```
606
1079
 
607
- Not everything in a CQRS+ES system needs event sourcing. Master data (reference data) with simple characteristics is better implemented as plain CRUD — it's simpler and easier to maintain.
1080
+ | Criteria | Judgment |
1081
+ |----------|----------|
1082
+ | Reusing IDs as raw String | Consider value object |
1083
+ | Same field combination, such as from/to, appears in multiple places | Extract value object |
1084
+ | Value object contains business logic such as state transitions | REJECT. Aggregate responsibility |
1085
+ | No `init` block to guarantee invariants | REJECT |
1086
+
1087
+ ## Master Data, Settings, and CRUD Use
608
1088
 
609
- However, don't mechanically decide "it's master data, so CRUD". The more criteria below that apply, the more CRUD is suitable. Conversely, if even one requirement calls for CQRS+ES, consider adopting it.
1089
+ Even inside a CQRS+ES system, not everything needs to be event-sourced. Simple master data, reference data, managed settings, and allowlists are often simpler and easier to maintain as normal CRUD.
610
1090
 
611
- **Criteria for determining CRUD is sufficient:**
1091
+ Do not mechanically decide "master data means CRUD". The more the following criteria apply, the more suitable CRUD is. Conversely, if explicit requirements match CQRS+ES adoption criteria, consider CQRS+ES.
612
1092
 
613
- | Aspect | Leans CRUD | Leans CQRS+ES |
614
- |--------|-----------|---------------|
615
- | Business requirements | Just "manage X" with no special mentions | Specific business rules or constraints |
616
- | Logic evolution | Simple reference/update, no foreseeable complexity | State transitions or lifecycle may grow complex |
617
- | Change history / audit | No need to track "who changed what when" | Change history or audit trail required |
618
- | Domain events | Changes don't affect other aggregates or processes | Changes trigger downstream processes |
619
- | Consistency scope | Self-contained, no cross-aggregate consistency needed | Must maintain consistency with other aggregates |
620
- | Point-in-time queries | No "what was the state at time T" queries | Point-in-time queries required |
1093
+ **Criteria for deciding CRUD is enough:**
621
1094
 
622
- **Typical CRUD candidates:**
623
- - Code masters such as prefecture/country codes
624
- - Classification masters such as categories and tags
625
- - Configuration values, constant tables
1095
+ | Perspective | CRUD-leaning | CQRS+ES-leaning |
1096
+ |-------------|--------------|-----------------|
1097
+ | Business requirement | Around "manage X" with no special mention | Specific business rules or constraints exist |
1098
+ | Logic evolution | Simple reference/update completes it, unlikely to evolve | State transitions or lifecycle can become complex |
1099
+ | Change history/audit | No need to track who changed what and when | Need change-history reference or audit evidence |
1100
+ | Domain events | This change does not affect other Aggregates or processes | Change triggers downstream processes |
1101
+ | Consistency scope | Self-contained, no need for consistency with other Aggregates | Needs consistency with other Aggregates |
1102
+ | Point-in-time reference | No question asks for "state at a past point" | Point-in-time queries are needed |
626
1103
 
627
- **Cases where CQRS+ES is justified:**
628
- - Product master, but price change history tracking is needed
629
- - Organization master, but changes trigger permission recalculation
630
- - Business partner master, but has credit assessment state transitions
1104
+ **Typical CRUD targets:**
1105
+ - Prefecture and country-code master data
1106
+ - Category and tag classification master data
1107
+ - Settings and constant tables
1108
+ - Current-value managed settings such as IP allowlists, feature flags, and notification settings
1109
+
1110
+ **Examples where CQRS+ES can be justified:**
1111
+ - Product master data where price-change history must be tracked
1112
+ - Organization master data where changes trigger permission recalculation
1113
+ - Customer/vendor master data with credit-review state transitions
631
1114
 
632
1115
  ```kotlin
633
- // CRUD is sufficient: Simple category master
1116
+ // CRUD is enough: simple category master
634
1117
  @Entity
635
1118
  data class Category(
636
1119
  @Id val categoryId: String,
@@ -638,7 +1121,7 @@ data class Category(
638
1121
  val displayOrder: Int
639
1122
  )
640
1123
 
641
- // CQRS+ES is appropriate: Product with price change history tracking
1124
+ // CQRS+ES is appropriate: product requiring price-change history
642
1125
  data class Product(
643
1126
  val productId: String,
644
1127
  val currentPrice: Money
@@ -650,12 +1133,12 @@ data class Product(
650
1133
  }
651
1134
  ```
652
1135
 
653
- Even when implementing with CRUD, other aggregates in the CQRS+ES system reference CRUD entities by ID. The principle that CRUD entities don't directly access aggregate internal state still applies.
1136
+ When implementing with CRUD, other Aggregates in a CQRS+ES system should still reference it by ID. It is the same principle that CRUD entities must not directly reference Aggregate internal state.
654
1137
 
655
1138
  ## Infrastructure Layer
656
1139
 
657
- Check:
658
- - Is event store choice appropriate?
659
- - Does messaging infrastructure meet requirements?
660
- - Is snapshot strategy defined?
661
- - Is event serialization format appropriate?
1140
+ Checklist:
1141
+ - Is the event-store choice appropriate?
1142
+ - Does the messaging infrastructure satisfy requirements?
1143
+ - Is the snapshot strategy defined?
1144
+ - Is the event serialization format appropriate?