@milehimikey/em 1.13.1 → 1.14.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 (235) 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 +50 -11
  6. package/.claude/skills/event-modeling-conform/SKILL.md +1 -1
  7. package/.claude/skills/event-modeling-design/SKILL.md +127 -18
  8. package/.claude/skills/event-modeling-discover/SKILL.md +1 -1
  9. package/.claude/skills/event-modeling-engagement/SKILL.md +192 -0
  10. package/.claude/skills/event-modeling-engagement/reference/engagement.md +143 -0
  11. package/.claude/skills/event-modeling-implement/SKILL.md +1 -1
  12. package/.claude/skills/event-modeling-implement/reference/implement.md +38 -11
  13. package/.claude/skills/event-modeling-review/SKILL.md +2 -2
  14. package/.claude/skills/event-modeling-shared/reference/em-dsl.md +279 -189
  15. package/.claude/skills/event-modeling-shared/reference/operating-principles.md +61 -0
  16. package/.claude/skills/event-modeling-shared/reference/slice-doc-schema.md +116 -5
  17. package/.claude/skills/event-modeling-shared/templates/constitution.md +18 -1
  18. package/.claude/skills/event-modeling-shared/templates/slice.md +64 -39
  19. package/.claude/skills/event-modeling-shared/templates/state.md +5 -1
  20. package/.claude-plugin/marketplace.json +14 -0
  21. package/README.md +25 -11
  22. package/dist/catalog/apiFirst.d.ts +28 -0
  23. package/dist/catalog/apiFirst.js +49 -0
  24. package/dist/catalog/apiFirst.js.map +1 -0
  25. package/dist/catalog/docJoin.d.ts +13 -0
  26. package/dist/catalog/docJoin.js +11 -0
  27. package/dist/catalog/docJoin.js.map +1 -1
  28. package/dist/catalog/docModelConsistencyValidate.js +44 -4
  29. package/dist/catalog/docModelConsistencyValidate.js.map +1 -1
  30. package/dist/catalog/invariantsValidate.d.ts +4 -0
  31. package/dist/catalog/invariantsValidate.js +102 -0
  32. package/dist/catalog/invariantsValidate.js.map +1 -0
  33. package/dist/catalog/scenarios.d.ts +15 -0
  34. package/dist/catalog/scenarios.js +97 -0
  35. package/dist/catalog/scenarios.js.map +1 -0
  36. package/dist/catalog/sliceDoc.d.ts +8 -0
  37. package/dist/catalog/sliceDoc.js +9 -0
  38. package/dist/catalog/sliceDoc.js.map +1 -1
  39. package/dist/catalog/sliceReadyValidate.d.ts +31 -4
  40. package/dist/catalog/sliceReadyValidate.js +66 -8
  41. package/dist/catalog/sliceReadyValidate.js.map +1 -1
  42. package/dist/catalog/sliceSections.d.ts +55 -0
  43. package/dist/catalog/sliceSections.js +317 -0
  44. package/dist/catalog/sliceSections.js.map +1 -0
  45. package/dist/cli/agentsMd.js +7 -1
  46. package/dist/cli/agentsMd.js.map +1 -1
  47. package/dist/cli/api.d.ts +90 -0
  48. package/dist/cli/api.js +318 -0
  49. package/dist/cli/api.js.map +1 -0
  50. package/dist/cli/ciInit.d.ts +82 -21
  51. package/dist/cli/ciInit.js +468 -64
  52. package/dist/cli/ciInit.js.map +1 -1
  53. package/dist/cli/conformScope.d.ts +28 -0
  54. package/dist/cli/conformScope.js +67 -0
  55. package/dist/cli/conformScope.js.map +1 -1
  56. package/dist/cli/coverage.d.ts +9 -0
  57. package/dist/cli/coverage.js +60 -8
  58. package/dist/cli/coverage.js.map +1 -1
  59. package/dist/cli/defer.d.ts +55 -0
  60. package/dist/cli/defer.js +244 -0
  61. package/dist/cli/defer.js.map +1 -0
  62. package/dist/cli/engagement.d.ts +210 -0
  63. package/dist/cli/engagement.js +444 -0
  64. package/dist/cli/engagement.js.map +1 -0
  65. package/dist/cli/engagementFile.d.ts +86 -0
  66. package/dist/cli/engagementFile.js +255 -0
  67. package/dist/cli/engagementFile.js.map +1 -0
  68. package/dist/cli/metrics.d.ts +10 -0
  69. package/dist/cli/metrics.js +49 -1
  70. package/dist/cli/metrics.js.map +1 -1
  71. package/dist/cli/pluginPin.d.ts +55 -0
  72. package/dist/cli/pluginPin.js +188 -0
  73. package/dist/cli/pluginPin.js.map +1 -0
  74. package/dist/cli/ratify.d.ts +27 -2
  75. package/dist/cli/ratify.js +112 -13
  76. package/dist/cli/ratify.js.map +1 -1
  77. package/dist/cli/reratify.d.ts +4 -2
  78. package/dist/cli/reratify.js +41 -16
  79. package/dist/cli/reratify.js.map +1 -1
  80. package/dist/cli/scopeInputs.d.ts +48 -0
  81. package/dist/cli/scopeInputs.js +176 -0
  82. package/dist/cli/scopeInputs.js.map +1 -0
  83. package/dist/cli/skillCheck.d.ts +15 -1
  84. package/dist/cli/skillCheck.js +22 -1
  85. package/dist/cli/skillCheck.js.map +1 -1
  86. package/dist/cli/skillDirs.d.ts +5 -0
  87. package/dist/cli/skillDirs.js +13 -1
  88. package/dist/cli/skillDirs.js.map +1 -1
  89. package/dist/cli/skillSync.d.ts +4 -0
  90. package/dist/cli/skillSync.js +23 -0
  91. package/dist/cli/skillSync.js.map +1 -1
  92. package/dist/cli/sliceNew.d.ts +11 -7
  93. package/dist/cli/sliceNew.js +91 -18
  94. package/dist/cli/sliceNew.js.map +1 -1
  95. package/dist/cli/sliceSync.d.ts +50 -0
  96. package/dist/cli/sliceSync.js +170 -0
  97. package/dist/cli/sliceSync.js.map +1 -0
  98. package/dist/cli/status.d.ts +24 -1
  99. package/dist/cli/status.js +20 -1
  100. package/dist/cli/status.js.map +1 -1
  101. package/dist/cli/systemInputs.d.ts +53 -10
  102. package/dist/cli/systemInputs.js +186 -11
  103. package/dist/cli/systemInputs.js.map +1 -1
  104. package/dist/cli/upgrade.d.ts +16 -3
  105. package/dist/cli/upgrade.js +366 -30
  106. package/dist/cli/upgrade.js.map +1 -1
  107. package/dist/cli/usageLog.d.ts +4 -2
  108. package/dist/cli/usageLog.js +4 -2
  109. package/dist/cli/usageLog.js.map +1 -1
  110. package/dist/cli.js +599 -94
  111. package/dist/cli.js.map +1 -1
  112. package/dist/emit/api.d.ts +29 -0
  113. package/dist/emit/api.js +220 -0
  114. package/dist/emit/api.js.map +1 -0
  115. package/dist/emit/apiCheckJson.d.ts +3 -0
  116. package/dist/emit/apiCheckJson.js +21 -0
  117. package/dist/emit/apiCheckJson.js.map +1 -0
  118. package/dist/emit/codeownersJson.d.ts +3 -0
  119. package/dist/emit/codeownersJson.js +19 -0
  120. package/dist/emit/codeownersJson.js.map +1 -0
  121. package/dist/emit/diffJson.d.ts +1 -1
  122. package/dist/emit/diffJson.js +8 -1
  123. package/dist/emit/diffJson.js.map +1 -1
  124. package/dist/emit/engagementJson.d.ts +8 -0
  125. package/dist/emit/engagementJson.js +46 -0
  126. package/dist/emit/engagementJson.js.map +1 -0
  127. package/dist/emit/glossaryJson.d.ts +1 -1
  128. package/dist/emit/glossaryJson.js +4 -1
  129. package/dist/emit/glossaryJson.js.map +1 -1
  130. package/dist/emit/json.d.ts +17 -1
  131. package/dist/emit/json.js +30 -1
  132. package/dist/emit/json.js.map +1 -1
  133. package/dist/emit/metricsJson.d.ts +1 -1
  134. package/dist/emit/metricsJson.js +4 -1
  135. package/dist/emit/metricsJson.js.map +1 -1
  136. package/dist/emit/queryJson.d.ts +1 -1
  137. package/dist/emit/queryJson.js +7 -1
  138. package/dist/emit/queryJson.js.map +1 -1
  139. package/dist/emit/scopeJson.d.ts +12 -0
  140. package/dist/emit/scopeJson.js +26 -0
  141. package/dist/emit/scopeJson.js.map +1 -0
  142. package/dist/emit/skillCheckJson.d.ts +3 -2
  143. package/dist/emit/skillCheckJson.js +10 -2
  144. package/dist/emit/skillCheckJson.js.map +1 -1
  145. package/dist/emit/statusJson.d.ts +1 -1
  146. package/dist/emit/statusJson.js +15 -1
  147. package/dist/emit/statusJson.js.map +1 -1
  148. package/dist/emit/systemJson.d.ts +15 -8
  149. package/dist/emit/systemJson.js +28 -13
  150. package/dist/emit/systemJson.js.map +1 -1
  151. package/dist/emit/validateJson.d.ts +3 -3
  152. package/dist/emit/validateJson.js +10 -4
  153. package/dist/emit/validateJson.js.map +1 -1
  154. package/dist/mcp/server.d.ts +1 -1
  155. package/dist/mcp/server.js +163 -31
  156. package/dist/mcp/server.js.map +1 -1
  157. package/dist/model/diff.d.ts +4 -1
  158. package/dist/model/diff.js +44 -8
  159. package/dist/model/diff.js.map +1 -1
  160. package/dist/model/glossary.d.ts +2 -0
  161. package/dist/model/glossary.js +1 -0
  162. package/dist/model/glossary.js.map +1 -1
  163. package/dist/model/model.d.ts +55 -2
  164. package/dist/model/model.js +57 -0
  165. package/dist/model/model.js.map +1 -1
  166. package/dist/model/qualifiedRef.d.ts +18 -0
  167. package/dist/model/qualifiedRef.js +26 -0
  168. package/dist/model/qualifiedRef.js.map +1 -1
  169. package/dist/model/queryIndex.d.ts +9 -1
  170. package/dist/model/queryIndex.js +18 -2
  171. package/dist/model/queryIndex.js.map +1 -1
  172. package/dist/model/rules.d.ts +143 -2
  173. package/dist/model/rules.js +162 -3
  174. package/dist/model/rules.js.map +1 -1
  175. package/dist/model/validate.d.ts +40 -0
  176. package/dist/model/validate.js +147 -1
  177. package/dist/model/validate.js.map +1 -1
  178. package/dist/parser/ast.d.ts +31 -3
  179. package/dist/parser/parser.js +148 -14
  180. package/dist/parser/parser.js.map +1 -1
  181. package/dist/query/format.js +1 -1
  182. package/dist/query/format.js.map +1 -1
  183. package/dist/query/verbs.d.ts +6 -0
  184. package/dist/query/verbs.js +6 -2
  185. package/dist/query/verbs.js.map +1 -1
  186. package/dist/system/adaptation.d.ts +14 -0
  187. package/dist/system/adaptation.js +49 -0
  188. package/dist/system/adaptation.js.map +1 -0
  189. package/dist/system/codeowners.d.ts +77 -0
  190. package/dist/system/codeowners.js +242 -0
  191. package/dist/system/codeowners.js.map +1 -0
  192. package/dist/system/manifest.d.ts +10 -5
  193. package/dist/system/manifest.js +54 -26
  194. package/dist/system/manifest.js.map +1 -1
  195. package/dist/system/migrateManifest.d.ts +28 -0
  196. package/dist/system/migrateManifest.js +155 -0
  197. package/dist/system/migrateManifest.js.map +1 -0
  198. package/dist/system/scope.d.ts +46 -0
  199. package/dist/system/scope.js +132 -0
  200. package/dist/system/scope.js.map +1 -0
  201. package/dist/system/verify.d.ts +50 -13
  202. package/dist/system/verify.js +179 -58
  203. package/dist/system/verify.js.map +1 -1
  204. package/dist/templates.d.ts +2 -2
  205. package/dist/templates.js +23 -2
  206. package/dist/templates.js.map +1 -1
  207. package/package.json +5 -2
  208. package/plugin/.claude-plugin/plugin.json +18 -0
  209. package/plugin/agents/em-critic.md +27 -0
  210. package/plugin/agents/em-implementer.md +35 -0
  211. package/plugin/agents/em-reviewer.md +32 -0
  212. package/plugin/agents/em-validator.md +28 -0
  213. package/plugin/shared/reference/em-dsl.md +871 -0
  214. package/plugin/shared/reference/methodology.md +329 -0
  215. package/plugin/shared/reference/operating-principles.md +222 -0
  216. package/plugin/shared/reference/slice-doc-schema.md +574 -0
  217. package/plugin/shared/templates/conformance-report.md +85 -0
  218. package/plugin/shared/templates/constitution.md +136 -0
  219. package/plugin/shared/templates/model-readme.md +46 -0
  220. package/plugin/shared/templates/slice.md +251 -0
  221. package/plugin/shared/templates/state.md +77 -0
  222. package/plugin/skills/conform/SKILL.md +93 -0
  223. package/plugin/skills/conform/reference/conform.md +428 -0
  224. package/plugin/skills/design/SKILL.md +298 -0
  225. package/plugin/skills/discover/SKILL.md +90 -0
  226. package/plugin/skills/discover/reference/extract.md +147 -0
  227. package/plugin/skills/engagement/SKILL.md +193 -0
  228. package/plugin/skills/engagement/reference/engagement.md +144 -0
  229. package/plugin/skills/event-modeling/SKILL.md +132 -0
  230. package/plugin/skills/implement/SKILL.md +61 -0
  231. package/plugin/skills/implement/reference/implement.md +439 -0
  232. package/plugin/skills/review/SKILL.md +74 -0
  233. package/dist/emit/typespec.d.ts +0 -26
  234. package/dist/emit/typespec.js +0 -356
  235. package/dist/emit/typespec.js.map +0 -1
@@ -0,0 +1,298 @@
1
+ ---
2
+ name: design
3
+ em-version: 1.14.1
4
+ description: >-
5
+ Use when structuring a draft event model into swimlanes and the four patterns (State Change,
6
+ State View, Automation, Translation), evaluating a model's structural completeness, or writing
7
+ implementation-ready deep slice documents (field tables, invariants, Given/When/Then scenarios,
8
+ error flows) for a model that already has its happy-path spine. Drives `em`'s model and slice
9
+ phases — the core modeling work between discovery and implementation.
10
+ ---
11
+ <!-- GENERATED FILE from .claude/skills/event-modeling-design/SKILL.md by scripts/build-plugin.ts — do not hand-edit; run npm run docs:generate -->
12
+
13
+ # Event Modeling — model & slice
14
+
15
+ You are facilitating the core modeling work: turning a draft happy-path spine into a
16
+ structurally complete, validated model, and then a deep implementation-ready spec per slice.
17
+ Your job is to **extract an accurate model from the user through Socratic questioning** — never
18
+ to invent the domain. You drive the `em` CLI to render the model live, and you produce
19
+ implementation-ready slice docs.
20
+
21
+ Read `${CLAUDE_PLUGIN_ROOT}/shared/reference/operating-principles.md` (preconditions, project
22
+ layout, the Socratic/validation discipline every phase follows) and
23
+ `${CLAUDE_PLUGIN_ROOT}/shared/reference/methodology.md` (the 7 steps + 4 patterns) before doing real
24
+ work — they are the source of truth. Run the preconditions there first: this phase expects a
25
+ draft model already exists (from `em:discover`'s `discover` or `extract` phase).
26
+
27
+ ## Write scope, evidence, versions (MIL-236) — read before any edit
28
+
29
+ Full rules: `${CLAUDE_PLUGIN_ROOT}/shared/reference/operating-principles.md` ("Write scope and
30
+ evidence"). In short:
31
+
32
+ - **Bound to this model's directory.** The session is bound to the model directory it started
33
+ in (`dirname(<model>.em)`). Never write under another model's directory or to its contract
34
+ file. Read a neighbour only through its public surface (`public: true` elements in
35
+ `em export <neighbour>.em`, or its committed contract), read-only.
36
+ - **Seam change request, never a cross-model edit.** When an open question needs another
37
+ model's `public` element changed, add one dated bullet to THIS model's `.event-modeling.md`
38
+ `## Decisions log`:
39
+ `- YYYY-MM-DD: seam change request → <producerKey>:<kind>.<slug> — <what> — <why>`.
40
+ Keep the question open, `blocked on:` the producer's owners. Never edit the other side.
41
+ - **Out of scope during design — never write:** generated contract artifacts
42
+ (`contracts/*.tsp`, any OpenAPI or JSON-schema output), SDD/spec artifacts (`specs/`,
43
+ `.specify/` — read the constitution, never write it), implementation source (`src/`, the
44
+ model's `Code roots:`), `plugin/`, and a draft slice doc's `version:`. Exception: after
45
+ marking an element `public` or changing a field of a `public` element, run
46
+ `em api generate <model>.em` and commit the regenerated contract with the model edit — the
47
+ API-first gate (`em validate --slice-ready`) refuses a public-touching slice whose contract
48
+ is missing or stale. Never hand-edit the contract.
49
+ - **Slice docs: edit authored sections only.** Never hand-edit a generated region
50
+ (`<!-- GENERATED:em-slice-…:start -->` … `:end -->`: field tables, the Invariants list); after
51
+ any model edit run `em slice sync <model>.em` (see **Generated vs authored sections** below).
52
+ - **Evidence rule.** The `.em` model and the slice docs are the evidence for modeling
53
+ decisions. Never read implementation source, generated contracts or `specs/` to decide a
54
+ modeling question — ask the user. Code is read only in `extract` and `conform`.
55
+ - **Version rule.** Never bump a draft's `version:` on edit. Before any `em slice reratify`,
56
+ run `em status <model>.em --json`: pre-release = the model's `modelVersion` entry has
57
+ `design: null` **and** `slices.byStatus.implemented === 0`. Pre-release → never run
58
+ `reratify`; edit `draft`/`reviewed` docs in place. Released → the re-ratification path in
59
+ the `slice` phase below.
60
+ - **Pre-commit check.** Stage the session's files, run `em system scope --staged` (STOP on any
61
+ `seam-crossing`), then `git diff --cached --name-only` (STOP on any path outside this model's
62
+ directory, any out-of-scope path above, or a changed `version:` line on a draft doc).
63
+ Unstage the offending path and tell the user why.
64
+
65
+ ## Phase: `model` — steps 5-7
66
+
67
+ Goal: a structurally complete, **validated** model with correct patterns and swimlanes.
68
+
69
+ 1. **Swimlanes & patterns (step 5).** Group events into **contexts** (bounded contexts /
70
+ aggregates) — ask which events share a consistency boundary and who owns them. Classify each
71
+ slice as one of the 4 patterns. **Share the slice between every automation or translation and
72
+ the command it triggers** (the reaction — processor/translation — together with the command
73
+ and event it produces; the read model it watches, if any, stays in the slice before). A
74
+ translation or automation is a reaction: it **triggers a command and never records an event
75
+ directly**. Add `translation` slices for external inputs (externally triggered: external →
76
+ translation → command → event, no slice before) and for the system reacting to its own state
77
+ (internally triggered: read model in the slice before → translation → command → event).
78
+ 2. **Elaborate scenarios — first pass (step 6).** Scaffold every slice's doc now, one per
79
+ slice, none skipped: `em slice new "<slice name>" --pattern <state-change|state-view|
80
+ automation|translation> --swimlane "<Persona> → <Context>" --wire <model>.em` writes
81
+ `slices/<slice-name>.md` at `status: draft` (the same command, flags, and `--wire`
82
+ note-binding the `slice` phase's step 2 below documents in full). A slice whose only element
83
+ is a `view X again` instance is a **continuation** (MIL-208), not a slice to scaffold —
84
+ `--wire` refuses it and names the originating slice holding `X`'s first declaration; its
85
+ scenarios go into that doc instead. Then, for each freshly scaffolded doc, hold a first-pass
86
+ Socratic pass and write the happy-path Given/When/Then into
87
+ its `## Scenarios (Given / When / Then)` section (as `### Scenario:` blocks — see **Generated
88
+ vs authored sections** in the `slice` phase below) and the obvious invariants known so far into
89
+ its `## Invariants / Business Rules` section — **into the doc, never into the state file.**
90
+ Keep this pass shallow (no field tables, alternate/error flows, or NFRs yet — that's the full
91
+ deep-dive in the `slice` phase below); the docs are the home for everything collected from
92
+ here on, not yet implementation-ready specs. Because every doc already exists by the time the
93
+ `slice` phase begins, its own step 2 scaffolding call is then a no-op — that phase picks up
94
+ at its step 1's deeper pass. For a purely exploratory/backbone-mapping model that only needs
95
+ status coloring right now (not a full spec per slice yet), `em slice stub-all <model>.em` is
96
+ the one-command fast path: a near-free stub per undocumented slice, wired, in slice order
97
+ (MIL-184) — skip the per-slice `em slice new` calls above and deepen individual docs later
98
+ when the team is ready.
99
+ 3. **Evaluate completeness (step 7).** Walk the model: every slice is a **complete** pattern, not a
100
+ half-slice — **every command has something that triggers it**, every command emits an event,
101
+ **every event is read by a read model**, **every read model has a consumer**, every view has a
102
+ source, every UI is reachable, every connection is
103
+ one of the six legal pairs, and every automation **and** translation triggers a command (none
104
+ wired straight to an event, none with no command at all). Run `em validate` and resolve all
105
+ errors and warnings — it catches a reaction wired straight to an event too now (see
106
+ `${CLAUDE_PLUGIN_ROOT}/shared/reference/em-dsl.md`), so a clean run covers this case.
107
+ For an unread event, don't just bolt on a view to silence the warning: ask the user who looks at
108
+ this fact and what they do with it. The honest answers are "here's the read model we missed" or
109
+ "nobody — so why are we recording it", and both improve the model.
110
+ **Per `view`, also ask who reads it (MIL-215):** a person or client reads it → draw the `ui`
111
+ that reads it; another model/system reads it → mark it `public`; only an automation reads
112
+ it → nothing, it's internal by design. No new keyword — reachability is already what `ui` and
113
+ `public` mean.
114
+ **Per translation fed from another model, declare what it consumes (MIL-235):** write
115
+ `translation Order Intake consumes checkout:event.order-submitted`. The ref is
116
+ `<modelKey>:<kind>.<slug>`, where kind is `event` or `view` and the target is marked `public`
117
+ in its own model. The consumer declares the seam; the producer never lists its consumers, and
118
+ the ref carries no version and no slice. Only a `translation` may carry `consumes`. If a
119
+ reaction reads another model, it is a translation. `em validate` checks only the ref's
120
+ grammar. Run `em system` from the repository (or on its `system.yaml`) to resolve it, and fix
121
+ any `consumes-unknown-model`/`consumes-unknown-element` error. Never edit the other model to
122
+ make a ref resolve. Ask its owner to publish the element instead, and record that ask as a
123
+ seam change request (see **Write scope** above).
124
+ **When you mark an element `public` here**, run `em api generate <model>.em` in the same
125
+ session and stage `contracts/<model key>.tsp` with the model edit — the one derived artifact
126
+ a design session produces, always by the command, never by hand.
127
+
128
+ End of phase: render, update state (`em state set-phase slice` — the mechanical marker the state
129
+ machine expects), and stop. Every slice already has a draft doc ready to receive the deep spec,
130
+ but slicing is deliberate work the team starts when it's ready for it, not this phase's automatic
131
+ next step.
132
+
133
+ ## Phase: `slice` — deep slice documents
134
+
135
+ Goal: implementation-ready specs. Go slice by slice **in timeline order, starting with the first
136
+ slice on the storyboard** — never ask the user which slice to spec first, and never propose
137
+ starting with whichever slice looks highest-risk or most uncertain instead. Ratification
138
+ proceeds in the same order: later slices' scenarios and invariants depend on earlier slices'
139
+ events already being settled, so working out of order means re-litigating an earlier slice's
140
+ contract mid-spec. Check `README.md`'s Slices table for what's already done (run `em slice index
141
+ <model-name>.em` first if it looks stale).
142
+
143
+ This is also where **branch / unhappy-path events** are discovered and added to the model — as a
144
+ slice's alternate/error flows surface (a rejection, removal, cancellation, decline, expiry), add
145
+ the corresponding event/slice to the `.em` and re-render. The happy-path spine from earlier
146
+ phases is the starting point, not the finished event set. **Every new event needs its reader too**
147
+ — a rejection or cancellation that nothing projects will warn, and usually the missing piece is
148
+ real (someone has to see that the request was declined).
149
+
150
+ For each slice:
151
+ 0. **Check for re-ratification first.** A `draft` or `reviewed` doc is never re-ratified: edit
152
+ it in place and leave its `version:` alone. If `slices/<slice-name>.md` already exists with
153
+ `status: implemented` (or is ratified and needs a change), run `em status <model>.em --json`
154
+ first. **Pre-release** (the model's `modelVersion` entry has `design: null` and
155
+ `slices.byStatus.implemented === 0`): never run `em slice reratify`; a ratified doc holds a
156
+ human sign-off, so STOP, park the change as an open question, and let the ratifier decide.
157
+ **Released** (either condition false): a doc with `status: implemented` is a
158
+ re-ratification, not fresh authoring: hold a Socratic
159
+ deep-dive on what changed (same rigor as step 1, scoped to the delta), update whichever
160
+ sections actually changed, then express the change as the `## Delta` section — fixed heading,
161
+ `### Added`/`Modified`/`Removed`/`Renamed` Requirement blocks each carrying its own scenarios
162
+ (see `${CLAUDE_PLUGIN_ROOT}/shared/templates/slice.md` and
163
+ `${CLAUDE_PLUGIN_ROOT}/shared/reference/slice-doc-schema.md#delta-section-grammar-and-lifecycle`)
164
+ — **overwriting** whatever the section held before, never appending to it. Then continue at
165
+ step 2 to bump `version` and flip `status` back to `ready-to-implement`, leaving
166
+ `implementedIn` naming the prior version's PR (intended drift signal, not a bug — see the
167
+ schema doc). Otherwise, this is first-time authoring: continue at step 1.
168
+ 1. Hold a Socratic deep-dive to fill every section of `${CLAUDE_PLUGIN_ROOT}/shared/templates/slice.md`:
169
+ intent, trigger/actor,
170
+ command fields (types & rules), event(s) + payload (mark immutable facts) — field names and
171
+ types go into the `.em`, the doc's tables are generated from it — invariants
172
+ (give each a stable ID — see **Invariants live in the model** below), Given/When/Then scenarios (happy path + rule boundaries + edge cases),
173
+ alternate/error flows (retries, idempotency, compensations), non-functional requirements
174
+ (security/authz, PII/compliance, performance/SLA), read models affected, open questions. Park
175
+ anything unresolved rather than guessing. Before finalizing field names/types or invariants,
176
+ check whether the shape is already defined in **this model** (`.em` and its slice docs) or in
177
+ a neighbour's public surface (read-only) — don't guess a shape that's already defined there.
178
+ Never Grep or Read implementation source, OpenAPI files, DB migrations or `specs/` to settle
179
+ it (evidence rule): if the user says the existing code is the source of truth, ask them for
180
+ the shape, or park the question for an `extract` or `conform` session. Before finalizing an
181
+ invariant, also compare it with the slices in
182
+ **this same model** — never another `.em` model — that make the same kind of change (other
183
+ `Archive*` or `Create*` state changes on different entities, say). If one of them already
184
+ states an analogous rule, either state the same rule here — written out in this doc under
185
+ this slice's own `INV-<MNEMONIC>-n` ID, not by pointing at the sibling's — or record why this
186
+ slice's case differs. If this slice has a rule those siblings lack, raise that with the user
187
+ as an open question against each of them; a ratified sibling changes only through its own
188
+ re-ratification, never as a side effect of this slice. Repeating a rule across slice docs is
189
+ correct: each doc is a self-contained spec, and whether the code behind those rules is shared
190
+ is the constitution's call, not this doc's.
191
+ **Invariants live in the model.** Write every NEW invariant into the `.em` as a standalone
192
+ `invariant INV-<MNEMONIC>-<n> "rule sentence"` line directly after the command (or event) it
193
+ guards — always with the rule sentence — then cite that ID in the doc's Invariants section as a
194
+ plain mention and elaborate there (`- INV-CHK-1 — why, edge cases, the error returned`); never
195
+ restate it as a `**INV-CHK-1:**` rule (`em validate` flags that as
196
+ `invariants/declared-in-both`). A `public` command needs at least one invariant
197
+ (`invariants/public-command-without-invariants`). A doc that already declares its own
198
+ invariants keeps working; migrate one only when you are editing that slice anyway — move the
199
+ ID and rule into the model, keep the doc's elaboration. Run `em validate` after the edit:
200
+ `invariants/malformed-id` and `invariants/duplicate-id` are errors.
201
+ **Generated vs authored sections.** A doc `em slice new` wrote has two kinds of section. The
202
+ GENERATED regions — each command's, event's and read model's field table and the Invariants
203
+ list, between `<!-- GENERATED:em-slice-…:start -->` / `:end -->` markers — restate the model:
204
+ **never hand-edit a generated region.** To change a field, a type, or an invariant, edit the
205
+ `.em`, then **after any model edit run `em slice sync <model>.em`**, which rewrites every
206
+ region in place and leaves everything else byte-for-byte (`em slice sync <model>.em --check`
207
+ reports a stale doc without writing). **Edit authored sections only**: Intent, Trigger &
208
+ Actor, `Triggered by`/`Read by`/`Consumed by`, the invariant elaborations below the generated
209
+ list, Scenarios, Alternate & Error Flows, NFRs, Dependencies, Open Questions. Scenarios use the
210
+ constrained shape `em` reads back: a `### Scenario: <title>` heading, then `- **Given:**`,
211
+ `- **When:**`, `- **Then:**` bullets with nested ` - ` items — all three in every block
212
+ (`em validate` flags a block missing one as `slice-doc/structured-section-malformed`, and
213
+ `--slice-ready` refuses it). A doc written before em 1.14 has no markers; leave it as it is
214
+ unless you are re-creating it (`em slice new … --wire --force`, carrying the prose across).
215
+ A question this version will not answer is deferred with `em slice defer <model> <key> "<question>" --until v<n> --decision "<what this version does>"` — never left `- [ ]`, never deleted, never answered by guessing.
216
+ 2. **First-time authoring:** scaffold the doc mechanically rather than hand-writing the
217
+ frontmatter — `em slice new "<slice name>" --pattern <state-change|state-view|automation|
218
+ translation> --swimlane "<Persona> → <Context>" --wire <model>.em` writes `slices/<slice-name>.md`
219
+ (kebab-cased to match, via the same slugging `em` uses everywhere else) with the canonical
220
+ `schemaVersion`/`pattern`/`swimlane`/`status: draft`/`version: 1` frontmatter — exactly the
221
+ five keys `em export` and the readiness gate require, no more — and the `# Slice:` heading +
222
+ diagram-image stub already in place. `--wire` also inserts the `note
223
+ "slices/<slice-name>.md"` line straight into the `.em`, onto the slice's primary element (the
224
+ command for State Change, the view for State View, the processor for Automation, the
225
+ translation for Translation), matched by export key — see step 4 for when it refuses instead.
226
+ A slice whose sole candidate view is a later `view X again` instance is a **continuation**
227
+ (MIL-208): `--wire` refuses it and names the originating slice holding `X`'s first
228
+ declaration — write this slice's scenarios into that doc instead of scaffolding a new one.
229
+ Both `--pattern` and `--swimlane` are required; an invalid `--pattern` is refused with the
230
+ valid choices listed. Never hand-type this block, and never fall back to a placeholder
231
+ pattern/swimlane to dodge the flags. It also writes every template section: the generated
232
+ regions already filled from the model (field tables, Invariants list), the authored sections
233
+ as placeholders. Then fill in the authored sections from step 1 (Intent, Trigger & Actor,
234
+ invariant elaborations, Scenarios, ...) — those stay hand-authored; the generated regions
235
+ follow the model through `em slice sync`. Record the originating need (ticket/conversation
236
+ link) in the Intent section when one exists. When this doc exists because of a split, merge, or rename, add the matching
237
+ lineage key(s) by hand (`split-from`/`merged-from`/`superseded-by`, `<slice-key>@v<N>` grammar
238
+ — see `${CLAUDE_PLUGIN_ROOT}/shared/reference/slice-doc-schema.md` for the full schema; `em validate` catches a malformed one
239
+ after the fact, see the `lineage-*` rules below).
240
+ **Re-ratification (step 0) — released models only.** Confirm the model is released first
241
+ (`em status <model>.em --json`: the `modelVersion` entry's `design` is not `null`, or
242
+ `slices.byStatus.implemented > 0`); on a pre-release model never run `reratify`. The doc
243
+ already exists, so `em slice new` doesn't apply here
244
+ (it refuses to overwrite an existing file without `--force`, and forcing would blow away the
245
+ doc's authored body) — run `em slice reratify <model>.em <slice-key>` instead: it bumps
246
+ `version` and flips `status` back to `ready-to-implement` in the existing frontmatter,
247
+ clearing any stale `ratifiedBy:`/`ratifiedOn:` from the prior version so a follow-up
248
+ `em slice ratify --by <name>` (if the team records that) applies cleanly. The same command
249
+ changes a ratified doc that **never shipped** (`ready-to-implement` with `ratifiedBy` set — a
250
+ gap answered mid-build, MIL-258): it bumps `version` and clears the sign-off but leaves
251
+ `status` alone. Either way the doc is not ratified until `em slice ratify --by <name>` records
252
+ the new sign-off. A `ready-to-implement` doc with no `ratifiedBy` is already awaiting that
253
+ sign-off and refuses a second `reratify`; a `draft`/`reviewed` doc is simply edited, and its
254
+ `version:` never moves.
255
+ **Public-touching slices (MIL-238).** On a slice that owns a `public` command, event or view,
256
+ `em slice ratify --by <name>` and `em slice reratify` refuse without one of two flags: pass
257
+ `--meaning-unchanged` when this version does not change what the public contract means, or
258
+ `--contract-change "<why>"` when a consumer must read the change differently. Without
259
+ either, both refuse with `slice "<key>" touches the public surface — pass
260
+ --meaning-unchanged, or --contract-change "<why>" if a consumer must read this change
261
+ differently`; passing both refuses too. Run
262
+ `em api generate <model>.em` first, so `em validate --slice-ready` sees a current contract
263
+ (otherwise it blocks with `slice-ready-contract-stale`).
264
+ Which flag applies is the ratifier's call, not yours: ask, never pick one to make the
265
+ command pass. Ratification itself stays a human gate outside this session; a review session
266
+ never ratifies (see `em:review`).
267
+ 3. Render the slice's own diagram: `em render <model>.em --slice "<slice name>" -o
268
+ slices/<slice-name>.svg` (kebab-case, matching the doc's filename and the `![Diagram]` stub
269
+ `em slice new` already wrote) — redraws just this slice in its own canonical pattern shape.
270
+ 4. Confirm it's wired into the `.em`: `em slice new --wire` (step 2) already inserted the `note
271
+ "slices/<slice-name>.md"` line onto the slice's primary element's own declaration line — it
272
+ only ever edits that one line, and only when doing so is unambiguous, so it refuses (writing
273
+ NEITHER the doc nor the `.em` edit) rather than guess when the slice has zero or more than one
274
+ candidate element of the primary kind, or when that line already carries a `note` clause. On a
275
+ refusal, re-run step 2 without `--wire` — it prints the exact line to add, paste it onto the
276
+ slice's primary element by hand.
277
+ 5. Run `em slice index <model-name>.em` to regenerate `README.md`'s Slices table — the one
278
+ canonical slice index — from the model and the doc frontmatter you just wrote (status,
279
+ `implementedIn` once shipped). Never hand-edit the table; it's a generated block.
280
+ 6. Re-render, run `em slice sync <model-name>.em --check` (it must print `ok:` for every doc you
281
+ touched), and `em validate`.
282
+ 7. Before committing, stage the session's files and run the pre-commit check: `em system scope
283
+ --staged`, then `git diff --cached --name-only`. STOP on any `seam-crossing`, any path outside
284
+ this model's directory, any out-of-scope path (see **Write scope** above), or a changed
285
+ `version:` line on a draft doc.
286
+
287
+ A finished draft doesn't become implementable here: it goes through the review gate and then the
288
+ ratification gate, both human, both outside this phase — see
289
+ `docs/process.md#the-slice-lifecycle-gates` for who runs which and in what order.
290
+
291
+ Before the project's first slice is implemented it also needs a ratified implementation
292
+ constitution (stack, code style, testing norms, NFR baselines, review norms) — the
293
+ `em:implement` skill elicits it, see its `reference/implement.md` §7.
294
+
295
+ End of phase: every slice that should exist has a doc — except a continuation slice (MIL-208, a
296
+ later `view X again` instance), which has none by design and is documented by the originating
297
+ slice instead — `README.md`'s Slices table is current, and the model validates clean. Suggest
298
+ `em:implement` for each ratified slice.
@@ -0,0 +1,90 @@
1
+ ---
2
+ name: discover
3
+ em-version: 1.14.1
4
+ description: >-
5
+ Use when starting a brand-new event model from scratch — greenfield discovery: brainstorming
6
+ past-tense events, storyboarding the happy path, finding the commands and read models — or when
7
+ extracting a current-state model from an EXISTING codebase or system (event-driven or
8
+ procedural). Drives `em`'s discover and extract phases; produces a draft or as-is `.em` model
9
+ ready for the model/slice phases (`em:design`).
10
+ ---
11
+ <!-- GENERATED FILE from .claude/skills/event-modeling-discover/SKILL.md by scripts/build-plugin.ts — do not hand-edit; run npm run docs:generate -->
12
+
13
+ # Event Modeling — discover & extract
14
+
15
+ You are facilitating the start of an Event Modeling session. Your job is to **extract an
16
+ accurate model from the user through Socratic questioning** — never to invent the domain. You
17
+ drive the `em` CLI to render the model live.
18
+
19
+ Read `${CLAUDE_PLUGIN_ROOT}/shared/reference/operating-principles.md` (preconditions, project
20
+ layout, the Socratic/validation discipline every phase follows) and
21
+ `${CLAUDE_PLUGIN_ROOT}/shared/reference/methodology.md` (the 7 steps + 4 patterns) before doing real
22
+ work — they are the source of truth. Run the preconditions there first.
23
+
24
+ **Existing system?** Use the `extract` phase below. **New feature inside an existing codebase,
25
+ not a whole-system extraction?** Stay in `discover`, but before locking in command/event names,
26
+ Grep/Read adjacent real sources (OpenAPI specs, DB migrations, existing DTOs/event classes in
27
+ sibling contexts) rather than guessing conventions — same check the `slice` phase applies to
28
+ field tables (`em:design`).
29
+
30
+ ## Phase: `discover` — steps 1-4
31
+
32
+ Goal: a draft model of events, storyboard, commands, and views. Loose is OK; structure comes next.
33
+
34
+ 1. **Brainstorm events (step 1).** Ask the user to name everything that happens, as past-tense
35
+ facts. Probe for missed state changes. For each candidate, apply the **"is it an event?"
36
+ test** (`methodology.md` step 1 — the "would this wake the CEO at 3am" heuristic):
37
+ reject derived values (belong in a read model) and telemetry/activity (not business state).
38
+ Don't silently drop rejections — park them in the state file's Decisions log with why, so
39
+ they aren't re-proposed later. Capture the survivors as a flat list.
40
+ 2. **Plot / storyboard (step 2).** Order the events into the narrative. Identify **personas**
41
+ (actors) and the **UI** screens at each step. Add `persona` declarations and `ui` elements.
42
+ In a **headless/API** model there are no human personas or screens — identify the external
43
+ **callers** at each step instead; declare one persona per caller/role, and its `ui` boxes are
44
+ the API calls (see `methodology.md`).
45
+ 3. **Inputs (step 3).** For each event, find the **command** that causes it (imperative name).
46
+ Form `command → event` slices (State Change pattern). Every command needs a **trigger** in the
47
+ same breath: the screen the user issues it from (a `ui` in the slice), or — for the Automation
48
+ and Translation patterns — the reaction that triggers it, also in this slice. A command
49
+ nothing points at is a write nobody can start. *"Who does this, and where are they when they
50
+ do it?"*
51
+ 4. **Outputs (step 4).** Identify the **read models / views** consumers need and wire them with
52
+ `from "Event"` (State View pattern), each with the screen (or reaction) that consumes it — a
53
+ read model nothing displays is information dropped on the floor. In a **headless/API** model
54
+ the consumer is the caller persona's `ui` (the API query) — same as any screen. Read models are
55
+ **repeated** in each slice where they're read (use `view X again` after the first, each instance
56
+ with its own consumer) so the timeline flows (see `methodology.md`).
57
+ **Close the loop before leaving this step:** every event from step 3 must be read by at least one
58
+ read model. Walk the event list and ask, for each one, *"who looks at this, and what do they do
59
+ with it?"* An event with no answer is either missing its read model or shouldn't be recorded —
60
+ both are worth raising with the user rather than leaving dangling.
61
+
62
+ End of phase: write/refresh the `.em`, render it, update `.event-modeling.md` (steps done,
63
+ decisions, open questions — run `em state set-phase model` rather than hand-editing `Current
64
+ phase:`), and run `em slice index <model-name>.em` to seed `README.md`'s Slices table (every
65
+ slice already reads "no doc yet" — no slice docs exist until the `slice` phase writes them).
66
+ Tell the user they can stop here and resume with `/em:event-modeling` (or invoke
67
+ `em:design` directly).
68
+
69
+ ## Phase: `extract` — current-state model of an existing system
70
+
71
+ Goal: a faithful **current-state** model extracted from an existing system — the as-is sibling
72
+ of `discover` (it replaces discover; the model then proceeds to `model` as usual, in
73
+ `em:design`).
74
+
75
+ **Read `reference/extract.md` before doing any extract work** — it carries a stance override
76
+ that inverts methodology step 1 (capture how the system behaves *today*; never model desired
77
+ state).
78
+
79
+ - Two source modes, detected from the system itself and confirmed with the user:
80
+ **event-driven** (an event vocabulary already exists — schemas, topics, handlers) and
81
+ **procedural/monolith** (no event vocabulary — synthesize candidate events from behavior
82
+ and docs).
83
+ - Converge via a ~7-round confirm-and-clarify loop (one concern per round; render from round 2,
84
+ validate from round 4 — see the playbook).
85
+ - **Current-state-only:** park unknowns as `# TBD` comments in the `.em`, mirrored in the state
86
+ file's Open Questions — never guess intended design.
87
+
88
+ End of phase: validated as-is model; state file updated (source mode, rounds, decisions; `em
89
+ state set-phase model` for `Current phase:`); run `em slice index <model-name>.em` to seed
90
+ `README.md`'s Slices table; then hand off to `em:design` (steps 5-7).
@@ -0,0 +1,147 @@
1
+ <!-- GENERATED FILE from .claude/skills/event-modeling-discover/reference/extract.md by scripts/build-plugin.ts — do not hand-edit; run npm run docs:generate -->
2
+ # Extract Phase — Current-State Model of an Existing System
3
+
4
+ Read this **before doing any extract work**. It governs the `extract` phase and **overrides
5
+ `methodology.md` step 1's stance** for the duration of extraction: you are capturing how the
6
+ system behaves **today**, not modeling what the business needs. Everything else in the
7
+ methodology — the 4 patterns, the "is it an event?" test, the Socratic stance, the
8
+ reaction-shares-a-slice-with-its-command shape — still applies.
9
+
10
+ ## Stance override — current-state-only
11
+
12
+ - Capture the system **as it is**, faithfully, warts included. Never invent future or desired
13
+ state. Improvement ideas that surface go to the state file's Open Questions or Decisions log
14
+ — never into the model.
15
+ - Methodology step 1's caution ("model the process the business **needs**, not how the current
16
+ system happens to work — existing system behavior is a common source of fake events") is
17
+ **deliberately suspended**: here the existing system *is* the subject.
18
+ - The **"is it an event?" filter still applies.** Derived values (totals, calculated fields)
19
+ and telemetry/activity (clicks, page views, logins) are still not events, even when the
20
+ current system stores or emits them as such. Park each rejection in the Decisions log with
21
+ why, so it isn't re-proposed.
22
+ - **Never guess.** Unknown or ambiguous current behavior is parked (see TBD convention below),
23
+ not filled in with what the design "probably" intends.
24
+
25
+ ## Setup
26
+
27
+ 1. Run the shared preconditions (tool check, model location) — see
28
+ `../../../shared/reference/operating-principles.md`. If no model exists yet, run
29
+ `em scaffold <model-name>` (see that doc's "Project layout this skill creates").
30
+ 2. **Agree the scope line first**: which system, which part, what's out of bounds. Write it to
31
+ the state file's Session inputs.
32
+ 3. Record in the state file: `Source mode` (after detection, below) and
33
+ `Existing system refs` — the repo paths, event-schema/topic locations, and docs you will
34
+ extract from.
35
+
36
+ ## Mode detection
37
+
38
+ Inspect the system with your own tools (Grep/Read over the codebase and docs), then confirm
39
+ the mode with the user:
40
+
41
+ - **Event-driven** — the system already has an event vocabulary. Signals: event
42
+ schemas/classes, topics/streams/queues, message or CDC handlers, an event store, pub/sub
43
+ wiring. Extraction *lifts* the model from that vocabulary.
44
+ - **Procedural / monolith** — no event vocabulary. Signals: transaction scripts, service
45
+ methods mutating rows, CRUD controllers, procedural runbooks/docs. Extraction *synthesizes*
46
+ candidate events from the state changes the behavior implies.
47
+
48
+ Mixed systems happen: pick the dominant mode, note the exception areas in the state file.
49
+
50
+ ## Sourcing by mode
51
+
52
+ - **Event-driven:** inventory the emitted/consumed event types; take past-tense business facts
53
+ directly; then filter — technical/infrastructure messages (retries, heartbeats, envelope
54
+ wrappers) and derived/telemetry types are dropped (log why). Keep the system's own names
55
+ where they are honest past-tense facts; propose a cleaner past-tense name only when the
56
+ original is technical (record the mapping in the Decisions log).
57
+ - **Procedural:** for each significant operation, ask "what business fact has become true when
58
+ this completes?" — those are the candidate events. Screens and tables are *evidence* of
59
+ state changes, not events themselves; a table row's lifecycle (created/updated/closed) often
60
+ implies several events. Apply the event filter hard here — procedural systems are where
61
+ derived values and activity records masquerade as facts.
62
+
63
+ Before minting a new candidate event/command/view name, check whether sibling `.em` models
64
+ already exist in the repo. If they do, run `em glossary <this-model>.em <sibling>.em ...` (or
65
+ `--json` it) as a fuzzy-name-matching aid: it surfaces the same normalized term used with a
66
+ different kind or a different field type across models. A hit there usually means the
67
+ extracted system already has an established name/kind elsewhere — prefer that over minting a
68
+ near-duplicate synonym (e.g. "Order Confirmed" vs. an existing "Order Confirmation"). This is
69
+ informational judgment, not a gate: `em glossary` only flags conflicts, it doesn't resolve
70
+ them.
71
+
72
+ ## The confirm-and-clarify loop (~7 rounds)
73
+
74
+ One concern per round. After every round: mirror what you extracted back to the user, update
75
+ the `.em`, and (from R2) re-render so they see the model grow. Convergence beats coverage —
76
+ it is fine to finish a round with `# TBD`s still parked.
77
+
78
+ - **R1 — Candidate events.** Extract (event-driven) or synthesize (procedural) the past-tense
79
+ facts; apply the event test; user confirms/renames/rejects. Ambiguous candidates → `# TBD`.
80
+ - **R2 — Timeline order.** Order the confirmed events into the as-is narrative. Identify the
81
+ *actual* actors/callers and the *real* UI or API surfaces (headless systems: still `ui`/`persona`
82
+ — one persona per caller/role, its `ui` boxes are the API calls, per `em-dsl.md`). **First
83
+ `em render` here.**
84
+ - **R3 — Commands / inputs.** For each event, the request that actually produces it today
85
+ (imperative name), *and what triggers it* — the real screen, or the scheduler/listener that
86
+ fires it (model that as a reaction sharing this same slice, per R5). Unclear trigger → `# TBD`,
87
+ not a plausible guess; expect a *command has nothing that triggers it* warning until R5 fills
88
+ it in, and don't invent a screen to silence it.
89
+ - **R4 — Read models / outputs.** The projections/queries the system actually serves; wire
90
+ `from "Event"` (repeat read models span-1 per `em-dsl.md`, using `view X again` after the
91
+ first). **Start running `em validate`** each round from here. Expect *event is not read by any
92
+ read model* warnings: real systems do record fire-and-forget events. In an as-is model that is
93
+ a **finding about the system**, not a modelling mistake — never invent a reader to silence it
94
+ (that would be desired state). Park it in the Decisions log and carry the warning.
95
+ - **R5 — Boundaries & reactions.** Current automations and integrations as-is: schedulers,
96
+ listeners, outbound syncs, inbound webhooks. Model each as a reaction sharing its slice with
97
+ the `command` + `event` it triggers (`reaction → command → event`, per `em-dsl.md`) — the
98
+ read model it watches, if any, lives in the slice before, named via the reaction's own
99
+ `from`. This matches how the current code usually already reads (scheduler/listener → one
100
+ procedural step); note any real as-built difference in the slice's `# TBD`/Decisions.
101
+ - **R6 — Gap & TBD reconciliation.** Walk every parked `# TBD` with the user. Resolve only
102
+ what they can confirm **as a current-state fact**; everything else stays parked. Explicitly
103
+ refuse to resolve a TBD by inventing desired behavior.
104
+ - **R7 — Convergence.** Final `em render` + `em validate` (fix errors and warnings — this now
105
+ catches a reaction wired straight to an event too, via *reaction triggers no command*). User
106
+ confirms the model is a faithful picture of today. Mark extraction complete in the state file
107
+ and hand off.
108
+
109
+ Seven rounds is the default shape, not a quota — collapse rounds for a small system, repeat
110
+ one for a sprawling one. Record any deviation in the Decisions log.
111
+
112
+ ## TBD parking convention
113
+
114
+ - Inline `.em` comment on its own line directly above (or trailing) the element it concerns:
115
+ `# TBD: <specific question about current behavior>`. Comments are native DSL syntax — they
116
+ render and validate cleanly.
117
+ - Mirror every `# TBD` 1:1 as a checkbox in the state file's **Open questions / parking lot** —
118
+ add `source`/`blocked on`/`revisit` metadata when known — and remove both together when
119
+ resolved.
120
+ - A `# TBD` is only ever resolved by a confirmed current-state fact — never replaced by a
121
+ guessed element.
122
+
123
+ ## Completion & handoff
124
+
125
+ Extraction is complete when: the model renders; `em validate` is clean (or remaining warnings
126
+ are consciously accepted in the Decisions log — unread events in an as-is model are the usual
127
+ accepted case, and each one is worth naming explicitly as a finding); every unknown is a parked
128
+ `# TBD`; and the user has confirmed the model reflects today's behavior. Then:
129
+
130
+ 1. State file: set phase progress (extraction rounds checked), log the completion decision.
131
+ Run `em slice index <model-name>.em` to seed `README.md`'s **Slices table** — every slice,
132
+ its AST-derived pattern, status "no doc yet" (no slice docs exist until the `slice` phase
133
+ writes them).
134
+ 2. The extracted model **replaces `discover`** — suggest `/em:event-modeling model` next
135
+ (steps 5-7: swimlanes, patterns, completeness). Desired-state changes belong *after* the
136
+ as-is model is agreed — as normal `model`/`slice` evolution, clearly separated from
137
+ extraction.
138
+
139
+ ## Anti-patterns
140
+
141
+ - **Inventing desired state** — the cardinal sin of extraction; park it instead.
142
+ - **Importing screens/tables as events** — they are evidence of state changes, not facts.
143
+ - **Guessing an unclear trigger or reaction** instead of parking a `# TBD`.
144
+ - **Cleaning up the domain while extracting** — renames and restructures beyond honest
145
+ past-tense naming are `model`-phase work, done with the user after the as-is picture exists.
146
+ - **Skipping render/validate until the end** — the loop depends on the user seeing the model
147
+ grow round by round.