@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,251 @@
1
+ # Cratis Conventions — Distilled
2
+
3
+ > Self-contained distillation of the Cratis way of building software, taken from the shared
4
+ > Cratis AI configuration (cratis.io). The build skills (`build-state-change`, `build-state-view`,
5
+ > `build-automation`) all rely on these conventions. When in doubt, follow the established pattern in
6
+ > the target project over anything written here — **consistency is king**.
7
+
8
+ ## Stack
9
+
10
+ - .NET / C# 13, ASP.NET Core. **Cratis Arc** for CQRS, **Cratis Chronicle** for event sourcing,
11
+ **MongoDB** for read models.
12
+ - React + TypeScript (Vite), PrimeReact UI — generated from C# via **proxy generation** on `dotnet build`.
13
+ - Specs: xUnit + **Cratis.Specifications** + NSubstitute, BDD `for_*/when_*/and_*` layout.
14
+
15
+ ## The eight values (apply these when a rule doesn't cover a case)
16
+
17
+ 1. **Lovable APIs** — sane defaults, flexible, overridable.
18
+ 2. **Easy to do right, hard to do wrong** — convention over configuration; discovery by naming.
19
+ 3. **Events are facts** — immutable, never nullable, never ambiguous, never multipurpose. A nullable
20
+ property on an event means you need a *second* event.
21
+ 4. **High cohesion through vertical slices** — everything for a behavior lives together.
22
+ 5. **Full-stack type safety** — C# → proxy generation → TypeScript, no manual sync.
23
+ 6. **Specialization over reuse** — dedicated read models per use case, never one shared model.
24
+ 7. **Consistency is king** — match the established pattern over local cleverness.
25
+ 8. American English spelling everywhere (behavior, color, initialize, modeling, dialog, …).
26
+
27
+ ## Non-negotiable structural rules
28
+
29
+ - **ONE `.cs` file per slice** holds ALL backend artifacts: `[Command]` records with `Handle()`,
30
+ validators, constraints, `[EventType]` records, `[ReadModel]` records with static query methods,
31
+ projections/reducers, reactors, and slice-specific concepts.
32
+ - **Never split by artifact type** — no `Commands/`, `Handlers/`, `Events/` folders.
33
+ - **Commands define `Handle()` directly on the record** — never a separate handler class.
34
+ - **`[EventType]` takes NO arguments** — the type name is the identifier.
35
+ - **Drop the `.Features.` segment from namespaces** — `MyApp.Authors.Registration`, not
36
+ `MyApp.Features.Authors.Registration`.
37
+ - Complete one slice end-to-end before starting the next.
38
+ - **File header:** match the project. The shipped starter's example slices use **no** header — follow
39
+ that. (Cratis's own framework repos use a copyright header; only add one if the project's existing
40
+ `.cs` files already carry it.)
41
+
42
+ ## Folder convention
43
+
44
+ Slices live in a **module → feature → slice** hierarchy. **Discover the actual top-level folder from an
45
+ existing slice in the project** (the shipped starter uses `SomeModule/SomeFeature/...`; some projects
46
+ use a `Features/` root) — match what's already there. Namespace mirrors the folders and drops any
47
+ `.Features.` segment: `<Root>.<Module>.<Feature>.<Slice>` (the starter's `<Root>` is `CratisApp`).
48
+
49
+ ```
50
+ <Module>/<Feature>/
51
+ ├── <Feature>.tsx ← composition page (layout, buttons, dialogs, tables)
52
+ ├── index.ts ← barrel: export * from './<Feature>'
53
+ ├── <Concept>.cs ← shared concepts for this feature (ConceptAs<T>)
54
+ └── <Slice>/
55
+ ├── <Slice>.cs ← ALL backend artifacts in ONE file
56
+ ├── <Proxy>.ts ← GENERATED by dotnet build — never hand-edit (marked // @generated)
57
+ ├── <Component>.tsx ← React component(s) using the co-located generated proxy
58
+ ├── index.ts ← barrel export
59
+ └── when_<behavior>/ ← integration specs (state-change slices)
60
+ ├── and_<scenario>.cs
61
+ └── ...
62
+ ```
63
+
64
+ **Proxies are generated next to the source.** The starter `.csproj` sets
65
+ `CratisProxiesUseSourceFileAsOutputFile=true`, so `dotnet build` writes each TypeScript proxy beside its
66
+ `.cs` file (`Register.ts` next to `Registration.cs`). Frontend imports the proxy from the **same folder**
67
+ (`import { Register } from './Register'`). Never edit a `// @generated` file. (Some projects instead
68
+ generate into a separate `api/` folder — follow whatever the existing slices do.)
69
+
70
+ ## Concepts — `ConceptAs<T>` (never raw primitives in domain models)
71
+
72
+ ```csharp
73
+ public record AuthorId(Guid Value) : ConceptAs<Guid>(Value)
74
+ {
75
+ public static readonly AuthorId NotSet = new(Guid.Empty);
76
+ public static implicit operator Guid(AuthorId id) => id.Value;
77
+ public static implicit operator AuthorId(Guid value) => new(value);
78
+ public static implicit operator EventSourceId(AuthorId id) => new(id.Value.ToString());
79
+ public static AuthorId New() => new(Guid.NewGuid());
80
+ }
81
+ ```
82
+
83
+ - Positional `record` inheriting `ConceptAs<T>` (value equality + immutability for free).
84
+ - Add implicit conversion **to** `T`. Add a `static readonly NotSet` sentinel instead of `null`.
85
+ - `Guid` identity concepts: add `static New()` and an implicit conversion to `EventSourceId`.
86
+ - One concept per file, in the feature folder it belongs to (shared between features → `Features/` root).
87
+
88
+ ## Events — `[EventType]`
89
+
90
+ ```csharp
91
+ [EventType]
92
+ public record AuthorRegistered(AuthorName Name);
93
+ ```
94
+
95
+ - **No attribute arguments** — never `[EventType("some-guid")]`.
96
+ - Past-tense names: `AuthorRegistered`, `BookReserved`, `AddressChanged`.
97
+ - Never nullable properties; one purpose per event.
98
+
99
+ ## Commands — `[Command]` with `Handle()` on the record
100
+
101
+ ```csharp
102
+ [Command]
103
+ public record RegisterAuthor(AuthorName Name)
104
+ {
105
+ public AuthorRegistered Handle() => new(Name);
106
+ }
107
+ ```
108
+
109
+ - `[Command]` from `Cratis.Arc.Commands.ModelBound`; required for discovery.
110
+ - `Handle()` returns: single event, `(eventSourceId, event)` tuple (Arc returns the id as
111
+ `CommandResult<T>.response`), `IEnumerable<object>` for multiple events, `Result<TSuccess,TError>`,
112
+ or `void`.
113
+ - Imperative name — `RegisterAuthor`, not `RegisterAuthorCommand`.
114
+ - Event source resolved from a `[Key]` parameter, an `EventSourceId`-convertible concept, or
115
+ `ICanProvideEventSourceId`.
116
+ - Inject services / constraints / read models as `Handle()` parameters — DI provides them. A read-model
117
+ parameter gives you current Chronicle-derived state for **DCB** business rules.
118
+
119
+ ### Validation
120
+ ```csharp
121
+ public class RegisterAuthorValidator : CommandValidator<RegisterAuthor>
122
+ {
123
+ public RegisterAuthorValidator() =>
124
+ RuleFor(c => c.Name).NotEmpty().WithMessage("Name is required").MaximumLength(100);
125
+ }
126
+ ```
127
+ - Extend `CommandValidator<T>` (not `AbstractValidator<T>`) — auto-discovered, runs client-side and
128
+ via the auto-generated `/validate` endpoint.
129
+
130
+ ## Read Models — `[ReadModel]` + static query methods
131
+
132
+ ```csharp
133
+ [ReadModel]
134
+ public record AuthorListItem(AuthorId Id, AuthorName Name, int BookCount)
135
+ {
136
+ public static async Task<IEnumerable<AuthorListItem>> AllAuthors(
137
+ IMongoCollection<AuthorListItem> collection)
138
+ => await collection.Find(_ => true).ToListAsync();
139
+
140
+ public static ISubject<IEnumerable<AuthorListItem>> ObserveAllAuthors(
141
+ IMongoCollection<AuthorListItem> collection)
142
+ => collection.Observe();
143
+ }
144
+ ```
145
+
146
+ - `[ReadModel]` required. Query methods are **`public static`** on the record — the method name becomes
147
+ the TypeScript query proxy class name. No controller, no `IReadModels` injection.
148
+ - Observable (real-time) queries return `ISubject<T>` directly — never `Task<ISubject<T>>`.
149
+ - Prefer model-bound projection attributes; AutoMap is **on by default** (`.From<EventType>()` directly).
150
+ - **Projections join events, never read models.** One read model per use case.
151
+
152
+ ### Projection (declarative mapping)
153
+ ```csharp
154
+ public class AuthorListItemProjection : IProjectionFor<AuthorListItem>
155
+ {
156
+ public void Define(IProjectionBuilderFor<AuthorListItem> builder) => builder
157
+ .From<AuthorRegistered>(from => from.Set(m => m.Name).To(e => e.Name))
158
+ .From<BookAdded>(from => from.Add(m => m.BookCount).With(_ => 1));
159
+ }
160
+ ```
161
+
162
+ ### Model-bound shorthand
163
+ ```csharp
164
+ [ReadModel]
165
+ public record AuthorInfo(
166
+ [Key] Guid Id,
167
+ [FromEvent<AuthorRegistered>] string Name);
168
+ ```
169
+
170
+ ### Reducer (running aggregates — balances, counts, sums)
171
+ ```csharp
172
+ public class BalanceReducer : IReducerFor<Balance>
173
+ {
174
+ public Balance Deposited(FundsDeposited @event, Balance? current, EventContext context)
175
+ => (current ?? new(0m)) with { Amount = (current?.Amount ?? 0m) + @event.Amount };
176
+ }
177
+ ```
178
+ Return the complete new state; never mutate `current` (`null` on first event). Projections/reducers
179
+ are discovered automatically — no registration.
180
+
181
+ ## Reactors — `IReactor` (automation + translation slices)
182
+
183
+ ```csharp
184
+ public class StockKeeping(IStockKeeper stock, ICommandPipeline commands) : IReactor
185
+ {
186
+ public async Task BookReserved(BookReserved @event, EventContext context) =>
187
+ await commands.Execute(new DecreaseStock(@event.Isbn, await stock.GetStock(@event.Isbn)));
188
+ }
189
+ ```
190
+
191
+ - `IReactor` is a **marker interface** — dispatch is by the **first parameter type** of each public
192
+ method. Method name is for readability only. Signature: `Task Method(TEvent @event, EventContext context)`.
193
+ - Reactors *do things* (side effects); they never build read-model state.
194
+ - To write new events, inject `ICommandPipeline` and `Execute` a command — **never** use `IEventLog`
195
+ directly from a reactor.
196
+ - **Design for idempotency** — a reactor may run more than once (replay/recovery). Use event data
197
+ directly; never read the read model back inside a reactor. Keep reactors stateless.
198
+ - **Translation** = a reactor that adapts an event by triggering a command in its own slice.
199
+
200
+ ## Specs — BDD with Cratis.Specifications
201
+
202
+ ```csharp
203
+ public class when_registering : Specification
204
+ {
205
+ RegisterAuthor _command;
206
+ AuthorRegistered _event;
207
+
208
+ void Establish() => _command = new(new AuthorName("John"));
209
+ void Because() => _event = _command.Handle();
210
+
211
+ [Fact] void should_carry_the_name() => _event.Name.Value.ShouldEqual("John");
212
+ }
213
+ ```
214
+
215
+ - Inherit `Specification`. `Establish()` = setup, `Because()` = the **one** action, `[Fact] should_*()`
216
+ = one assertion each (no blank lines between facts). Fields `private`/`protected`, `_camelCase`.
217
+ - Shared setup → `given/` contexts (`all_dependencies` → `a_<sut>`). Mock with NSubstitute.
218
+ - Folder/file names read as English: `for_<Type>/when_<behavior>/and_<condition>.cs`.
219
+ - Chronicle integration specs live in the slice's `when_<behavior>/` folder and run against a real
220
+ event store using `Given<context>` + `ChronicleOutOfProcessFixture`; assert with
221
+ `Context.ShouldHaveAppendedEvent<T>(...)`.
222
+
223
+ ## React (frontend) — only relevant once `dotnet build` has generated proxies
224
+
225
+ - Import the generated command/query proxy from the slice's `../api/...` path.
226
+ - Commands: `const [command] = MyCommand.use();` → set props → `await command.execute()` →
227
+ check `result.isSuccess` / `result.response`.
228
+ - Queries: `MyQuery.use()` (snapshot) or `MyQuery.useWithPaging(pageSize)`.
229
+ - Dialogs: `CommandDialog` from `@cratis/components/CommandDialog`, `Dialog` from
230
+ `@cratis/components/Dialogs`. **Never** import `Dialog` from `primereact/dialog`.
231
+ - PrimeReact CSS variables for colors (no hard-coded hex). No `any` — use `unknown` + type guards.
232
+ Full descriptive names (`event`, not `e`; `index`, not `idx`).
233
+
234
+ ## Build / test / sequencing
235
+
236
+ - **`dotnet build`** compiles the backend AND regenerates the TypeScript proxies. The frontend cannot
237
+ reference a slice's proxy until the backend compiles — so the order is strict:
238
+ **Backend → `dotnet build` → Frontend.** Backend and frontend of the same slice never run in parallel.
239
+ - Zero warnings, zero errors — warnings are treated as errors.
240
+ - `dotnet test` (filter to the slice while iterating:
241
+ `dotnet test --filter "FullyQualifiedName~<SliceName>"`).
242
+ - Slice-by-slice order: Backend → Specs → `dotnet build` → Frontend → Composition page → Routes.
243
+
244
+ ## Final verification against `slice.json` (the JSON is the source of truth)
245
+
246
+ - Every `commands[]` field → a Command record property. No invented fields, none missing.
247
+ - Every event in `events[]` → an `[EventType]` record; names match exactly; no invented fields.
248
+ - Every read model field → a `[ReadModel]` property fed by a projection/reducer mapping.
249
+ - Every specification / GWT scenario in the slice → an executable spec.
250
+ - No business rule appears in `Handle()` that is not in the slice `description`/`comments`.
251
+ - If a field is not in `slice.json`, it is not in the code.
@@ -0,0 +1,99 @@
1
+ ---
2
+ name: build-automation
3
+ description: >
4
+ Implement automation and translation slices the Cratis way — an IReactor that observes events and
5
+ produces side effects, triggering further writes via ICommandPipeline. Use when: (1) implementing a
6
+ new automation / reactor in a Cratis project, (2) a slice.json has a non-empty processors[] section
7
+ or sliceType === "TRANSLATION", (3) the user provides an Event Modeling artifact or description of an
8
+ event-driven reaction and asks to implement it, (4) the user says "implement", "create", "add" an
9
+ automation, reactor, translation, or event-to-command flow in a Cratis Arc / Chronicle project.
10
+ ---
11
+
12
+ # Cratis — Automation / Translation Slice
13
+
14
+ An automation *reacts* to events and *does things* (side effects). A translation adapts an event by
15
+ triggering a command in its own slice. Both are implemented with an **`IReactor`**.
16
+
17
+ ```
18
+ event → IReactor method (dispatch by first param type) → side effect / ICommandPipeline.Execute(command)
19
+ ```
20
+
21
+ > **Read first:** [../_shared/cratis-conventions.md](../_shared/cratis-conventions.md). Everything
22
+ > below assumes those rules.
23
+
24
+ ## Step 0 — Discover conventions
25
+
26
+ Read `CLAUDE.md` and one existing reactor. Confirm the namespace root, how reactors are placed within a
27
+ slice, which services are available for side effects, and how existing translations call
28
+ `ICommandPipeline`. Resolve slice `comments` when done (see the state-change skill's Step 0).
29
+
30
+ ## Step 1 — Understand the input (`slice.json` is the source of truth)
31
+
32
+ | Element | What to extract |
33
+ |---|---|
34
+ | **Trigger event(s)** | Which event(s) the reactor observes (the method's first parameter type) |
35
+ | **Reaction** | The side effect — notify, call a service, or trigger a command |
36
+ | **Target command** | For translation: which command to `Execute`, and how its fields map from the event |
37
+ | **Idempotency** | The reaction must be safe to run more than once (replay/recovery) |
38
+ | **Specifications** | Each scenario → an executable spec |
39
+
40
+ If a command field has no source in the trigger event or an injected service, do not invent it.
41
+
42
+ ## Step 2 — Write the reactor in the slice `.cs` file
43
+
44
+ `<Module>/<Feature>/<Slice>/<Slice>.cs` — namespace `<Root>.<Module>.<Feature>.<Slice>` (mirror the folders; drop any `.Features.`).
45
+
46
+ ### Automation — side effect
47
+ ```csharp
48
+ public class ProjectRegisteredNotifier(INotificationService notifications) : IReactor
49
+ {
50
+ public async Task ProjectRegistered(ProjectRegistered @event, EventContext context) =>
51
+ await notifications.Notify($"Project '{@event.Name}' was registered.");
52
+ }
53
+ ```
54
+
55
+ ### Translation — event → command via ICommandPipeline
56
+ ```csharp
57
+ public class StockKeeping(IStockKeeper stock, ICommandPipeline commands) : IReactor
58
+ {
59
+ public async Task BookReserved(BookReserved @event, EventContext context) =>
60
+ await commands.Execute(new DecreaseStock(@event.Isbn, await stock.GetStock(@event.Isbn)));
61
+ }
62
+ ```
63
+
64
+ **Critical rules:**
65
+ - `IReactor` is a **marker interface** — dispatch is by the **first parameter type**. Method name is
66
+ for readability only. Signature: `Task Method(TEvent @event, EventContext context)` (`EventContext`
67
+ optional).
68
+ - To write new events, inject `ICommandPipeline` and `Execute` a command — **never** use `IEventLog`
69
+ directly from a reactor.
70
+ - **Design for idempotency** — a reactor may run more than once. Use event data directly; never query
71
+ the read model back inside a reactor. Keep the reactor stateless (constructor-injected deps only).
72
+ - One focused concern per reactor class; multiple handler methods are fine if they serve that concern.
73
+
74
+ If the translation's target command doesn't exist yet, implement it first with the
75
+ **build-state-change** skill, then reference it here.
76
+
77
+ ## Step 3 — Build
78
+
79
+ `dotnet build` from the project root — zero warnings / errors.
80
+
81
+ ## Step 4 — Specs
82
+
83
+ Write specs proving the reactor reacts correctly — for translations, assert the expected command was
84
+ executed (mock `ICommandPipeline` with NSubstitute and verify `Execute` was called with the mapped
85
+ command); for automations, assert the side-effect service was invoked. Cover idempotency where the
86
+ slice calls for it. One spec per scenario. Run `dotnet test --filter "FullyQualifiedName~<SliceName>"`.
87
+ See [references/patterns.md](references/patterns.md).
88
+
89
+ ## Final verification — does the implementation match `slice.json`?
90
+
91
+ - [ ] The reactor observes exactly the trigger event(s) named in the slice.
92
+ - [ ] For translation: the executed command and its field mapping match the slice; the command exists.
93
+ - [ ] No new events written via `IEventLog` — only via `ICommandPipeline`.
94
+ - [ ] The reaction is idempotent; the reactor is stateless.
95
+ - [ ] Every scenario → an executable spec; `dotnet build` clean; specs pass.
96
+
97
+ ## References
98
+ - [references/patterns.md](references/patterns.md) — full reactor/translation code and reactor specs.
99
+ - [../_shared/cratis-conventions.md](../_shared/cratis-conventions.md) — the Cratis conventions.
@@ -0,0 +1,115 @@
1
+ # Automation / Translation Patterns — Cratis Arc + Chronicle
2
+
3
+ Complete code for automation and translation slices. The reactor lives in the slice's single
4
+ `<Module>/<Feature>/<Slice>/<Slice>.cs` (the shipped starter uses `SomeModule/SomeFeature/...` — match
5
+ whatever the existing slices use). The example slices use no file header (match the project); namespace
6
+ mirrors the folders and drops any `.Features.`. Examples below use short illustrative names; mirror your
7
+ project's actual module/feature layout instead.
8
+
9
+ ---
10
+
11
+ ## 1. Automation — react with a side effect
12
+
13
+ ```csharp
14
+ // Features/Projects/RegistrationNotifications/RegistrationNotifications.cs
15
+ namespace MyApp.Projects.RegistrationNotifications;
16
+
17
+ using Cratis.Chronicle.Events;
18
+ using Cratis.Chronicle.Reactors;
19
+
20
+ public class ProjectRegisteredNotifier(INotificationService notifications) : IReactor
21
+ {
22
+ /// <summary>Sends a notification when a project is registered.</summary>
23
+ public async Task ProjectRegistered(ProjectRegistered @event, EventContext context) =>
24
+ await notifications.Notify($"Project '{@event.Name}' was registered.");
25
+ }
26
+ ```
27
+
28
+ ---
29
+
30
+ ## 2. Translation — event → command via ICommandPipeline
31
+
32
+ ```csharp
33
+ // Features/Inventory/StockKeeping/StockKeeping.cs
34
+ namespace MyApp.Inventory.StockKeeping;
35
+
36
+ using Cratis.Arc.Commands;
37
+ using Cratis.Chronicle.Events;
38
+ using Cratis.Chronicle.Reactors;
39
+
40
+ public class StockKeeping(IStockKeeper stock, ICommandPipeline commands) : IReactor
41
+ {
42
+ /// <summary>Decreases stock when a book is reserved.</summary>
43
+ public async Task BookReserved(BookReserved @event, EventContext context) =>
44
+ await commands.Execute(new DecreaseStock(@event.Isbn, await stock.GetStock(@event.Isbn)));
45
+ }
46
+ ```
47
+
48
+ The `DecreaseStock` command lives in its own slice (build it with the build-state-change skill first if
49
+ it doesn't exist). The reactor only triggers it.
50
+
51
+ ---
52
+
53
+ ## 3. Multiple handlers in one reactor (same concern)
54
+
55
+ ```csharp
56
+ public class OrderFulfillment(ICommandPipeline commands) : IReactor
57
+ {
58
+ public Task OrderPlaced(OrderPlaced @event, EventContext context) =>
59
+ commands.Execute(new ReserveInventory(@event.OrderId, @event.Items));
60
+
61
+ public Task PaymentCaptured(PaymentCaptured @event, EventContext context) =>
62
+ commands.Execute(new ShipOrder(@event.OrderId));
63
+ }
64
+ ```
65
+
66
+ ---
67
+
68
+ ## 4. Idempotency
69
+
70
+ A reactor may be invoked more than once for the same event (replay, recovery, redelivery). Use only the
71
+ event's own data, keep the reactor stateless, and make the triggered command/side effect safe to repeat
72
+ (e.g. the target command is itself idempotent, or carries a key that de-duplicates). Never read the read
73
+ model back inside the reactor to "check if already done".
74
+
75
+ ---
76
+
77
+ ## 5. Reactor spec (translation — assert the command was executed)
78
+
79
+ ```csharp
80
+ namespace MyApp.Inventory.StockKeeping.for_StockKeeping.when_a_book_is_reserved;
81
+
82
+ public class and_stock_is_available : Specification
83
+ {
84
+ IStockKeeper _stock;
85
+ ICommandPipeline _commands;
86
+ StockKeeping _reactor;
87
+
88
+ void Establish()
89
+ {
90
+ _stock = Substitute.For<IStockKeeper>();
91
+ _stock.GetStock("123").Returns(Task.FromResult(5));
92
+ _commands = Substitute.For<ICommandPipeline>();
93
+ _reactor = new StockKeeping(_stock, _commands);
94
+ }
95
+
96
+ async Task Because() => await _reactor.BookReserved(new BookReserved("123"), EventContext.Empty);
97
+
98
+ [Fact] void should_decrease_stock() =>
99
+ _commands.Received(1).Execute(Arg.Is<DecreaseStock>(c => c.Isbn == "123" && c.Quantity == 5));
100
+ }
101
+ ```
102
+
103
+ For automations, mock the side-effect service (e.g. `INotificationService`) and assert it was called.
104
+ Match the project's existing reactor-spec harness (`IReactorInvoker` / `ReactorHandler`) for full
105
+ event-store-driven specs — discover it from an existing reactor's specs.
106
+
107
+ ---
108
+
109
+ ## Checklist
110
+
111
+ - One `.cs` file, namespace without `.Features.` (no file header unless the project uses one).
112
+ - `IReactor` marker; dispatch by **first parameter type**; `Task Method(TEvent, EventContext)`.
113
+ - New writes only via `ICommandPipeline.Execute(...)` — never `IEventLog` from a reactor.
114
+ - Stateless and idempotent; constructor-injected dependencies only.
115
+ - One spec per scenario; `dotnet build` clean; specs pass.
@@ -0,0 +1,163 @@
1
+ ---
2
+ name: build-state-change
3
+ description: >
4
+ Implement Event Sourcing write slices the Cratis way — using Cratis Arc (CQRS) + Cratis Chronicle
5
+ (event sourcing) in a .NET / C# project. A write slice is: Command → Handle() → Event(s), with
6
+ optional validators, constraints, and DCB business rules. Use when: (1) implementing a new write
7
+ slice / command in a Cratis project, (2) a slice.json has a non-empty commands[] / events[] section,
8
+ (3) the user provides an Event Modeling artifact, specification, or natural-language description of a
9
+ command and asks to implement it, (4) the user says "implement", "create", "add" a write slice,
10
+ command, or state change in a Cratis Arc / Chronicle project.
11
+ ---
12
+
13
+ # Cratis — Write Slice (State Change)
14
+
15
+ A write slice mutates state by recording events. In Cratis Arc the whole path lives in **one `.cs`
16
+ file**:
17
+
18
+ ```
19
+ [Command] record + Handle() → validator/constraint → [EventType] record(s) → dotnet build → specs
20
+ ```
21
+
22
+ > **Read first:** [../_shared/cratis-conventions.md](../_shared/cratis-conventions.md) — the
23
+ > non-negotiable Cratis rules. Everything below assumes them.
24
+
25
+ ## Step 0 — Discover the target project's conventions
26
+
27
+ Before writing code, read the project's `CLAUDE.md` and **at least one existing slice** (the starter
28
+ ships one under `SomeModule/SomeFeature/`). Confirm:
29
+
30
+ - The namespace root (read the `.csproj` `<RootNamespace>` and existing `.cs` files — never
31
+ hard-code it; the namespace mirrors the folders and drops any `.Features.` segment).
32
+ - How existing commands return results (single event / tuple / `Result<,>` / `void`).
33
+ - How `ConceptAs<T>` identity types are declared and where they live.
34
+ - Whether existing `.cs` files use a file header (the starter uses none).
35
+
36
+ > **Comments & description:** each slice element carries a `comments: string[]` array and a
37
+ > `description`. Use them as implementation hints. When done, resolve each used comment:
38
+ > `POST <BASE_URL>/api/org/<ORG_ID>/boards/<BOARD_ID>/nodes/<nodeId>/comments/<commentId>/resolve`
39
+ > (get IDs first via GET on the same path).
40
+
41
+ ## Step 1 — Understand the input (`slice.json` is the source of truth)
42
+
43
+ Extract, regardless of input format:
44
+
45
+ | Element | What to extract |
46
+ |---|---|
47
+ | **Command** | Name (imperative), fields, which field is the event source / `[Key]` |
48
+ | **Events** | Names (past tense), fields, which events this command appends |
49
+ | **Business rules** | Preconditions, invariants, idempotency — from `description` / `comments` only |
50
+ | **State needed for rules** | Which read model must be inspected (DCB) to evaluate a rule |
51
+ | **Specifications** | Each GWT / scenario maps 1:1 to an executable spec |
52
+
53
+ **If a field is not in `slice.json`, it does not go in the code.** If requirements are unclear, ask
54
+ the user before proceeding.
55
+
56
+ ### Determine the trigger
57
+ If unclear how the command is dispatched, ask:
58
+ > - **UI / REST** — exposed automatically by Arc; add a React component + integration spec.
59
+ > - **Automation only** — dispatched internally by a reactor (no UI). The command still exists; no `.tsx`.
60
+
61
+ ## Step 2 — Create concept types (if needed)
62
+
63
+ For any new identity / value, add one `ConceptAs<T>` per file in the feature folder. Use the canonical
64
+ Guid-identity pattern (with `NotSet`, `New()`, and the `EventSourceId` conversion) — see the shared
65
+ conventions doc and [references/patterns.md](references/patterns.md).
66
+
67
+ ## Step 3 — Write the slice `.cs` file
68
+
69
+ `<Module>/<Feature>/<Slice>/<Slice>.cs` — ALL backend artifacts in this one file; namespace
70
+ `<Root>.<Module>.<Feature>.<Slice>` (no file header unless the project uses one).
71
+
72
+ ### Events first
73
+ ```csharp
74
+ [EventType] // NEVER any arguments
75
+ public record AuthorRegistered(AuthorName Name);
76
+ ```
77
+ Past tense, no nullable properties, one purpose each. If the context's events already exist elsewhere,
78
+ reuse them — don't redefine.
79
+
80
+ ### Command with `Handle()` on the record
81
+ ```csharp
82
+ [Command]
83
+ public record RegisterAuthor(AuthorName Name)
84
+ {
85
+ public AuthorRegistered Handle() => new(Name); // Arc appends the returned event
86
+ }
87
+ ```
88
+
89
+ Pick the return shape that matches the slice:
90
+ - **single event** → `EventName Handle()`
91
+ - **generate + return a new id** → `(NewId, EventName) Handle()` (Arc returns the id as
92
+ `CommandResult<T>.response`)
93
+ - **multiple events** → `IEnumerable<object> Handle() => [ new A(...), new B(...) ];`
94
+ - **success/error** → `Result<TSuccess, TError> Handle()`
95
+ - **side-effect only** → `void Handle()`
96
+
97
+ Event source: a `[Key]` parameter, an `EventSourceId`-convertible concept, or `ICanProvideEventSourceId`.
98
+
99
+ ### Business rules (DCB) — inject the read model
100
+ When a rule depends on event-sourced state, take the read model as a `Handle()` parameter; Arc injects
101
+ current state. Throw / return an error for violations:
102
+ ```csharp
103
+ [Command]
104
+ public record RegisterAuthor(AuthorName Name)
105
+ {
106
+ public Result<AuthorRegistered, RegistrationError> Handle(AuthorByName existing) =>
107
+ existing is not null
108
+ ? RegistrationError.NameAlreadyTaken
109
+ : new AuthorRegistered(Name);
110
+ }
111
+ ```
112
+ Only encode rules that appear in the slice `description` / `comments`. See
113
+ [references/patterns.md](references/patterns.md) for the full DCB and constraint patterns.
114
+
115
+ ### Validation (optional but recommended)
116
+ ```csharp
117
+ public class RegisterAuthorValidator : CommandValidator<RegisterAuthor>
118
+ {
119
+ public RegisterAuthorValidator() =>
120
+ RuleFor(c => c.Name).NotEmpty().WithMessage("Name is required").MaximumLength(100);
121
+ }
122
+ ```
123
+ Extend `CommandValidator<T>` — auto-discovered, no registration. One `RuleFor` per validation in the slice.
124
+
125
+ ## Step 4 — Build
126
+
127
+ From the project root: `dotnet build`. Fix ALL warnings and errors before continuing (warnings = errors).
128
+ This also regenerates the TypeScript proxies the frontend depends on.
129
+
130
+ ## Step 5 — Write specs (one per scenario in `slice.json`)
131
+
132
+ Put integration specs in `<Module>/<Feature>/<Slice>/when_<behavior>/and_<scenario>.cs`. Cover, from the
133
+ slice's specifications:
134
+ - **Happy path** — command succeeds, correct event appended.
135
+ - **Each validation failure** — one spec per rule.
136
+ - **Each DCB business-rule violation** — one spec per read-model condition in `Handle()`.
137
+ - **Each constraint violation.**
138
+
139
+ Use the Cratis BDD pattern (`Specification`, `Establish` / `Because` / `[Fact] should_*`). For
140
+ Chronicle integration specs use `Given<context>` + `ChronicleOutOfProcessFixture` and assert with
141
+ `Context.ShouldHaveAppendedEvent<T>(...)`. See [references/patterns.md](references/patterns.md).
142
+
143
+ Run `dotnet test --filter "FullyQualifiedName~<SliceName>"`. Fix all failures.
144
+
145
+ ## Step 6 — Frontend (only if the command is UI-triggered)
146
+
147
+ After `dotnet build` generated the proxy (co-located next to the `.cs`), add
148
+ `<Module>/<Feature>/<Slice>/<Component>.tsx` importing the proxy from `./`, using `CommandDialog` /
149
+ inline form. Add a barrel `index.ts`. Register it in the feature's composition
150
+ page. See the shared conventions doc's React section and [references/patterns.md](references/patterns.md).
151
+
152
+ ## Final verification — does the implementation match `slice.json`?
153
+
154
+ - [ ] Every `commands[]` field → a Command record property (no invented, none missing).
155
+ - [ ] Every `events[]` entry → an `[EventType]` record; names match exactly; fields match.
156
+ - [ ] Every specification / GWT scenario → an executable spec.
157
+ - [ ] No business rule in `Handle()` that is absent from the slice `description` / `comments`.
158
+ - [ ] `dotnet build` is clean (0 warnings / 0 errors); slice specs pass.
159
+
160
+ ## References
161
+ - [references/patterns.md](references/patterns.md) — full command/event/validator/constraint/DCB code,
162
+ integration specs, and the React command UI patterns.
163
+ - [../_shared/cratis-conventions.md](../_shared/cratis-conventions.md) — the Cratis conventions.