@milehimikey/em 1.13.0 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/.claude/agents/em-critic.md +26 -0
  2. package/.claude/agents/em-implementer.md +34 -0
  3. package/.claude/agents/em-reviewer.md +31 -0
  4. package/.claude/agents/em-validator.md +27 -0
  5. package/.claude/skills/event-modeling/SKILL.md +52 -12
  6. package/.claude/skills/event-modeling-conform/SKILL.md +1 -1
  7. package/.claude/skills/event-modeling-conform/reference/conform.md +1 -1
  8. package/.claude/skills/event-modeling-design/SKILL.md +140 -18
  9. package/.claude/skills/event-modeling-discover/SKILL.md +1 -1
  10. package/.claude/skills/event-modeling-engagement/SKILL.md +192 -0
  11. package/.claude/skills/event-modeling-engagement/reference/engagement.md +143 -0
  12. package/.claude/skills/event-modeling-implement/SKILL.md +7 -4
  13. package/.claude/skills/event-modeling-implement/reference/implement.md +51 -12
  14. package/.claude/skills/event-modeling-review/SKILL.md +1 -1
  15. package/.claude/skills/event-modeling-shared/reference/em-dsl.md +274 -188
  16. package/.claude/skills/event-modeling-shared/reference/operating-principles.md +61 -0
  17. package/.claude/skills/event-modeling-shared/reference/slice-doc-schema.md +116 -4
  18. package/.claude/skills/event-modeling-shared/templates/constitution.md +18 -1
  19. package/.claude/skills/event-modeling-shared/templates/slice.md +64 -39
  20. package/.claude/skills/event-modeling-shared/templates/state.md +5 -1
  21. package/.claude-plugin/marketplace.json +14 -0
  22. package/README.md +25 -11
  23. package/dist/catalog/apiFirst.d.ts +28 -0
  24. package/dist/catalog/apiFirst.js +49 -0
  25. package/dist/catalog/apiFirst.js.map +1 -0
  26. package/dist/catalog/docJoin.d.ts +13 -0
  27. package/dist/catalog/docJoin.js +11 -0
  28. package/dist/catalog/docJoin.js.map +1 -1
  29. package/dist/catalog/docModelConsistencyValidate.js +44 -4
  30. package/dist/catalog/docModelConsistencyValidate.js.map +1 -1
  31. package/dist/catalog/invariantsValidate.d.ts +4 -0
  32. package/dist/catalog/invariantsValidate.js +102 -0
  33. package/dist/catalog/invariantsValidate.js.map +1 -0
  34. package/dist/catalog/scenarios.d.ts +15 -0
  35. package/dist/catalog/scenarios.js +97 -0
  36. package/dist/catalog/scenarios.js.map +1 -0
  37. package/dist/catalog/sliceDoc.d.ts +8 -0
  38. package/dist/catalog/sliceDoc.js +9 -0
  39. package/dist/catalog/sliceDoc.js.map +1 -1
  40. package/dist/catalog/sliceReadyValidate.d.ts +31 -4
  41. package/dist/catalog/sliceReadyValidate.js +66 -8
  42. package/dist/catalog/sliceReadyValidate.js.map +1 -1
  43. package/dist/catalog/sliceSections.d.ts +55 -0
  44. package/dist/catalog/sliceSections.js +317 -0
  45. package/dist/catalog/sliceSections.js.map +1 -0
  46. package/dist/cli/agentsMd.js +7 -1
  47. package/dist/cli/agentsMd.js.map +1 -1
  48. package/dist/cli/api.d.ts +90 -0
  49. package/dist/cli/api.js +318 -0
  50. package/dist/cli/api.js.map +1 -0
  51. package/dist/cli/ciInit.d.ts +88 -3
  52. package/dist/cli/ciInit.js +507 -50
  53. package/dist/cli/ciInit.js.map +1 -1
  54. package/dist/cli/conformScope.d.ts +28 -0
  55. package/dist/cli/conformScope.js +67 -0
  56. package/dist/cli/conformScope.js.map +1 -1
  57. package/dist/cli/coverage.d.ts +25 -2
  58. package/dist/cli/coverage.js +86 -12
  59. package/dist/cli/coverage.js.map +1 -1
  60. package/dist/cli/engagement.d.ts +210 -0
  61. package/dist/cli/engagement.js +444 -0
  62. package/dist/cli/engagement.js.map +1 -0
  63. package/dist/cli/engagementFile.d.ts +86 -0
  64. package/dist/cli/engagementFile.js +255 -0
  65. package/dist/cli/engagementFile.js.map +1 -0
  66. package/dist/cli/metrics.d.ts +10 -0
  67. package/dist/cli/metrics.js +49 -1
  68. package/dist/cli/metrics.js.map +1 -1
  69. package/dist/cli/pluginPin.d.ts +55 -0
  70. package/dist/cli/pluginPin.js +188 -0
  71. package/dist/cli/pluginPin.js.map +1 -0
  72. package/dist/cli/ratify.d.ts +27 -2
  73. package/dist/cli/ratify.js +112 -13
  74. package/dist/cli/ratify.js.map +1 -1
  75. package/dist/cli/reratify.d.ts +11 -4
  76. package/dist/cli/reratify.js +89 -35
  77. package/dist/cli/reratify.js.map +1 -1
  78. package/dist/cli/scopeInputs.d.ts +48 -0
  79. package/dist/cli/scopeInputs.js +176 -0
  80. package/dist/cli/scopeInputs.js.map +1 -0
  81. package/dist/cli/skillCheck.d.ts +15 -1
  82. package/dist/cli/skillCheck.js +22 -1
  83. package/dist/cli/skillCheck.js.map +1 -1
  84. package/dist/cli/skillDirs.d.ts +5 -0
  85. package/dist/cli/skillDirs.js +13 -1
  86. package/dist/cli/skillDirs.js.map +1 -1
  87. package/dist/cli/skillSync.d.ts +4 -0
  88. package/dist/cli/skillSync.js +23 -0
  89. package/dist/cli/skillSync.js.map +1 -1
  90. package/dist/cli/sliceNew.d.ts +11 -7
  91. package/dist/cli/sliceNew.js +91 -18
  92. package/dist/cli/sliceNew.js.map +1 -1
  93. package/dist/cli/sliceSync.d.ts +50 -0
  94. package/dist/cli/sliceSync.js +170 -0
  95. package/dist/cli/sliceSync.js.map +1 -0
  96. package/dist/cli/status.d.ts +24 -1
  97. package/dist/cli/status.js +20 -1
  98. package/dist/cli/status.js.map +1 -1
  99. package/dist/cli/systemInputs.d.ts +53 -10
  100. package/dist/cli/systemInputs.js +186 -11
  101. package/dist/cli/systemInputs.js.map +1 -1
  102. package/dist/cli/upgrade.d.ts +30 -7
  103. package/dist/cli/upgrade.js +434 -50
  104. package/dist/cli/upgrade.js.map +1 -1
  105. package/dist/cli/usageLog.d.ts +4 -2
  106. package/dist/cli/usageLog.js +4 -2
  107. package/dist/cli/usageLog.js.map +1 -1
  108. package/dist/cli.js +631 -100
  109. package/dist/cli.js.map +1 -1
  110. package/dist/emit/api.d.ts +29 -0
  111. package/dist/emit/api.js +220 -0
  112. package/dist/emit/api.js.map +1 -0
  113. package/dist/emit/apiCheckJson.d.ts +3 -0
  114. package/dist/emit/apiCheckJson.js +21 -0
  115. package/dist/emit/apiCheckJson.js.map +1 -0
  116. package/dist/emit/codeownersJson.d.ts +3 -0
  117. package/dist/emit/codeownersJson.js +19 -0
  118. package/dist/emit/codeownersJson.js.map +1 -0
  119. package/dist/emit/coverageJson.d.ts +5 -2
  120. package/dist/emit/coverageJson.js +6 -2
  121. package/dist/emit/coverageJson.js.map +1 -1
  122. package/dist/emit/diffJson.d.ts +1 -1
  123. package/dist/emit/diffJson.js +8 -1
  124. package/dist/emit/diffJson.js.map +1 -1
  125. package/dist/emit/engagementJson.d.ts +8 -0
  126. package/dist/emit/engagementJson.js +46 -0
  127. package/dist/emit/engagementJson.js.map +1 -0
  128. package/dist/emit/glossaryJson.d.ts +1 -1
  129. package/dist/emit/glossaryJson.js +4 -1
  130. package/dist/emit/glossaryJson.js.map +1 -1
  131. package/dist/emit/json.d.ts +17 -1
  132. package/dist/emit/json.js +30 -1
  133. package/dist/emit/json.js.map +1 -1
  134. package/dist/emit/metricsJson.d.ts +1 -1
  135. package/dist/emit/metricsJson.js +4 -1
  136. package/dist/emit/metricsJson.js.map +1 -1
  137. package/dist/emit/queryJson.d.ts +1 -1
  138. package/dist/emit/queryJson.js +7 -1
  139. package/dist/emit/queryJson.js.map +1 -1
  140. package/dist/emit/scopeJson.d.ts +12 -0
  141. package/dist/emit/scopeJson.js +26 -0
  142. package/dist/emit/scopeJson.js.map +1 -0
  143. package/dist/emit/skillCheckJson.d.ts +3 -2
  144. package/dist/emit/skillCheckJson.js +10 -2
  145. package/dist/emit/skillCheckJson.js.map +1 -1
  146. package/dist/emit/statusJson.d.ts +1 -1
  147. package/dist/emit/statusJson.js +15 -1
  148. package/dist/emit/statusJson.js.map +1 -1
  149. package/dist/emit/systemJson.d.ts +15 -8
  150. package/dist/emit/systemJson.js +28 -13
  151. package/dist/emit/systemJson.js.map +1 -1
  152. package/dist/emit/upgradeJson.d.ts +1 -1
  153. package/dist/emit/upgradeJson.js +4 -1
  154. package/dist/emit/upgradeJson.js.map +1 -1
  155. package/dist/emit/validateJson.d.ts +3 -3
  156. package/dist/emit/validateJson.js +10 -4
  157. package/dist/emit/validateJson.js.map +1 -1
  158. package/dist/mcp/server.d.ts +1 -1
  159. package/dist/mcp/server.js +186 -37
  160. package/dist/mcp/server.js.map +1 -1
  161. package/dist/model/diff.d.ts +4 -1
  162. package/dist/model/diff.js +44 -8
  163. package/dist/model/diff.js.map +1 -1
  164. package/dist/model/glossary.d.ts +2 -0
  165. package/dist/model/glossary.js +1 -0
  166. package/dist/model/glossary.js.map +1 -1
  167. package/dist/model/model.d.ts +55 -2
  168. package/dist/model/model.js +57 -0
  169. package/dist/model/model.js.map +1 -1
  170. package/dist/model/qualifiedRef.d.ts +18 -0
  171. package/dist/model/qualifiedRef.js +26 -0
  172. package/dist/model/qualifiedRef.js.map +1 -1
  173. package/dist/model/queryIndex.d.ts +9 -1
  174. package/dist/model/queryIndex.js +18 -2
  175. package/dist/model/queryIndex.js.map +1 -1
  176. package/dist/model/rules.d.ts +143 -2
  177. package/dist/model/rules.js +162 -3
  178. package/dist/model/rules.js.map +1 -1
  179. package/dist/model/validate.d.ts +40 -0
  180. package/dist/model/validate.js +147 -1
  181. package/dist/model/validate.js.map +1 -1
  182. package/dist/parser/ast.d.ts +31 -3
  183. package/dist/parser/parser.js +148 -14
  184. package/dist/parser/parser.js.map +1 -1
  185. package/dist/query/format.js +1 -1
  186. package/dist/query/format.js.map +1 -1
  187. package/dist/query/verbs.d.ts +6 -0
  188. package/dist/query/verbs.js +6 -2
  189. package/dist/query/verbs.js.map +1 -1
  190. package/dist/system/adaptation.d.ts +14 -0
  191. package/dist/system/adaptation.js +49 -0
  192. package/dist/system/adaptation.js.map +1 -0
  193. package/dist/system/codeowners.d.ts +77 -0
  194. package/dist/system/codeowners.js +242 -0
  195. package/dist/system/codeowners.js.map +1 -0
  196. package/dist/system/manifest.d.ts +10 -5
  197. package/dist/system/manifest.js +54 -26
  198. package/dist/system/manifest.js.map +1 -1
  199. package/dist/system/migrateManifest.d.ts +28 -0
  200. package/dist/system/migrateManifest.js +155 -0
  201. package/dist/system/migrateManifest.js.map +1 -0
  202. package/dist/system/scope.d.ts +46 -0
  203. package/dist/system/scope.js +132 -0
  204. package/dist/system/scope.js.map +1 -0
  205. package/dist/system/verify.d.ts +50 -13
  206. package/dist/system/verify.js +179 -58
  207. package/dist/system/verify.js.map +1 -1
  208. package/dist/templates.d.ts +2 -2
  209. package/dist/templates.js +23 -2
  210. package/dist/templates.js.map +1 -1
  211. package/package.json +6 -3
  212. package/plugin/.claude-plugin/plugin.json +18 -0
  213. package/plugin/agents/em-critic.md +27 -0
  214. package/plugin/agents/em-implementer.md +35 -0
  215. package/plugin/agents/em-reviewer.md +32 -0
  216. package/plugin/agents/em-validator.md +28 -0
  217. package/plugin/shared/reference/em-dsl.md +866 -0
  218. package/plugin/shared/reference/methodology.md +329 -0
  219. package/plugin/shared/reference/operating-principles.md +222 -0
  220. package/plugin/shared/reference/slice-doc-schema.md +566 -0
  221. package/plugin/shared/templates/conformance-report.md +85 -0
  222. package/plugin/shared/templates/constitution.md +136 -0
  223. package/plugin/shared/templates/model-readme.md +46 -0
  224. package/plugin/shared/templates/slice.md +251 -0
  225. package/plugin/shared/templates/state.md +77 -0
  226. package/plugin/skills/conform/SKILL.md +93 -0
  227. package/plugin/skills/conform/reference/conform.md +428 -0
  228. package/plugin/skills/design/SKILL.md +297 -0
  229. package/plugin/skills/discover/SKILL.md +90 -0
  230. package/plugin/skills/discover/reference/extract.md +147 -0
  231. package/plugin/skills/engagement/SKILL.md +193 -0
  232. package/plugin/skills/engagement/reference/engagement.md +144 -0
  233. package/plugin/skills/event-modeling/SKILL.md +132 -0
  234. package/plugin/skills/implement/SKILL.md +61 -0
  235. package/plugin/skills/implement/reference/implement.md +439 -0
  236. package/plugin/skills/review/SKILL.md +74 -0
  237. package/dist/emit/typespec.d.ts +0 -26
  238. package/dist/emit/typespec.js +0 -356
  239. package/dist/emit/typespec.js.map +0 -1
@@ -0,0 +1,329 @@
1
+ <!-- GENERATED FILE from .claude/skills/event-modeling-shared/reference/methodology.md by scripts/build-plugin.ts — do not hand-edit; run npm run docs:generate -->
2
+ # Event Modeling — Methodology Reference
3
+
4
+ This is the authoritative reference for the **7 steps** and **4 patterns** of Event Modeling
5
+ (Adam Dymitruk's method). Consult it while running any phase of the `event-modeling` skill.
6
+ Do not invent domain facts — use the Socratic prompts to extract them from the user.
7
+
8
+ ---
9
+
10
+ ## Core idea
11
+
12
+ An event model is a **timeline of state changes** told as a story. Information only moves
13
+ three ways:
14
+
15
+ 1. **Into** the system via a **command** (a request to change state) → produces an **event**.
16
+ 2. **Out of** the system via a **read model / view** (a projection of past events) → shown on a **UI** or consumed by an automation.
17
+ 3. There is **no other way** for information to flow. Every box on the diagram is one of:
18
+ UI screen, command, event, read model, or automation/processor.
19
+
20
+ Concretely, that leaves exactly six legal connections, and `em` infers only these:
21
+ `ui → command`, `command → event`, `event → read model`, `read model → ui`,
22
+ `read model → reaction`, `reaction → command`. Every other pair is a rule violation, and an
23
+ explicit `arrow` writing one is a validation error. The two worth naming, because they are the
24
+ tempting shortcuts:
25
+
26
+ - **`command → read model`** — the CQRS violation. A write is only ever visible to a reader
27
+ through the event it recorded. Put the event between them.
28
+ - **`read model → command`** — reads never drive a write directly. A processor or translation
29
+ watches the read model and issues the command that sits in the same slice.
30
+
31
+ If you find yourself wanting an arrow the patterns don't allow, the model is missing an
32
+ **element**, not an arrow.
33
+
34
+ **Automations and translations are not exceptions to rule 1.** They are *reactions* — a processor
35
+ or an adapter that wakes up, decides something must change, and **issues a command** to do it. They
36
+ never record an event themselves. A reaction box always points at a **command in its own slice** —
37
+ the same shape a `ui` already uses in State Change — and that command produces the event. If you
38
+ ever see a translation or automation wired straight to an event, the model is wrong.
39
+
40
+ Events are **immutable past-tense facts** ("Order Placed", "Payment Captured"). They are the
41
+ spine of the model. Everything else hangs off the events.
42
+
43
+ The unit of delivery is a **slice**: a thin vertical cut through the swimlanes that delivers
44
+ one of the four patterns. A slice is what a developer implements and tests in isolation.
45
+
46
+ ---
47
+
48
+ ## The 4 patterns
49
+
50
+ Every slice is exactly one of these. In `em`, each maps to a specific shape (see `em-dsl.md`).
51
+
52
+ ### 1. State Change (Command pattern)
53
+ **UI → Command → Event.** A user (or automation) submits a command; the system validates it
54
+ against invariants and records one or more events.
55
+ - **A State Change never travels alone, and it's joined at both ends.** Something must *trigger*
56
+ the command — the `ui` it's issued from, or (patterns 3 and 4) the reaction that triggers it,
57
+ also in this slice — and its event needs a read model that projects it. So the unit of work is a State Change
58
+ slice *plus* the State View slice that reads its event. Write them together; a command nothing
59
+ points at is a write nobody can start, an unread event is a write nobody can see, and
60
+ `em validate` warns on each.
61
+ - `em` shape (one element per line — there is no one-line slice form):
62
+ ```em
63
+ slice "Do The Thing" {
64
+ ui X @Persona
65
+ command Y
66
+ event Z @Context
67
+ }
68
+ slice "See The Result" { # the State View slice that reads Z — pattern 2, but required here
69
+ view V from "Z"
70
+ ui Screen @Persona
71
+ }
72
+ ```
73
+ - This is where **invariants** live — the command is rejected if a rule is violated.
74
+ - Socratic prompts: *"What does the user do here? What request are they making? What must be
75
+ true for it to succeed? What fact gets recorded when it does? What gets rejected and why?"*
76
+
77
+ ### 2. State View (View pattern)
78
+ **Event(s) → Read Model → UI.** Past events are projected into a read model that a screen
79
+ displays. Read-only; changes no state.
80
+ - `em` shape: `slice { view V from "Event A", "Event B" ui Screen @Persona }`
81
+ - **Headless / API systems:** still use `ui`/`persona` — declare a persona per external
82
+ caller/role (e.g. `IntegratorAPI`) and treat its `ui` boxes as API calls, not screens. Shape:
83
+ `slice { view V from "..." ui Read V @IntegratorAPI }`, same as any State View slice.
84
+ `translation` stays reserved for genuine reactions/external-system boundaries (pattern 4), not
85
+ for a synchronous request/response API call.
86
+ - **Repeat read models across the timeline.** A read model is drawn fresh in **every** slice where
87
+ it is read — after the events that update it, before the actions that consume it — so the diagram
88
+ shows state flowing left-to-right (the information-completeness staircase). The same read-model
89
+ name recurring is intentional and **renders cleanly**. Declare every instance after the first with
90
+ **`view V again from "..."`**: `again` instances are exempt from the duplicate-name warning even
91
+ when something references them, and each reference resolves to the right instance. (A plain repeat
92
+ only stays warning-free while nothing references it by name, and resolves to the *first*
93
+ declaration when something does.) **Wire each event to a read model
94
+ exactly once:** a repeated instance's `from` lists only the **new** events since the previous
95
+ instance (not cumulative) — otherwise an event draws a duplicate arrow to the same read model at
96
+ every repeat. (An event may still feed several *different* read models, once each.)
97
+ - **Instances are never connected to one another.** There is no arrow between two appearances of one
98
+ read model, and an explicit one is a validation error. The repeat is an ergonomic device for
99
+ showing the same read model at successive points in time — continuity is implied by the shared
100
+ name, and the events arriving at each instance are what show it changing. An arrow between them
101
+ would say the read model feeds itself.
102
+ - **Place each repeat immediately after its feeding event (span-1).** The cleanest staircase puts a
103
+ read-model instance right after each event that updates it, sourcing **only that one adjacent
104
+ event** — every `event → read model` arrow is then short and forward-flowing. A read model sitting
105
+ far from its source events (e.g. a "list/queue" read placed early but fed by late events) draws
106
+ long arrows that sweep across the diagram. The renderer routes them *around* intervening boxes
107
+ rather than through them, so they no longer *look* like a forbidden read→read link — but the
108
+ distance itself is the problem: the arrowhead lands columns away from the event that produced it,
109
+ so **the write slice reads as dangling**, as if nothing consumed its event. A reader has to trace
110
+ the line across the diagram to see otherwise. The fix is never a `view → view` edge; it's to
111
+ repeat the read model next to each event that feeds it, and to keep a sub-flow that detours into
112
+ another context together rather than parking it at the end of the model.
113
+ - Socratic prompts: *"What does the consumer need to see to make their next decision? Which past
114
+ events provide that information? Is this a screen or an API read? What's the freshness/consistency
115
+ expectation?"*
116
+
117
+ ### 3. Automation (Processor pattern)
118
+ **Read Model → Processor → Command → Event, the last three together in one slice.** The system
119
+ reacts on its own: a processor watches a read model (a "to-do list" of work) and **issues a
120
+ command** — it never records an event directly.
121
+ - `em` shape: the read model in the slice before, then the reaction with its command and event —
122
+ plus the read slice that consumes the event — three slices in all:
123
+ ```em
124
+ slice "Todo" {
125
+ view Todo from "..."
126
+ }
127
+ slice "Do It" { # processor, command, and event all share this slice
128
+ processor P from "Todo"
129
+ command C
130
+ event E @Context
131
+ }
132
+ slice "Todo — done" {
133
+ view Todo again from "E" # every event needs a reader; here it clears the to-do list
134
+ ui Work Board @Persona # ...and every read model needs a consumer
135
+ }
136
+ ```
137
+ The processor never records the event directly — the command it triggers does, and that event
138
+ happens to live in the processor's own slice now because the command does. A processor with no
139
+ command in its slice (and no explicit arrow to one) is a validation warning: a decision the
140
+ system never acts on. Forgetting the third slice leaves `E` unread, which is also a warning.
141
+ - **Naming:** name the read model after the pending work — `Payments To Process`,
142
+ `Orders To Fulfill`, `Pending Approvals` — never after the triggering event. The event is what
143
+ happened; the view is what's left to do. Reusing the event's name also collides in the shared
144
+ element namespace: `from` references then resolve by element kind, and `em validate` flags the
145
+ duplicated name.
146
+ - Socratic prompts: *"What should happen without a human? What condition triggers it? What work
147
+ list does the processor watch? What command does it fire? What if it fails or retries?"*
148
+
149
+ ### 4. Translation
150
+ **Boundary crossing → command → event, the reaction and what it triggers together in one slice.**
151
+ An adapter translates data across a boundary (an external system, or another bounded context) into
152
+ the model's own language. Like an automation, a translation is a *reaction*: it **triggers a
153
+ command**, never records an event directly. Two independent questions shape which form it takes —
154
+ don't conflate them:
155
+ - **Trigger source:** does the input come from outside the model, or from the model's own state
156
+ pushed back out?
157
+ - **Durable artifact:** is there a queryable, persisted thing behind the trigger (a queue, topic,
158
+ or log), or is it an ephemeral call with nothing to query afterward?
159
+
160
+ The `em` shape only tracks the second question — trigger source doesn't change it:
161
+ - **No durable artifact:** `external input → translation → command → event`. No internal `from`.
162
+ Typically (not necessarily) externally triggered — a bare webhook call with nothing persisted
163
+ behind it.
164
+ - **Durable artifact:** `read model → translation → command → event`. The translation reads a
165
+ **view** via `from`. This shape is the same whether the view was filled by the model's own event
166
+ (internally triggered, e.g. pushing data out) or by an external system's persisted queue
167
+ (externally triggered, e.g. a webhook whose inbound message is stored first for retries,
168
+ ordering, or audit) — an externally triggered translation backed by a real queue is
169
+ architecturally closer to this case than to the no-artifact one above.
170
+ - **Event legitimacy:** a received external message counts as a legitimate domain `event` when
171
+ it's scoped to the context/lane whose fact it represents, not by who committed it — the same
172
+ move as an Anti-Corruption-Layer boundary event in DDD — and stays legitimate as long as it
173
+ doesn't leak into the model's own domain vocabulary.
174
+ - `em` shape: exactly like an automation — the read model (durable-artifact form only) in the
175
+ slice before, then the reaction with its command and event, plus the read slice for the event:
176
+ ```em
177
+ slice "Boundary" {
178
+ view Source from "..." # durable-artifact form only; omit if there's nothing persisted
179
+ }
180
+ slice "Record It" { # translation, command, and event all share this slice
181
+ translation T from "Source" # omit `from` entirely for the no-durable-artifact form
182
+ command C
183
+ event E @Context
184
+ }
185
+ slice "Source — recorded" {
186
+ view Source again from "E" # every event needs a reader
187
+ ui Status Screen @Persona # ...and every read model needs a consumer
188
+ }
189
+ ```
190
+ The translation never records the event directly — the command it triggers does.
191
+ - Socratic prompts: *"What boundary are we crossing, and which way? What outside system or context
192
+ feeds us (or do we feed)? In what format? How do we know its data is trustworthy/complete? What
193
+ internal **command** does it trigger, and what event does that command record?"*
194
+
195
+ ---
196
+
197
+ ## The 7 steps
198
+
199
+ Run them in order. The skill groups them into phases: **discover = 1–4**, **model = 5–7**,
200
+ and a dedicated **slice** phase deepens step 6 to implementation-ready specs.
201
+
202
+ ### Step 1 — Brainstorm Events *(discover)*
203
+ List the domain **events** as past-tense facts, unordered at first. Go wide; capture every
204
+ state change anyone can think of. No commands, no UI yet — just facts.
205
+ - Prompts: *"What are all the things that happen in this process? Say each as something that
206
+ already occurred. What changed when that happened?"*
207
+ - **Is it an event?** Events here are *business* facts, not technical ones. Test: if this fact
208
+ failed to occur, would it wake the CEO at 3am? Yes → event. No → not an event.
209
+ - **Yes:** Order Created, Item Added to Order, Order Shipped, Order Delivered, Order
210
+ Cancelled, User Registered, Item Provisioned.
211
+ - **No — derived values** (a read model computes these from other events; they aren't
212
+ independent facts): Order Total Calculated, Order Tax Calculated, Discount Applied.
213
+ - **No — activity, not business state** (telemetry, not something the domain decides on):
214
+ User Clicked Button, User Viewed Page, User Logged In.
215
+ - Model the process the business **needs**, not how the current system happens to work. Existing
216
+ system behavior is a common source of fake events — screens and tables leaking in as facts.
217
+ (**Extract mode inverts this**: in the `em:discover` skill's `extract` phase the
218
+ existing system *is* the subject and this caution is deliberately suspended — see the
219
+ "Extract" section below and `em:discover`'s own `reference/extract.md`. The
220
+ derived-value and telemetry filters still apply there.)
221
+ - Output: a flat list of candidate `event` names.
222
+
223
+ ### Step 2 — The Plot / Storyboard *(discover)*
224
+ Put the events in **timeline order** to form the narrative. Identify the **personas** (actors)
225
+ and sketch the **UI** screens that move the story forward. This is the storyboard.
226
+ - Prompts: *"In what order do these happen? Who is on screen at each step? What screen are they
227
+ looking at? What's the happy-path story start to finish?"*
228
+ - Output: ordered events + `persona` list + `ui` screens per step.
229
+
230
+ ### Step 3 — Inputs (Commands) *(discover)*
231
+ For each event, identify the **command** that causes it (State Change pattern). Name the intent,
232
+ not the outcome ("Place Order", not "Order Placed").
233
+ - Prompts: *"What action produces this event? Who or what issues it? Could it be refused?"*
234
+ - Output: `command → event` pairs.
235
+
236
+ ### Step 4 — Outputs (Read Models) *(discover)*
237
+ Identify the **read models / views** each UI and automation needs (State View pattern). Wire
238
+ each view to the events that feed it.
239
+ - Prompts: *"What information does this screen show? Which past events supply it? Does any
240
+ automation need a work list derived from events?"*
241
+ - Output: `view` elements with their `from "Event"` sources.
242
+
243
+ ### Step 5 — Swimlanes & Apply the Patterns *(model)*
244
+ Organize elements into swimlanes: one **persona** row per actor, one **context** (bounded
245
+ context / aggregate) row per event family. Classify every slice as one of the **4 patterns** and
246
+ wire them correctly — especially **share the slice between every automation or translation and the
247
+ command it triggers** (the read model it watches, if any, stays in the slice before). A translation
248
+ or automation never records an event directly; it triggers a command.
249
+ - Prompts: *"Which events belong together as one consistency boundary (aggregate)? Who owns this
250
+ data? Is this slice a state change, a view, an automation, or a translation?"*
251
+ - Output: a structurally complete model with swimlanes and pattern-correct slices.
252
+
253
+ ### Step 6 — Elaborate Scenarios *(model first pass, slice deep pass)*
254
+ For each slice, write **Given / When / Then** scenarios and surface **invariants**, **critical
255
+ fields**, and **alternate / error flows**. In the `model` phase do a light first pass; the
256
+ dedicated `slice` phase writes the full rich spec (see `../templates/slice.md`).
257
+ - Prompts: *"Given what starting state, when this command/trigger fires, then what event(s)
258
+ result? What must always be true? What are the failure paths? Which fields are essential and
259
+ what are their rules?"*
260
+ - **Existing codebase:** before finalizing field names/types, check adjacent real sources
261
+ (OpenAPI specs, DB migrations, existing DTOs/event classes in sibling contexts) rather than
262
+ guessing — mirrors `extract.md`'s whole-system grounding, applied per-slice.
263
+ - Output: rich slice docs linked into the `.em` via `note "slices/<name>.md"`.
264
+
265
+ ### Step 7 — Evaluate Completeness *(model)*
266
+ Walk the whole model with stakeholders and check for loose ends:
267
+ - Every **command** has something that **triggers** it — a `ui` in its slice, or the reaction that
268
+ triggers it, also in its slice. A command nothing points at is a write nobody can start.
269
+ `em validate` warns.
270
+ - Every **command** produces at least one **event**.
271
+ - Every **view** has at least one source event, **and a consumer** — a `ui` (headless: the `ui`
272
+ tagged to the API-caller persona), or a reaction watching it. Every *instance* of a repeated
273
+ view, not just the last one.
274
+ A read model nothing displays is information projected out and then dropped. `em validate` warns.
275
+ - Every **event** is **read by a read model** — a `view` naming it in `from` (any `again` instance
276
+ counts), or a `view` with no `from` sitting in its slice. A reaction consuming it does **not**
277
+ count: reactions read views, not events. An event nothing projects is a write nobody can see, so
278
+ there was no point recording it. `em validate` warns on each one.
279
+ - Every **UI** is reachable and leads somewhere.
280
+ - Every connection is one of the six legal pairs (`ui → command`, `command → event`,
281
+ `event → read model`, `read model → ui`, `read model → reaction`, `reaction → command`). No
282
+ arrow between two instances of one read model.
283
+ - Automations **and** translations are wired correctly (reaction → command → event, all in one
284
+ slice — never a reaction wired straight to an event, and never a reaction with no command at
285
+ all); translations cover every external input.
286
+ - Run `em validate` and resolve all errors and warnings. It catches illegal `arrow` kind pairs
287
+ (error), unread events (warning), and a reaction with no command in its slice (warning) — a
288
+ reaction wired straight to an event with no command anywhere is exactly what that last check
289
+ catches, so a clean `em validate` run does cover this case.
290
+ - Prompts: *"Is there any event nobody reads — and if the business genuinely never looks at it, why
291
+ are we recording it? Any command that just happens, with nobody and nothing asking for it? Any
292
+ screen with no way in or out? Any command that records nothing? Any
293
+ external system we haven't translated? Any translation or automation that records an event instead
294
+ of triggering a command?"*
295
+
296
+ ---
297
+
298
+ ## Extract: current-state models of existing systems
299
+
300
+ The `extract` phase derives a model from an **existing** system instead of a greenfield
301
+ conversation. It is discover's as-is sibling: its confirm-and-clarify rounds converge the same
302
+ step 1–4 outputs (events, timeline, commands, read models) — *derived from the system*, then
303
+ confirmed with the user — after which the model enters `model` (steps 5–7) as usual.
304
+
305
+ The stance inversion, stated authoritatively: **extraction captures how the system behaves
306
+ today — faithfully, warts included — and never invents future or desired state.** Step 1's
307
+ anti-current-system caution is suspended for the duration; the "is it an event?" filter is
308
+ not — derived values and telemetry are still rejected even when the current system emits them.
309
+ Unknown or ambiguous current behavior is parked, never guessed — mirrored in the state file's
310
+ Open Questions, and on the element itself with `issue "text"` when it should also show up on the
311
+ rendered diagram (preferred over a bare `# TBD` comment, which is invisible once rendered).
312
+ Desired-state improvements belong to the `model` and `slice` phases, after the as-is picture is
313
+ agreed.
314
+
315
+ Procedure, source modes (event-driven vs. procedural synthesis), and the round-by-round loop:
316
+ the `em:discover` skill's own `reference/extract.md`.
317
+
318
+ ---
319
+
320
+ ## Socratic stance (applies throughout)
321
+
322
+ - Ask **one focused question at a time**; never assume a domain fact — extract it.
323
+ - Prefer "why", "what if", "who", "what must always be true", "how do you know" over yes/no.
324
+ - Mirror the model back after each increment and **re-render** so the team sees it evolve.
325
+ - Park unresolved questions in the state file instead of guessing.
326
+ - Name things crisply: events past-tense, commands imperative, views as the thing-shown.
327
+ - **Live workshop, one chat interface:** route through a single human proxy who relays questions
328
+ to the room and reports answers back; attribute every answer/decision in the Decisions log to
329
+ the named participant who gave it (see the state file's Participants section).
@@ -0,0 +1,222 @@
1
+ <!-- GENERATED FILE from .claude/skills/event-modeling-shared/reference/operating-principles.md by scripts/build-plugin.ts — do not hand-edit; run npm run docs:generate -->
2
+ # Operating principles, preconditions, and project layout (every phase)
3
+
4
+ Shared by every `em` plugin skill. Read this before doing any work in any phase — it's
5
+ the discipline and mechanics that don't change no matter which phase you're facilitating.
6
+ `methodology.md` (the 7 steps + 4 patterns) and `em-dsl.md` (DSL syntax and validation rules) in
7
+ this same directory are the other two shared references; read them too before doing real work —
8
+ they, together with this doc, are the source of truth every phase skill builds on. Templates
9
+ live in `templates/`, a sibling of this `reference/` directory. References to `docs/*.md`
10
+ throughout these skills mean the
11
+ [em repository's docs](https://github.com/milehimikey/em/tree/main/docs) — they are not
12
+ vendored alongside these skills, with one exception: the frontmatter schema doc is vendored
13
+ right here as `reference/slice-doc-schema.md` (kept in sync with the em repo's
14
+ `docs/slice-doc-schema.md` by `em`'s own `docs:generate`/`docs:check`), so every
15
+ `slice-doc-schema.md` reference elsewhere in this bundle points there instead of at `docs/`.
16
+
17
+ ## Operating principles
18
+
19
+ - **Socratic, one question at a time.** Ask focused questions; use `AskUserQuestion` for tight
20
+ multiple-choice decisions. Never assume a domain fact — extract it. Prefer "who/why/what-if/
21
+ what-must-always-be-true/how-do-you-know" over yes-no.
22
+ - **Don't guess — park it.** Unresolved items go into the Open Questions list in
23
+ `.event-modeling.md`, not into invented model content. Note the source, blocker, and revisit
24
+ date when known — cheap now, useful when you come back to it.
25
+ - **Happy path first; branches belong to slicing.** Steps 1-7 build the **happy-path spine**.
26
+ Alternate, unhappy, and exception-path events (rejections, removals, cancellations,
27
+ expirations, declines) are **not** enumerated as a separate discover/model task — they
28
+ surface during the `slice` phase as each slice's alternate/error flows and rule-boundary
29
+ scenarios, and any new branch events get added to the `.em` then. Never list "draft the
30
+ branch events" as a discover/model to-do or ask the user to do it before slicing.
31
+ - **Reflect and re-render.** After each meaningful increment, update the `.em` file, re-render,
32
+ and show the user what changed. Encourage running the live view (`watch`) so a team can follow.
33
+ - **Keep the `.em` structural; put depth in `note` docs.** The diagram holds flow; invariants,
34
+ fields, and scenarios live in `slices/*.md` linked via `note "slices/<name>.md"`.
35
+ - **Headless/API models still use `ui`.** If the system is headless (clients call an API, not
36
+ screens), declare a persona per external caller/role (e.g. `IntegratorAPI`) and treat its `ui`
37
+ boxes as API calls, not screens: writes are `ui → command → event` and reads are
38
+ `read model → ui`, exactly like any other State Change/State View slice — no separate trigger
39
+ slice. `translation` stays reserved for genuine reactions and real external-system boundaries,
40
+ not a synchronous request/response call. Internal-only commands/views with no public route carry
41
+ no `ui` at all — they follow the ordinary Automation pattern instead. **Repeat read models**
42
+ across slices so the timeline flows left-to-right — put each repeat **right after the event that feeds it**, sourcing
43
+ only that one adjacent event, so every arrow is short (a read model far from its source events
44
+ draws a long arrow whose head lands columns away, making the *write* slice read as dangling —
45
+ keep a sub-flow that detours into another context together, not parked at the end of the model).
46
+ Declare every instance after the first with
47
+ **`view X again from "..."`** — `again` instances are exempt from the duplicate-name warning even
48
+ when referenced, and each reference resolves to the right instance. A reaction's command is
49
+ wired by same-slice presence, exactly like `ui → command` — no positional/adjacency rule to get
50
+ right (see `em-dsl.md`).
51
+ See `methodology.md` (State View) and `em-dsl.md`.
52
+ - **Never connect two instances of one read model.** The repeat is a timeline device, not a flow:
53
+ continuity is implied by the shared name, and the events arriving at each instance are what show
54
+ it changing. An arrow between instances says the read model feeds itself, and is an error.
55
+ - **Every slice is a COMPLETE pattern, never a half-slice.** A State Change is
56
+ `ui → command → event`; a State View is `event → read model → ui` (or `→ reaction`). So: a
57
+ command needs something that **triggers** it (the `ui` it's issued from, or the reaction that
58
+ triggers it, also in this slice), its event needs a **reader**, and that read model needs a
59
+ **consumer** of its own. A command nothing points at is a write nobody can start; a reaction
60
+ that triggers no command is a decision the system never acts on; an event nothing projects is
61
+ a write nobody can see; a read model nothing displays is information dropped on the floor.
62
+ Write the whole chain together rather than sweeping up dangling ends later. Reactions don't
63
+ count as readers of *events* — they read views. **Every instance** of a repeated read model
64
+ needs its own consumer: if you repeat a view next to an event just to keep the arrow short,
65
+ bring its screen along, or don't add the instance.
66
+ - **Only six connections are legal:** `ui → command`, `command → event`, `event → read model`,
67
+ `read model → ui`, `read model → reaction`, `reaction → command`. Reaching for anything else —
68
+ above all `command → read model` (the CQRS violation) or `read model → command` — means the model
69
+ is missing an **element**, not an arrow.
70
+ - **Validate continuously.** Run `em validate` and fix errors/warnings as you go (see DSL ref).
71
+ - **Save state at the end of every session** so work resumes cleanly. Also log a Usage log
72
+ line: `em state log-usage <model>.em --phases <phase1,phase2,...>` computes and dedupes the
73
+ diagnostic *categories* that fired for you and appends the canonically-formatted line — never
74
+ hand-run `em validate --json` and format the line yourself. This is the team's only usage
75
+ signal today (`docs/usage-data.md`) — keep it cheap and habitual, not a task to skip.
76
+
77
+ ## Write scope and evidence (MIL-236)
78
+
79
+ These bind every design session — `discover`, `model`, `slice` — whichever skill runs it,
80
+ including sessions started from bare `/em:event-modeling`.
81
+
82
+ - **Write scope: one model directory.** The session is bound to the model directory it started
83
+ in: `dirname(<model>.em)`, which holds `slices/`, `model-versions/`, `conformance/`,
84
+ `contracts/`, `.event-modeling.md` and `README.md`. Never write under another model's
85
+ directory or to another model's contract file (`<model dir>/contracts/<model key>.tsp`). When
86
+ you need a neighbour, read only its public surface, read-only: the elements with
87
+ `public: true` in `em export <neighbour>.em`, or its committed contract file. Never open the
88
+ neighbour's slice docs or state file.
89
+ - **Seam change request instead of a cross-model edit.** When resolving an open question needs
90
+ another model's `public` element changed (a field added, renamed, retyped; an element made
91
+ `public`), record it in THIS model's `.event-modeling.md` `## Decisions log` as one dated
92
+ bullet, exactly this shape (`em changelog` reads only dated bullets):
93
+
94
+ ```
95
+ - YYYY-MM-DD: seam change request → <producerKey>:<kind>.<slug> — <what> — <why>
96
+ ```
97
+
98
+ `<producerKey>:<kind>.<slug>` is the same ref a `consumes` clause uses (`kind` is `event` or
99
+ `view`; write `command` for a public command). Leave the open question open with
100
+ `blocked on: <producerKey> owners (seam change request YYYY-MM-DD)`, and tell the user to take
101
+ the request to the producer's owners. Never edit the other side.
102
+ - **Out of scope during design — never write these:**
103
+ - generated contract artifacts: `contracts/*.tsp`, any OpenAPI or JSON-schema output;
104
+ - SDD/spec artifacts: `specs/`, `.specify/` (read `.specify/memory/constitution.md` when a
105
+ slice needs the house rules; never write it);
106
+ - implementation source: `src/`, and any path under the model's `Code roots:` bullet;
107
+ - `plugin/` (the generated skill plugin);
108
+ - a draft slice doc's `version:` (see the version rule below).
109
+
110
+ One exception, and only this one: when the session marks an element `public` (or changes a
111
+ field of a `public` element), run `em api generate <model>.em` before handing slices to
112
+ ratification. The API-first gate (`em validate --slice-ready`) refuses a public-touching
113
+ slice whose contract is missing or stale, so the regenerated `contracts/<model key>.tsp` is
114
+ committed with the model edit. Never hand-edit it.
115
+ - **Evidence rule.** The `.em` model and the slice docs are the evidence for every modeling
116
+ decision. Never open implementation source, generated contracts, or `specs/` to settle a
117
+ modeling question; ask the user. Code is consulted only in `extract`
118
+ (`em:discover`) and `conform` (`em:conform`), where reading it is the
119
+ point.
120
+ - **Version rule: never bump a draft's `version:` on edit.** A `draft`/`reviewed` doc is edited
121
+ in place. Check the release state with `em status <model>.em --json`: the model is
122
+ **pre-release** when its `modelVersion` entry has `design: null` and
123
+ `slices.byStatus.implemented === 0`. Pre-release: never run `em slice reratify`; edit
124
+ `draft`/`reviewed` docs in place and leave `version:` as it is. A pre-release doc that is
125
+ already ratified (`ready-to-implement` with `ratifiedBy`) carries a human sign-off: STOP,
126
+ record the needed change as an open question, and let the ratifier decide. Released (either
127
+ condition false): changing an `implemented` or ratified doc goes through `em slice reratify`
128
+ (see `em:design`).
129
+ - **Pre-commit check (STOP on any finding).** Stage the session's files, then run both:
130
+ 1. `em system scope --staged` from the repository root. Any `seam-crossing` error → STOP:
131
+ unstage the other model's files and record a seam change request instead.
132
+ 2. `git diff --cached --name-only`. Any path outside the bound model directory, or any
133
+ out-of-scope path above (`contracts/*.tsp` except the regenerated exception, OpenAPI /
134
+ JSON-schema output, `specs/`, `.specify/`, `src/`, code roots, `plugin/`), or a draft
135
+ doc whose `version:` line changed (`git diff --cached -- <doc>`) → STOP: unstage it and
136
+ tell the user what was touched and why it is out of scope.
137
+
138
+ ## Preconditions (run first)
139
+
140
+ 1. Check the tool: `em --version`. If missing, tell the user to run `npm i -g @milehimikey/em`
141
+ and stop until installed.
142
+ 2. Check this vendored skill bundle's own currency (MIL-219): read this file's `em-version:`
143
+ frontmatter stamp (or any sibling skill's SKILL.md — they're stamped together) and compare it
144
+ to `em --version` from step 1. **If the installed em is ahead by a minor version or more
145
+ (1.9.x → 1.11.x, or any major bump), STOP** — don't proceed with discover/model/slice/
146
+ implement/conform/review work on this stamp. Tell the user the vendored bundle is stale and to
147
+ run `em skill sync` (refresh the bundle only) or `em upgrade` (the bundle plus the model
148
+ repo's other mechanical migrations, docs/upgrading.md) before continuing. A patch-only gap
149
+ (1.11.2 → 1.11.4) is never worth stopping over — proceed normally. This matters because a
150
+ stale bundle silently follows old rules: an agent reading superseded preconditions/gates
151
+ can hand back work that looks right by last week's contract and wrong by this week's.
152
+ 3. Locate the model. Look for an existing `<dir>/.event-modeling.md` and `*.em` in the working
153
+ directory, or — for a multi-model project laid out per the convention below — one level down
154
+ inside a `models/` subfolder (each model gets its own `models/<slug>/` directory; check each
155
+ candidate for its own `.event-modeling.md`/`*.em` pair). If found, run `em state read <dir>`
156
+ to read the state file's mechanical fields (model path, phase, step, last updated, last
157
+ conformance, last review) as JSON — don't parse the bullets by hand. If not, and the phase
158
+ needs one, ask the user for the model name and where to create it (a lone model goes at the
159
+ project root; one of several goes under `models/`, per "Multi-model projects" below).
160
+ 4. If you're not sure which phase applies, or the user gave no phase, use `em state read <dir>`'s
161
+ `phase`/`step` to resume the recorded phase/step — or invoke the `event-modeling` skill, which
162
+ does exactly this and routes to the right skill. If no model exists, propose starting
163
+ `discover` (greenfield, `em:discover`) or `extract` (modeling an existing system,
164
+ also `em:discover`).
165
+ 5. Populate the state file's Participants section at session start. For a live workshop, ask for
166
+ a single human proxy to relay questions to the room, and attribute every answer/decision in
167
+ the Decisions log to a named participant.
168
+
169
+ ## Project layout these skills create
170
+
171
+ ```
172
+ <model-name>/
173
+ <model-name>.em # the model (slice docs linked via note "...")
174
+ <model-name>.svg # render target for em watch
175
+ README.md # overview + slice index (from templates/model-readme.md)
176
+ .event-modeling.md # resumable state (from templates/state.md)
177
+ slices/<slice-name>.md # one rich slice doc per slice (from templates/slice.md)
178
+ conformance/<date>-report.md
179
+ # conform-phase reports (from templates/conformance-report.md)
180
+ <model-name>-asis.em # conform-phase scratch model, regenerated per run — git-ignore this
181
+ ```
182
+
183
+ When creating a new model, run `em scaffold <model-name>` — it creates `<model-name>/` and
184
+ writes all three starter files in one step (`<model-name>.em`, `README.md`,
185
+ `.event-modeling.md`), filling the Model Name/model-name placeholders and the state file's
186
+ mechanical fields (model path, `Current phase: discover`, `Current step: 1`, dates) for you, so
187
+ there's nothing left to hand-copy or fill in. Refuses if `<model-name>/` already exists; pass
188
+ `--force` to overwrite. You'll
189
+ usually replace the scaffolded `.em`'s content as the discovery conversation builds up the real
190
+ model — it starts out as the same starter model `em init` writes, titled from `<model-name>`.
191
+ `slices/*.md` and `conformance/` aren't part of `em scaffold`; they come from the `slice` and
192
+ `conform` phases (`em:design` and `em:conform`). The `conformance/`
193
+ directory and `<model-name>-asis.em` only appear once the `conform` phase runs; `em conform-scope
194
+ --seed-asis` creates `<model-name>-asis.em` and gitignores `*-asis.em` for you (see
195
+ `em:conform`'s `reference/conform.md`) — it's scratch, never committed.
196
+
197
+ ### Multi-model projects
198
+
199
+ Slice docs resolve at a fixed path — `slices/<slug>.md`, sibling of the `.em` file that
200
+ declares the slice — with no model namespace anywhere, so **one directory per model** is the
201
+ whole guardrail against two models' slice names colliding. For more than one model, nest each
202
+ one's directory (the exact layout above) under a shared `models/` parent instead of scattering
203
+ them at the project root:
204
+
205
+ ```
206
+ models/
207
+ checkout/
208
+ checkout.em
209
+ slices/...
210
+ fulfillment/
211
+ fulfillment.em
212
+ slices/...
213
+ ```
214
+
215
+ Run `em scaffold <model-name> --under models` for each model — it creates `models/<slug>/`
216
+ directly rather than a two-step `cd models && em scaffold <model-name>`. See
217
+ [docs/cli.md, "Multi-model projects"](https://github.com/milehimikey/em/blob/main/docs/cli.md#multi-model-projects)
218
+ for the full rationale, and why slice keys stay unqualified (no `<model>/<slice>` prefix) —
219
+ directory isolation alone is sufficient. Two `.em` files sharing a directory with colliding
220
+ slice keys don't go undetected: `em status`/`em catalog`, the two commands that ever compile
221
+ more than one model in a run, raise a `cross-model-slice-doc-collision` warning naming both
222
+ files — treat that warning as a sign the models need separating, not a false positive.