@eventmodelers/cli 0.0.1

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 (220) hide show
  1. package/README.md +175 -0
  2. package/cli.js +676 -0
  3. package/package.json +35 -0
  4. package/shared/build-kit/code-export.mjs +560 -0
  5. package/shared/build-kit/lib/ollama-agent.js +147 -0
  6. package/shared/build-kit/package.json +11 -0
  7. package/shared/build-kit/ralph-ollama.js +39 -0
  8. package/shared/build-kit/realtime-agent.js +18 -0
  9. package/stacks/axon/templates/.claude/skills/build-automation/SKILL.md +407 -0
  10. package/stacks/axon/templates/.claude/skills/build-automation/references/feature-flag-patterns.md +114 -0
  11. package/stacks/axon/templates/.claude/skills/build-automation-workflow/SKILL.md +496 -0
  12. package/stacks/axon/templates/.claude/skills/build-automation-workflow/references/axon-workflow-api.md +279 -0
  13. package/stacks/axon/templates/.claude/skills/build-automation-workflow/references/feature-flag-patterns.md +114 -0
  14. package/stacks/axon/templates/.claude/skills/build-state-change/SKILL.md +396 -0
  15. package/stacks/axon/templates/.claude/skills/build-state-change/references/axon-test-fixture-patterns.md +162 -0
  16. package/stacks/axon/templates/.claude/skills/build-state-change/references/feature-flag-patterns.md +56 -0
  17. package/stacks/axon/templates/.claude/skills/build-state-change/references/rest-api-patterns.md +115 -0
  18. package/stacks/axon/templates/.claude/skills/build-state-view/SKILL.md +282 -0
  19. package/stacks/axon/templates/.claude/skills/connect/SKILL.md +178 -0
  20. package/stacks/axon/templates/.claude/skills/learn-eventmodelers-api/SKILL.md +611 -0
  21. package/stacks/axon/templates/.claude/skills/load-slice/SKILL.md +141 -0
  22. package/stacks/axon/templates/.claude/skills/update-slice-status/SKILL.md +105 -0
  23. package/stacks/axon/templates/build-kit/lib/AGENT.md +47 -0
  24. package/stacks/axon/templates/build-kit/lib/agent.sh +20 -0
  25. package/stacks/axon/templates/build-kit/lib/backend-prompt.md +158 -0
  26. package/stacks/axon/templates/build-kit/lib/prompt.md +126 -0
  27. package/stacks/axon/templates/build-kit/lib/ralph.js +369 -0
  28. package/stacks/axon/templates/build-kit/ralph-claude.js +44 -0
  29. package/stacks/axon/templates/build-kit/ralph.sh +98 -0
  30. package/stacks/axon/templates/root/.env.example +5 -0
  31. package/stacks/axon/templates/root/CLAUDE.md +60 -0
  32. package/stacks/axon/templates/root/README.md +44 -0
  33. package/stacks/axon/templates/root/docker-compose.yml +64 -0
  34. package/stacks/axon/templates/root/mvnw +259 -0
  35. package/stacks/axon/templates/root/mvnw.cmd +149 -0
  36. package/stacks/axon/templates/root/pom.xml +125 -0
  37. package/stacks/axon/templates/root/src/main/java/io/axoniq/quickstart/QuickstartApplication.java +128 -0
  38. package/stacks/axon/templates/root/src/main/java/io/axoniq/quickstart/config/WebConfig.java +155 -0
  39. package/stacks/axon/templates/root/src/main/resources/application.properties +2 -0
  40. package/stacks/axon/templates/root/src/main/resources/static/bg-body.png +0 -0
  41. package/stacks/axon/templates/root/src/main/resources/static/bg-header.png +0 -0
  42. package/stacks/axon/templates/root/src/main/resources/static/index.html +596 -0
  43. package/stacks/cratis-csharp/templates/.claude/skills/_shared/cratis-conventions.md +251 -0
  44. package/stacks/cratis-csharp/templates/.claude/skills/build-automation/SKILL.md +99 -0
  45. package/stacks/cratis-csharp/templates/.claude/skills/build-automation/references/patterns.md +115 -0
  46. package/stacks/cratis-csharp/templates/.claude/skills/build-state-change/SKILL.md +163 -0
  47. package/stacks/cratis-csharp/templates/.claude/skills/build-state-change/references/patterns.md +234 -0
  48. package/stacks/cratis-csharp/templates/.claude/skills/build-state-view/SKILL.md +120 -0
  49. package/stacks/cratis-csharp/templates/.claude/skills/build-state-view/references/patterns.md +166 -0
  50. package/stacks/cratis-csharp/templates/.claude/skills/connect/SKILL.md +169 -0
  51. package/stacks/cratis-csharp/templates/.claude/skills/learn-eventmodelers-api/SKILL.md +609 -0
  52. package/stacks/cratis-csharp/templates/.claude/skills/load-slice/SKILL.md +141 -0
  53. package/stacks/cratis-csharp/templates/.claude/skills/update-slice-status/SKILL.md +105 -0
  54. package/stacks/cratis-csharp/templates/build-kit/lib/AGENT.md +58 -0
  55. package/stacks/cratis-csharp/templates/build-kit/lib/agent.sh +20 -0
  56. package/stacks/cratis-csharp/templates/build-kit/lib/backend-prompt.md +129 -0
  57. package/stacks/cratis-csharp/templates/build-kit/lib/prompt.md +124 -0
  58. package/stacks/cratis-csharp/templates/build-kit/lib/ralph.js +302 -0
  59. package/stacks/cratis-csharp/templates/build-kit/ralph-claude.js +37 -0
  60. package/stacks/cratis-csharp/templates/build-kit/ralph.sh +98 -0
  61. package/stacks/cratis-csharp/templates/root/.frontend/index.css +29 -0
  62. package/stacks/cratis-csharp/templates/root/.frontend/index.html +17 -0
  63. package/stacks/cratis-csharp/templates/root/.frontend/main.tsx +18 -0
  64. package/stacks/cratis-csharp/templates/root/.frontend/tsconfig.json +42 -0
  65. package/stacks/cratis-csharp/templates/root/.frontend/tsconfig.node.json +11 -0
  66. package/stacks/cratis-csharp/templates/root/.frontend/vite.config.ts +56 -0
  67. package/stacks/cratis-csharp/templates/root/App.tsx +23 -0
  68. package/stacks/cratis-csharp/templates/root/CLAUDE.md +62 -0
  69. package/stacks/cratis-csharp/templates/root/CratisApp.csproj +25 -0
  70. package/stacks/cratis-csharp/templates/root/CratisApp.sln +18 -0
  71. package/stacks/cratis-csharp/templates/root/GlobalUsings.cs +3 -0
  72. package/stacks/cratis-csharp/templates/root/Home.tsx +102 -0
  73. package/stacks/cratis-csharp/templates/root/Program.cs +26 -0
  74. package/stacks/cratis-csharp/templates/root/README.md +192 -0
  75. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Listing/AllListings.ts +47 -0
  76. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Listing/Listing.cs +11 -0
  77. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Listing/Listing.ts +12 -0
  78. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Listing/ListingDataTable.tsx +17 -0
  79. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Listing/index.ts +1 -0
  80. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Registration/Register.ts +51 -0
  81. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Registration/RegisterDialog.tsx +18 -0
  82. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Registration/Registration.cs +27 -0
  83. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/Registration/index.ts +1 -0
  84. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/SomeFeature.tsx +22 -0
  85. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/SomeName.cs +3 -0
  86. package/stacks/cratis-csharp/templates/root/SomeModule/SomeFeature/index.ts +1 -0
  87. package/stacks/cratis-csharp/templates/root/appsettings.Development.json +9 -0
  88. package/stacks/cratis-csharp/templates/root/appsettings.json +26 -0
  89. package/stacks/cratis-csharp/templates/root/docker-compose.yml +23 -0
  90. package/stacks/cratis-csharp/templates/root/package.json +33 -0
  91. package/stacks/cratis-csharp/templates/root/tsconfig.json +3 -0
  92. package/stacks/modeling-kit/templates/.claude/settings.local.json +13 -0
  93. package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/SKILL.md +215 -0
  94. package/stacks/modeling-kit/templates/.claude/skills/attributes/SKILL.md +176 -0
  95. package/stacks/modeling-kit/templates/.claude/skills/connect/SKILL.md +182 -0
  96. package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/SKILL.md +383 -0
  97. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-applying-conways-law/SKILL.md +465 -0
  98. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/SKILL.md +587 -0
  99. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/references/facilitating-event-modeling-workshops.md +626 -0
  100. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/SKILL.md +542 -0
  101. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-event-models/SKILL.md +497 -0
  102. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md +783 -0
  103. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/SKILL.md +604 -0
  104. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/SKILL.md +699 -0
  105. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-integrating-legacy-systems/SKILL.md +674 -0
  106. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/SKILL.md +231 -0
  107. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/domain-patterns.md +150 -0
  108. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/patterns.md +329 -0
  109. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/snapshotting.md +204 -0
  110. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/SKILL.md +352 -0
  111. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/references/project-planning-with-event-modeling.md +341 -0
  112. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/SKILL.md +153 -0
  113. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/SKILL.md +136 -0
  114. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/examples.md +78 -0
  115. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/patterns.md +85 -0
  116. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/SKILL.md +655 -0
  117. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-translating-external-events/SKILL.md +479 -0
  118. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/SKILL.md +234 -0
  119. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/SKILL.md +353 -0
  120. package/stacks/modeling-kit/templates/.claude/skills/examples/SKILL.md +200 -0
  121. package/stacks/modeling-kit/templates/.claude/skills/handle-comment/SKILL.md +94 -0
  122. package/stacks/modeling-kit/templates/.claude/skills/learn-eventmodelers-api/SKILL.md +441 -0
  123. package/stacks/modeling-kit/templates/.claude/skills/place-element/SKILL.md +290 -0
  124. package/stacks/modeling-kit/templates/.claude/skills/storyboard/SKILL.md +212 -0
  125. package/stacks/modeling-kit/templates/.claude/skills/storyboard-screen/SKILL.md +123 -0
  126. package/stacks/modeling-kit/templates/.claude/skills/timeline/SKILL.md +353 -0
  127. package/stacks/modeling-kit/templates/.claude/skills/update-slice-status/SKILL.md +110 -0
  128. package/stacks/modeling-kit/templates/.claude/skills/wdyt/SKILL.md +134 -0
  129. package/stacks/modeling-kit/templates/kit/lib/agent.sh +20 -0
  130. package/stacks/modeling-kit/templates/kit/lib/ollama-agent.js +147 -0
  131. package/stacks/modeling-kit/templates/kit/lib/ralph.js +286 -0
  132. package/stacks/modeling-kit/templates/kit/package.json +12 -0
  133. package/stacks/modeling-kit/templates/kit/ralph-claude.js +50 -0
  134. package/stacks/modeling-kit/templates/kit/ralph-ollama.js +38 -0
  135. package/stacks/modeling-kit/templates/kit/ralph.sh +61 -0
  136. package/stacks/modeling-kit/templates/kit/realtime-agent.js +18 -0
  137. package/stacks/modeling-kit/templates/root/CLAUDE.md +56 -0
  138. package/stacks/node/templates/.claude/skills/build-automation/SKILL.md +266 -0
  139. package/stacks/node/templates/.claude/skills/build-state-change/SKILL.md +344 -0
  140. package/stacks/node/templates/.claude/skills/build-state-view/SKILL.md +412 -0
  141. package/stacks/node/templates/.claude/skills/connect/SKILL.md +178 -0
  142. package/stacks/node/templates/.claude/skills/learn-eventmodelers-api/SKILL.md +628 -0
  143. package/stacks/node/templates/.claude/skills/load-slice/SKILL.md +143 -0
  144. package/stacks/node/templates/.claude/skills/update-slice-status/SKILL.md +110 -0
  145. package/stacks/node/templates/build-kit/README.md +86 -0
  146. package/stacks/node/templates/build-kit/lib/AGENT.md +47 -0
  147. package/stacks/node/templates/build-kit/lib/agent.sh +20 -0
  148. package/stacks/node/templates/build-kit/lib/backend-prompt.md +152 -0
  149. package/stacks/node/templates/build-kit/lib/prompt.md +122 -0
  150. package/stacks/node/templates/build-kit/lib/ralph.js +369 -0
  151. package/stacks/node/templates/build-kit/ralph-claude.js +44 -0
  152. package/stacks/node/templates/build-kit/ralph.sh +98 -0
  153. package/stacks/node/templates/root/.env.example +15 -0
  154. package/stacks/node/templates/root/CLAUDE.md +60 -0
  155. package/stacks/node/templates/root/docker-compose.yml +15 -0
  156. package/stacks/node/templates/root/flyway.conf +17 -0
  157. package/stacks/node/templates/root/migrations/V1__schema.sql.example +12 -0
  158. package/stacks/node/templates/root/package.json +50 -0
  159. package/stacks/node/templates/root/server.ts +130 -0
  160. package/stacks/node/templates/root/setup-env.sh +53 -0
  161. package/stacks/node/templates/root/src/common/assertions.ts +6 -0
  162. package/stacks/node/templates/root/src/common/db.ts +32 -0
  163. package/stacks/node/templates/root/src/common/loadPostgresEventstore.ts +23 -0
  164. package/stacks/node/templates/root/src/common/parseEndpoint.ts +51 -0
  165. package/stacks/node/templates/root/src/common/processorDlq.ts +28 -0
  166. package/stacks/node/templates/root/src/common/realtimeBroadcast.ts +12 -0
  167. package/stacks/node/templates/root/src/common/replay.ts +16 -0
  168. package/stacks/node/templates/root/src/common/routes.ts +19 -0
  169. package/stacks/node/templates/root/src/common/testHelpers.ts +44 -0
  170. package/stacks/node/templates/root/src/swagger.ts +34 -0
  171. package/stacks/node/templates/root/src/util/assertions.ts +6 -0
  172. package/stacks/node/templates/root/src/util/hash.ts +9 -0
  173. package/stacks/node/templates/root/src/util/sanitize.ts +23 -0
  174. package/stacks/node/templates/root/tsconfig.json +32 -0
  175. package/stacks/node/templates/root/vercel.json +8 -0
  176. package/stacks/supabase/templates/.claude/skills/build-automation/SKILL.md +266 -0
  177. package/stacks/supabase/templates/.claude/skills/build-state-change/SKILL.md +344 -0
  178. package/stacks/supabase/templates/.claude/skills/build-state-view/SKILL.md +390 -0
  179. package/stacks/supabase/templates/.claude/skills/build-webhook/SKILL.md +303 -0
  180. package/stacks/supabase/templates/.claude/skills/connect/SKILL.md +178 -0
  181. package/stacks/supabase/templates/.claude/skills/learn-eventmodelers-api/SKILL.md +628 -0
  182. package/stacks/supabase/templates/.claude/skills/load-slice/SKILL.md +143 -0
  183. package/stacks/supabase/templates/.claude/skills/update-slice-status/SKILL.md +110 -0
  184. package/stacks/supabase/templates/build-kit/README.md +86 -0
  185. package/stacks/supabase/templates/build-kit/lib/AGENT.md +47 -0
  186. package/stacks/supabase/templates/build-kit/lib/agent.sh +20 -0
  187. package/stacks/supabase/templates/build-kit/lib/backend-prompt.md +152 -0
  188. package/stacks/supabase/templates/build-kit/lib/prompt.md +123 -0
  189. package/stacks/supabase/templates/build-kit/lib/ralph.js +369 -0
  190. package/stacks/supabase/templates/build-kit/ralph-claude.js +44 -0
  191. package/stacks/supabase/templates/build-kit/ralph.sh +98 -0
  192. package/stacks/supabase/templates/root/.env.example +22 -0
  193. package/stacks/supabase/templates/root/CLAUDE.md +61 -0
  194. package/stacks/supabase/templates/root/flyway.conf +17 -0
  195. package/stacks/supabase/templates/root/package.json +52 -0
  196. package/stacks/supabase/templates/root/server.ts +177 -0
  197. package/stacks/supabase/templates/root/setup-env.sh +53 -0
  198. package/stacks/supabase/templates/root/src/common/assertions.ts +6 -0
  199. package/stacks/supabase/templates/root/src/common/db.ts +32 -0
  200. package/stacks/supabase/templates/root/src/common/loadPostgresEventstore.ts +23 -0
  201. package/stacks/supabase/templates/root/src/common/parseEndpoint.ts +51 -0
  202. package/stacks/supabase/templates/root/src/common/processorDlq.ts +28 -0
  203. package/stacks/supabase/templates/root/src/common/realtimeBroadcast.ts +19 -0
  204. package/stacks/supabase/templates/root/src/common/replay.ts +16 -0
  205. package/stacks/supabase/templates/root/src/common/routes.ts +23 -0
  206. package/stacks/supabase/templates/root/src/common/testHelpers.ts +54 -0
  207. package/stacks/supabase/templates/root/src/supabase/README.md +141 -0
  208. package/stacks/supabase/templates/root/src/supabase/api.ts +56 -0
  209. package/stacks/supabase/templates/root/src/supabase/requireOrgaAdmin.ts +32 -0
  210. package/stacks/supabase/templates/root/src/supabase/requireSysUser.ts +40 -0
  211. package/stacks/supabase/templates/root/src/supabase/requireUser.ts +72 -0
  212. package/stacks/supabase/templates/root/src/swagger.ts +34 -0
  213. package/stacks/supabase/templates/root/src/util/assertions.ts +6 -0
  214. package/stacks/supabase/templates/root/src/util/hash.ts +9 -0
  215. package/stacks/supabase/templates/root/src/util/sanitize.ts +23 -0
  216. package/stacks/supabase/templates/root/supabase/config.toml +295 -0
  217. package/stacks/supabase/templates/root/supabase/migrations/V1__schema.sql.example +12 -0
  218. package/stacks/supabase/templates/root/supabase/seed.sql +1 -0
  219. package/stacks/supabase/templates/root/tsconfig.json +32 -0
  220. package/stacks/supabase/templates/root/vercel.json +8 -0
@@ -0,0 +1,114 @@
1
+ # Feature Flag Patterns
2
+
3
+ How to conditionally enable/disable individual slice components. Examples use a generic `Ordering` bounded context.
4
+
5
+ ---
6
+
7
+ ## Option 1: Spring Boot `@ConditionalOnProperty` (default)
8
+
9
+ Add `@ConditionalOnProperty` to every conditionally-loaded component in the slice. Update config files
10
+ and Spring metadata so the IDE auto-completes property keys.
11
+
12
+ ### Annotation on slice components
13
+
14
+ ```java
15
+ // Entity
16
+ @ConditionalOnProperty(prefix = "slices.ordering", name = "write.placeorder.enabled")
17
+ @EventSourced(tagKey = EventTags.ORDER_ID)
18
+ class PlaceOrderEntity { ... }
19
+
20
+ // Handler
21
+ @ConditionalOnProperty(prefix = "slices.ordering", name = "write.placeorder.enabled")
22
+ @Component
23
+ public class PlaceOrderHandler { ... }
24
+
25
+ // REST controller (if applicable)
26
+ @ConditionalOnProperty(prefix = "slices.ordering", name = "write.placeorder.enabled")
27
+ @RestController
28
+ public class PlaceOrderRestController { ... }
29
+ ```
30
+
31
+ For the **Explicit Registration pattern**, put `@ConditionalOnProperty` on the `@Configuration` class only:
32
+
33
+ ```java
34
+ @ConditionalOnProperty(prefix = "slices.ordering", name = "write.placeorder.enabled")
35
+ @Configuration
36
+ class PlaceOrderWriteSliceConfig { ... }
37
+ ```
38
+
39
+ ### `application.properties` (main — enable by default)
40
+
41
+ ```properties
42
+ slices.ordering.write.placeorder.enabled=true
43
+ slices.ordering.read.getorders.enabled=true
44
+ slices.ordering.automation.notifycustomeronorder.enabled=true
45
+ ```
46
+
47
+ ### `application.properties` (test — disable by default, opt-in per test)
48
+
49
+ ```properties
50
+ slices.ordering.write.placeorder.enabled=false
51
+ slices.ordering.read.getorders.enabled=false
52
+ slices.ordering.automation.notifycustomeronorder.enabled=false
53
+ ```
54
+
55
+ ### Opt-in in a specific test class
56
+
57
+ ```java
58
+ @TestPropertySource(properties = {"slices.ordering.write.placeorder.enabled=true"})
59
+ @SpringBootTest
60
+ class PlaceOrderTest { ... }
61
+ ```
62
+
63
+ ### `META-INF/additional-spring-configuration-metadata.json`
64
+
65
+ Register each property so IDEs auto-complete and validate it:
66
+
67
+ ```json
68
+ {
69
+ "properties": [
70
+ {
71
+ "name": "slices.ordering.write.placeorder.enabled",
72
+ "type": "java.lang.Boolean",
73
+ "description": "Enable/disable the PlaceOrder write slice in the Ordering bounded context."
74
+ },
75
+ {
76
+ "name": "slices.ordering.read.getorders.enabled",
77
+ "type": "java.lang.Boolean",
78
+ "description": "Enable/disable the GetOrders read slice in the Ordering bounded context."
79
+ }
80
+ ]
81
+ }
82
+ ```
83
+
84
+ ---
85
+
86
+ ## Option 2: Spring Profile (`@Profile`)
87
+
88
+ Enable/disable components by activating a named Spring profile. Simpler for "all or nothing" rollouts,
89
+ less granular than `@ConditionalOnProperty`.
90
+
91
+ ```java
92
+ @Profile("ordering-write")
93
+ @Component
94
+ public class PlaceOrderHandler { ... }
95
+ ```
96
+
97
+ Activate in tests via `@ActiveProfiles("ordering-write")` or `spring.profiles.active=ordering-write`.
98
+
99
+ ---
100
+
101
+ ## Option 3: No feature flags
102
+
103
+ If the project ships all slices unconditionally, omit `@ConditionalOnProperty` entirely. Applicable when:
104
+ - The project is small and startup time is not a concern
105
+ - There is no partial-rollout requirement
106
+ - Feature branches are the rollout mechanism
107
+
108
+ ---
109
+
110
+ ## Convention recommendation
111
+
112
+ - Use `@ConditionalOnProperty` for new projects: zero extra dependencies, IDE-friendly, test-friendly.
113
+ - Disable all slices by default in the test properties; opt-in per test class via `@TestPropertySource`.
114
+ - Always enable BOTH the slice under test AND its dependencies (e.g., the automation AND the target write slice).
@@ -0,0 +1,496 @@
1
+ ---
2
+ name: build-automation-workflow
3
+ authors:
4
+ - Martin Dilger
5
+ description: >
6
+ Analyze a list of automation slices and translate appropriate ones into Axon Framework 5 Workflows
7
+ (long-running, durable, multi-step processes) instead of plain @EventHandler automations.
8
+ Determines WHICH slices need a Workflow, then implements them using the AF5 Workflow engine.
9
+ Use when: (1) given a set of automation slice.json definitions, decide which ones should be
10
+ Workflows; (2) user asks to "convert automation to workf
11
+ low", "implement as workflow",
12
+ or "use Axon Workflow for this slice"; (3) a slice has multi-step logic, needs to wait for
13
+ external input/approval, requires compensation/rollback, or spans significant time.
14
+ NOTE: AF5 Workflows are in Preview — APIs may change; not intended for production use yet.
15
+ ---
16
+
17
+ # Build Automation Workflow (Java / Axon Framework 5)
18
+
19
+ > **Preview feature** — AF5 Workflow APIs may change without notice and are not yet for production.
20
+
21
+ New workflow slices live under
22
+ `src/main/java/de/eventmodelers/slices/{context}/automation/{slicename}/`.
23
+
24
+ ---
25
+
26
+ ## Step 0: Discover Project Conventions
27
+
28
+ > **Comments & description**: Each element in the slice carries a `comments: string[]` array and a `description` field. Use these as implementation hints. When done, resolve each used comment: `POST <BASE_URL>/api/org/<ORG_ID>/boards/<BOARD_ID>/nodes/<nodeId>/comments/<commentId>/resolve` (get IDs first via GET on same path).
29
+
30
+ Read `CLAUDE.md` and check whether the project already uses AF5 Workflows. Look for:
31
+
32
+ - A `WorkflowModule` bean in any `@Configuration` class
33
+ - Classes annotated with `@Workflow`
34
+ - Existing `*Workflow.java` files under any slice package
35
+ - The `WorkflowModule` dependency in `pom.xml`:
36
+
37
+ ```xml
38
+ <dependency>
39
+ <groupId>io.axoniq.framework</groupId>
40
+ <artifactId>axoniq-workflows</artifactId>
41
+ <!-- version managed by the BOM -->
42
+ </dependency>
43
+ ```
44
+
45
+ If the module is missing from the pom, add it before implementing any workflow.
46
+
47
+ ---
48
+
49
+ ## Step 1: Analyze Automation Slices — Which Ones Need a Workflow?
50
+
51
+ Read every slice.json under `.build-kit/.slices/{context}/` where `processors` is not empty,
52
+ or where the user specifies a list of slices to analyze.
53
+
54
+ For each automation slice, apply this decision table:
55
+
56
+ | Signal in the slice definition | Decision |
57
+ |---|---|
58
+ | Single trigger event → single command, no waiting | **Plain automation** (`build-automation`) |
59
+ | Multi-step: trigger → command → wait for event → another command | **Workflow** |
60
+ | Process must wait for human approval or external confirmation | **Workflow** |
61
+ | Compensation/rollback: if step N fails, undo steps 1..N-1 | **Workflow** |
62
+ | Steps involve a timer / scheduled delay | **Workflow** |
63
+ | Fan-out: one trigger dispatches to many entities then waits for all | **Workflow** |
64
+ | Parent-child: one process spawns another and waits for its completion | **Workflow** (sub-workflow) |
65
+ | Scatter-gather: send to many, process replies as they arrive | **Workflow** |
66
+
67
+ For each slice, output a classification:
68
+
69
+ ```
70
+ Slice: "ProcessOrderFulfillment" → WORKFLOW (reason: multi-step with compensation)
71
+ Slice: "SendWelcomeEmail" → AUTOMATION (reason: single-step fire-and-forget)
72
+ Slice: "ApproveExpenseReport" → WORKFLOW (reason: human approval with timeout/escalation)
73
+ ```
74
+
75
+ Ask the user to confirm before proceeding if any classification is ambiguous.
76
+
77
+ ---
78
+
79
+ ## Step 2: Ensure Events and Commands Exist
80
+
81
+ For each workflow slice, verify all trigger events and dispatched commands exist. If they don't,
82
+ create them **first** following `build-state-change` Step 4a (sealed event interface + concrete
83
+ event records).
84
+
85
+ ---
86
+
87
+ ## Step 3: Implement the Workflow
88
+
89
+ ### 3a — Annotated workflow class (default)
90
+
91
+ The simplest way to define a workflow using `@Workflow`:
92
+
93
+ ```java
94
+ // File: {slicename}/{SliceName}Workflow.java
95
+ package de.eventmodelers.slices.{context}.automation.{slicename};
96
+
97
+ import org.axonframework.workflow.annotation.Workflow;
98
+ import org.axonframework.workflow.SimpleWorkflowContext;
99
+
100
+ @Workflow(
101
+ idProperty = "{triggerEventIdField}", // field name on the trigger event that becomes the workflow ID
102
+ startOnEventClass = {TriggerEvent}.class, // event that starts a new workflow instance
103
+ workflowNamespace = "de.eventmodelers.slices.{context}"
104
+ )
105
+ public class {SliceName}Workflow {
106
+
107
+ /**
108
+ * The main workflow method. Runs as a durable, resumable coroutine.
109
+ * ctx.awaitExecute() blocks until the step completes or fails.
110
+ * ctx.waitForEvent() suspends until an external event arrives.
111
+ */
112
+ public void execute(SimpleWorkflowContext ctx) {
113
+ var orderId = (String) ctx.workflowPayload().get("{triggerEventIdField}");
114
+
115
+ // Step 1: dispatch the first command and wait for it to complete
116
+ ctx.awaitExecute("step1-{commandName}",
117
+ payload("{field}", ctx.workflowPayload().get("{field}")).getValues(),
118
+ {ServiceInterface}::{methodName});
119
+
120
+ // Step 2: wait for a confirmation event before continuing
121
+ var confirmation = ctx.waitForEvent("step2-await{EventName}",
122
+ {ConfirmationEvent}.class,
123
+ Duration.ofMinutes(30));
124
+
125
+ if (confirmation.timeout()) {
126
+ ctx.fail(new RuntimeException("No {ConfirmationEvent} received within 30 minutes"));
127
+ return;
128
+ }
129
+
130
+ confirmation.await(); // blocks until event arrives (already arrived since !timeout())
131
+
132
+ // Step 3: dispatch a second command based on the confirmation
133
+ ctx.awaitExecute("step3-{secondCommandName}",
134
+ payload("{field}", confirmation.result()).getValues(),
135
+ {ServiceInterface}::{secondMethodName});
136
+ }
137
+ }
138
+ ```
139
+
140
+ ### 3b — Key context API
141
+
142
+ | Method | What it does |
143
+ |---|---|
144
+ | `ctx.awaitExecute(stepName, payload, handler)` | Dispatches a command/action and **blocks** until completion |
145
+ | `ctx.execute(stepName, payload, handler, timeout, options)` | Dispatches **without blocking** — returns `WorkflowStepResult` immediately |
146
+ | `ctx.waitForEvent(stepName, EventClass.class, timeout)` | **Suspends** the workflow until the event arrives or the timeout fires |
147
+ | `ctx.awaitEvent(stepName, EventClass.class, timeout)` | Same as `waitForEvent` but also matches using an association predicate |
148
+ | `ctx.sleep(stepName, duration)` | Durably suspends for a duration (survives restarts) |
149
+ | `ctx.allMatch(predicate, steps...)` | Fan-in barrier: waits until all steps satisfy the predicate |
150
+ | `ctx.anyMatch(predicate, steps...)` | Scatter-gather: waits until any step satisfies the predicate |
151
+ | `ctx.setPayload(stepName, newPayload)` | Persists state to the durable workflow payload |
152
+ | `ctx.workflowId()` | The workflow instance ID (derived from the trigger event) |
153
+ | `ctx.workflowPayload()` | Current durable payload as `Map<String, Object>` |
154
+ | `ctx.fail(exception)` | Terminates the workflow with FAILED status |
155
+ | `ctx.cancel()` | Terminates the workflow with CANCELLED status |
156
+
157
+ The `handler` argument in `awaitExecute`/`execute` is a `BiFunction<ProcessingContext, Map<String, Object>, Map<String, Object>>` —
158
+ typically a Spring bean method reference: `MyService::doWork`.
159
+
160
+ ### 3c — Service handler beans
161
+
162
+ Workflow step handlers are Spring beans registered in the `ProcessingContext`. For each step,
163
+ create a Spring service bean:
164
+
165
+ ```java
166
+ // File: {slicename}/{SliceName}WorkflowServices.java
167
+ @Component
168
+ @ConditionalOnProperty(prefix = "slices.{context}.automation", name = "{slicename}.enabled")
169
+ public class {SliceName}WorkflowServices {
170
+
171
+ private final CommandGateway commandGateway;
172
+
173
+ public {SliceName}WorkflowServices(CommandGateway commandGateway) {
174
+ this.commandGateway = commandGateway;
175
+ }
176
+
177
+ /** Step 1: reserve/validate. Returns result map for next steps. */
178
+ public Map<String, Object> step1(ProcessingContext ctx, Map<String, Object> payload) {
179
+ var command = new {SliceName}Command(
180
+ (String) payload.get("{field}")
181
+ // map other fields
182
+ );
183
+ commandGateway.sendAndWait(command);
184
+ return Map.of(); // return data the next step needs, or empty map
185
+ }
186
+
187
+ /** Step 2: compensate step 1 on failure. */
188
+ public Map<String, Object> compensateStep1(ProcessingContext ctx, Map<String, Object> payload) {
189
+ var command = new Compensate{SliceName}Command((String) payload.get("{field}"));
190
+ commandGateway.sendAndWait(command);
191
+ return Map.of();
192
+ }
193
+ }
194
+ ```
195
+
196
+ ### 3d — Register the workflow module
197
+
198
+ Add the `WorkflowModule` to the Spring configuration. **Create once per bounded context**
199
+ (not once per slice).
200
+
201
+ ```java
202
+ // File: {context}/WorkflowConfiguration.java
203
+ @Configuration
204
+ @ConditionalOnProperty(prefix = "slices.{context}", name = "workflows.enabled")
205
+ public class {Context}WorkflowConfiguration {
206
+
207
+ @Bean
208
+ public EntityModule<?, ?> {context}WorkflowModule() {
209
+ return WorkflowModule
210
+ .usingContext(SimpleWorkflowContext.class)
211
+ .workflowContextFactory(c -> new SimpleWorkflowContextFactory())
212
+ .workflowExecutionFactory(c ->
213
+ new DSLAdoptingExecutionFactory<>(SimpleWorkflowContext.class))
214
+ .definitions(d -> d
215
+ // autodetected: reads @Workflow annotations from registered beans
216
+ .autodetected(c -> new {SliceName}Workflow(), SimpleWorkflowContext.class)
217
+ // add more workflows in the same chain:
218
+ // .autodetected(c -> new {OtherSlice}Workflow(), SimpleWorkflowContext.class)
219
+ );
220
+ }
221
+ }
222
+ ```
223
+
224
+ ---
225
+
226
+ ## Step 4: Common Patterns
227
+
228
+ ### Compensating transaction (saga)
229
+
230
+ ```java
231
+ public void execute(SimpleWorkflowContext ctx) {
232
+ var id = (String) ctx.workflowPayload().get("id");
233
+
234
+ // Step 1
235
+ ctx.awaitExecute("reserveStock",
236
+ payload("id", id).getValues(),
237
+ services::reserveStock);
238
+
239
+ // Step 2 — compensate step 1 on failure
240
+ try {
241
+ ctx.awaitExecute("chargePayment",
242
+ payload("id", id, "amount", ctx.workflowPayload().get("amount")).getValues(),
243
+ services::charge);
244
+ } catch (StepFailedException e) {
245
+ ctx.awaitExecute("releaseStock",
246
+ payload("id", id).getValues(),
247
+ services::releaseStock);
248
+ ctx.fail(new RuntimeException("Payment failed: " + e.getMessage()));
249
+ return;
250
+ }
251
+
252
+ // Step 3 — compensate steps 2 and 1 on failure
253
+ try {
254
+ ctx.awaitExecute("shipOrder",
255
+ payload("id", id).getValues(),
256
+ services::shipOrder);
257
+ } catch (StepFailedException e) {
258
+ ctx.awaitExecute("refundPayment",
259
+ payload("id", id).getValues(),
260
+ services::refundPayment);
261
+ ctx.awaitExecute("releaseStock",
262
+ payload("id", id).getValues(),
263
+ services::releaseStock);
264
+ ctx.fail(new RuntimeException("Shipping failed: " + e.getMessage()));
265
+ }
266
+ }
267
+ ```
268
+
269
+ ### Human-in-the-loop (approval with escalation)
270
+
271
+ ```java
272
+ public void execute(SimpleWorkflowContext ctx) {
273
+ // Request approval
274
+ ctx.awaitExecute("requestApproval",
275
+ payload("approver", "team-lead", "requestId", ctx.workflowId()).getValues(),
276
+ services::sendApprovalRequest);
277
+
278
+ // Wait up to 3 days for a response
279
+ var decision = ctx.waitForEvent("awaitApproval",
280
+ ApprovalDecisionEvent.class,
281
+ Duration.ofDays(3));
282
+
283
+ if (decision.timeout()) {
284
+ // Escalate to department head
285
+ ctx.awaitExecute("escalate",
286
+ payload("approver", "department-head", "requestId", ctx.workflowId()).getValues(),
287
+ services::escalate);
288
+
289
+ decision = ctx.waitForEvent("awaitEscalatedApproval",
290
+ ApprovalDecisionEvent.class,
291
+ Duration.ofDays(1));
292
+
293
+ if (decision.timeout()) {
294
+ ctx.fail(new RuntimeException("No approval received after escalation"));
295
+ return;
296
+ }
297
+ }
298
+
299
+ decision.await();
300
+ // decision.result() contains the ApprovalDecisionEvent payload
301
+ }
302
+ ```
303
+
304
+ ### Fan-out / Fan-in
305
+
306
+ ```java
307
+ public void execute(SimpleWorkflowContext ctx) {
308
+ var items = (List<Map<String, Object>>) ctx.workflowPayload().get("lineItems");
309
+
310
+ // Fan-out: launch reservations concurrently
311
+ var reservations = items.stream()
312
+ .map(item -> ctx.execute(
313
+ "reserve-" + item.get("sku"),
314
+ payload("sku", item.get("sku"), "qty", item.get("quantity")).getValues(),
315
+ services::reserveStock,
316
+ Duration.ofSeconds(30),
317
+ defaults()))
318
+ .toArray(WorkflowStepResult[]::new);
319
+
320
+ // Fan-in: wait for all
321
+ var guard = ctx.allMatch(WorkflowStepResult::isCompleted, reservations);
322
+
323
+ if (!guard.success()) {
324
+ for (var step : guard.unmatched()) {
325
+ if (!step.isCompleted()) step.cancel("Partial failure");
326
+ }
327
+ ctx.fail(new RuntimeException("Not all items could be reserved"));
328
+ }
329
+ }
330
+ ```
331
+
332
+ ### Lifecycle listeners
333
+
334
+ ```java
335
+ @Workflow(idProperty = "id", startOnEventClass = OrderPlaced.class,
336
+ workflowNamespace = "de.eventmodelers.slices.{context}")
337
+ public class {SliceName}Workflow {
338
+
339
+ public void execute(SimpleWorkflowContext ctx) { /* ... */ }
340
+
341
+ @OnSuccess
342
+ public void onSuccess(SimpleWorkflowContext ctx) {
343
+ log.info("Workflow {} completed for id={}", ctx.workflowId(),
344
+ ctx.workflowPayload().get("id"));
345
+ }
346
+
347
+ @OnFailure
348
+ public void onFailure(SimpleWorkflowContext ctx) {
349
+ log.error("Workflow {} failed for id={}", ctx.workflowId(),
350
+ ctx.workflowPayload().get("id"));
351
+ }
352
+
353
+ @OnTimeout
354
+ public void onTimeout(SimpleWorkflowContext ctx) {
355
+ log.warn("Workflow {} timed out", ctx.workflowId());
356
+ }
357
+ }
358
+ ```
359
+
360
+ ---
361
+
362
+ ## Step 5: Feature Flag Configuration
363
+
364
+ Follow the same pattern as other slices:
365
+
366
+ `src/main/resources/application.properties`:
367
+ ```properties
368
+ slices.{context}.automation.{slicename}.enabled=true
369
+ slices.{context}.workflows.enabled=true
370
+ ```
371
+
372
+ `src/test/resources/application.properties`:
373
+ ```properties
374
+ slices.{context}.automation.{slicename}.enabled=false
375
+ slices.{context}.workflows.enabled=false
376
+ ```
377
+
378
+ `META-INF/additional-spring-configuration-metadata.json` — add entries for the new properties.
379
+
380
+ See [references/feature-flag-patterns.md](references/feature-flag-patterns.md) for the full pattern.
381
+
382
+ ---
383
+
384
+ ## Step 6: Tests
385
+
386
+ ### Unit tests — test `execute()` logic with mock services
387
+
388
+ For simple workflows, stub the service methods and verify step sequencing:
389
+
390
+ ```java
391
+ class {SliceName}WorkflowTest {
392
+
393
+ private {SliceName}Workflow workflow;
394
+ private {SliceName}WorkflowServices services;
395
+ private SimpleWorkflowContext ctx;
396
+
397
+ @BeforeEach
398
+ void setUp() {
399
+ services = mock({SliceName}WorkflowServices.class);
400
+ workflow = new {SliceName}Workflow(services);
401
+ ctx = mock(SimpleWorkflowContext.class);
402
+ // stub workflowId() and workflowPayload() as needed
403
+ when(ctx.workflowId()).thenReturn("wf-1");
404
+ when(ctx.workflowPayload()).thenReturn(Map.of("id", "entity-1"));
405
+ }
406
+
407
+ @Test
408
+ @DisplayName("given trigger event, workflow executes all steps in order")
409
+ void happyPath() {
410
+ // stub ctx.awaitExecute to return normally
411
+ doNothing().when(ctx).awaitExecute(anyString(), anyMap(), any());
412
+ var mockEvent = mock(WorkflowStepResult.class);
413
+ when(ctx.waitForEvent(anyString(), any(), any())).thenReturn(mockEvent);
414
+ when(mockEvent.timeout()).thenReturn(false);
415
+
416
+ workflow.execute(ctx);
417
+
418
+ // verify steps were called in order
419
+ var inOrder = inOrder(ctx);
420
+ inOrder.verify(ctx).awaitExecute(eq("step1-{commandName}"), anyMap(), any());
421
+ inOrder.verify(ctx).waitForEvent(eq("step2-await{EventName}"), any(), any());
422
+ inOrder.verify(ctx).awaitExecute(eq("step3-{secondCommandName}"), anyMap(), any());
423
+ }
424
+
425
+ @Test
426
+ @DisplayName("given step2 times out, workflow fails")
427
+ void timeoutFails() {
428
+ doNothing().when(ctx).awaitExecute(anyString(), anyMap(), any());
429
+ var timedOut = mock(WorkflowStepResult.class);
430
+ when(ctx.waitForEvent(anyString(), any(), any())).thenReturn(timedOut);
431
+ when(timedOut.timeout()).thenReturn(true);
432
+
433
+ workflow.execute(ctx);
434
+
435
+ verify(ctx).fail(any(RuntimeException.class));
436
+ }
437
+
438
+ @Test
439
+ @DisplayName("given step2 payment fails, step1 stock is released")
440
+ void compensatesOnPaymentFailure() {
441
+ doNothing().when(ctx).awaitExecute(eq("reserveStock"), anyMap(), any());
442
+ doThrow(new StepFailedException("insufficient funds"))
443
+ .when(ctx).awaitExecute(eq("chargePayment"), anyMap(), any());
444
+
445
+ workflow.execute(ctx);
446
+
447
+ var inOrder = inOrder(ctx);
448
+ inOrder.verify(ctx).awaitExecute(eq("chargePayment"), anyMap(), any());
449
+ inOrder.verify(ctx).awaitExecute(eq("releaseStock"), anyMap(), any());
450
+ inOrder.verify(ctx).fail(any());
451
+ }
452
+ }
453
+ ```
454
+
455
+ ### Integration tests — `AbstractDeclarativeTestBase`
456
+
457
+ For full integration tests, use AF5's `AbstractDeclarativeTestBase` (requires the workflow module
458
+ on the test classpath):
459
+
460
+ ```java
461
+ @SpringBootTest
462
+ @TestPropertySource(properties = {
463
+ "slices.{context}.workflows.enabled=true",
464
+ "slices.{context}.automation.{slicename}.enabled=true"
465
+ })
466
+ class {SliceName}WorkflowIntegrationTest extends AbstractDeclarativeTestBase {
467
+
468
+ // AbstractDeclarativeTestBase provides fixture methods for
469
+ // publishing events and asserting workflow state/steps.
470
+ // Refer to the Axon Framework test documentation for the full API.
471
+ }
472
+ ```
473
+
474
+ ---
475
+
476
+ ## Files to Create / Modify
477
+
478
+ ```
479
+ src/main/java/de/eventmodelers/slices/{context}/automation/{slicename}/
480
+ ├── {SliceName}Workflow.java ← @Workflow class (execute method + lifecycle listeners)
481
+ └── {SliceName}WorkflowServices.java ← @Component step handlers (Spring beans)
482
+
483
+ src/main/java/de/eventmodelers/slices/{context}/
484
+ └── WorkflowConfiguration.java ← WorkflowModule bean (one per context, create if missing)
485
+
486
+ src/test/java/de/eventmodelers/slices/{context}/automation/{slicename}/
487
+ └── {SliceName}WorkflowTest.java ← unit tests with mocked context + services
488
+ ```
489
+
490
+ ---
491
+
492
+ ## References
493
+
494
+ - [Axon Workflow API](references/axon-workflow-api.md) — Annotations, context API, configuration, patterns
495
+ - [Feature Flag Patterns](references/feature-flag-patterns.md) — `@ConditionalOnProperty` and alternatives
496
+ - [Axon Workflow documentation](https://docs.axoniq.io/axon-framework-reference/development/workflows/) — Official reference (Preview)