@milehimikey/em 1.13.1 → 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.
- package/.claude/agents/em-critic.md +26 -0
- package/.claude/agents/em-implementer.md +34 -0
- package/.claude/agents/em-reviewer.md +31 -0
- package/.claude/agents/em-validator.md +27 -0
- package/.claude/skills/event-modeling/SKILL.md +50 -11
- package/.claude/skills/event-modeling-conform/SKILL.md +1 -1
- package/.claude/skills/event-modeling-design/SKILL.md +126 -18
- package/.claude/skills/event-modeling-discover/SKILL.md +1 -1
- package/.claude/skills/event-modeling-engagement/SKILL.md +192 -0
- package/.claude/skills/event-modeling-engagement/reference/engagement.md +143 -0
- package/.claude/skills/event-modeling-implement/SKILL.md +1 -1
- package/.claude/skills/event-modeling-implement/reference/implement.md +38 -11
- package/.claude/skills/event-modeling-review/SKILL.md +1 -1
- package/.claude/skills/event-modeling-shared/reference/em-dsl.md +274 -189
- package/.claude/skills/event-modeling-shared/reference/operating-principles.md +61 -0
- package/.claude/skills/event-modeling-shared/reference/slice-doc-schema.md +108 -5
- package/.claude/skills/event-modeling-shared/templates/constitution.md +18 -1
- package/.claude/skills/event-modeling-shared/templates/slice.md +64 -39
- package/.claude/skills/event-modeling-shared/templates/state.md +5 -1
- package/.claude-plugin/marketplace.json +14 -0
- package/README.md +25 -11
- package/dist/catalog/apiFirst.d.ts +28 -0
- package/dist/catalog/apiFirst.js +49 -0
- package/dist/catalog/apiFirst.js.map +1 -0
- package/dist/catalog/docJoin.d.ts +13 -0
- package/dist/catalog/docJoin.js +11 -0
- package/dist/catalog/docJoin.js.map +1 -1
- package/dist/catalog/docModelConsistencyValidate.js +44 -4
- package/dist/catalog/docModelConsistencyValidate.js.map +1 -1
- package/dist/catalog/invariantsValidate.d.ts +4 -0
- package/dist/catalog/invariantsValidate.js +102 -0
- package/dist/catalog/invariantsValidate.js.map +1 -0
- package/dist/catalog/scenarios.d.ts +15 -0
- package/dist/catalog/scenarios.js +97 -0
- package/dist/catalog/scenarios.js.map +1 -0
- package/dist/catalog/sliceDoc.d.ts +8 -0
- package/dist/catalog/sliceDoc.js +9 -0
- package/dist/catalog/sliceDoc.js.map +1 -1
- package/dist/catalog/sliceReadyValidate.d.ts +31 -4
- package/dist/catalog/sliceReadyValidate.js +66 -8
- package/dist/catalog/sliceReadyValidate.js.map +1 -1
- package/dist/catalog/sliceSections.d.ts +55 -0
- package/dist/catalog/sliceSections.js +317 -0
- package/dist/catalog/sliceSections.js.map +1 -0
- package/dist/cli/agentsMd.js +7 -1
- package/dist/cli/agentsMd.js.map +1 -1
- package/dist/cli/api.d.ts +90 -0
- package/dist/cli/api.js +318 -0
- package/dist/cli/api.js.map +1 -0
- package/dist/cli/ciInit.d.ts +82 -21
- package/dist/cli/ciInit.js +468 -64
- package/dist/cli/ciInit.js.map +1 -1
- package/dist/cli/conformScope.d.ts +28 -0
- package/dist/cli/conformScope.js +67 -0
- package/dist/cli/conformScope.js.map +1 -1
- package/dist/cli/coverage.d.ts +9 -0
- package/dist/cli/coverage.js +60 -8
- package/dist/cli/coverage.js.map +1 -1
- package/dist/cli/engagement.d.ts +210 -0
- package/dist/cli/engagement.js +444 -0
- package/dist/cli/engagement.js.map +1 -0
- package/dist/cli/engagementFile.d.ts +86 -0
- package/dist/cli/engagementFile.js +255 -0
- package/dist/cli/engagementFile.js.map +1 -0
- package/dist/cli/metrics.d.ts +10 -0
- package/dist/cli/metrics.js +49 -1
- package/dist/cli/metrics.js.map +1 -1
- package/dist/cli/pluginPin.d.ts +55 -0
- package/dist/cli/pluginPin.js +188 -0
- package/dist/cli/pluginPin.js.map +1 -0
- package/dist/cli/ratify.d.ts +27 -2
- package/dist/cli/ratify.js +112 -13
- package/dist/cli/ratify.js.map +1 -1
- package/dist/cli/reratify.d.ts +3 -2
- package/dist/cli/reratify.js +36 -14
- package/dist/cli/reratify.js.map +1 -1
- package/dist/cli/scopeInputs.d.ts +48 -0
- package/dist/cli/scopeInputs.js +176 -0
- package/dist/cli/scopeInputs.js.map +1 -0
- package/dist/cli/skillCheck.d.ts +15 -1
- package/dist/cli/skillCheck.js +22 -1
- package/dist/cli/skillCheck.js.map +1 -1
- package/dist/cli/skillDirs.d.ts +5 -0
- package/dist/cli/skillDirs.js +13 -1
- package/dist/cli/skillDirs.js.map +1 -1
- package/dist/cli/skillSync.d.ts +4 -0
- package/dist/cli/skillSync.js +23 -0
- package/dist/cli/skillSync.js.map +1 -1
- package/dist/cli/sliceNew.d.ts +11 -7
- package/dist/cli/sliceNew.js +91 -18
- package/dist/cli/sliceNew.js.map +1 -1
- package/dist/cli/sliceSync.d.ts +50 -0
- package/dist/cli/sliceSync.js +170 -0
- package/dist/cli/sliceSync.js.map +1 -0
- package/dist/cli/status.d.ts +24 -1
- package/dist/cli/status.js +20 -1
- package/dist/cli/status.js.map +1 -1
- package/dist/cli/systemInputs.d.ts +53 -10
- package/dist/cli/systemInputs.js +186 -11
- package/dist/cli/systemInputs.js.map +1 -1
- package/dist/cli/upgrade.d.ts +16 -3
- package/dist/cli/upgrade.js +366 -30
- package/dist/cli/upgrade.js.map +1 -1
- package/dist/cli/usageLog.d.ts +4 -2
- package/dist/cli/usageLog.js +4 -2
- package/dist/cli/usageLog.js.map +1 -1
- package/dist/cli.js +554 -94
- package/dist/cli.js.map +1 -1
- package/dist/emit/api.d.ts +29 -0
- package/dist/emit/api.js +220 -0
- package/dist/emit/api.js.map +1 -0
- package/dist/emit/apiCheckJson.d.ts +3 -0
- package/dist/emit/apiCheckJson.js +21 -0
- package/dist/emit/apiCheckJson.js.map +1 -0
- package/dist/emit/codeownersJson.d.ts +3 -0
- package/dist/emit/codeownersJson.js +19 -0
- package/dist/emit/codeownersJson.js.map +1 -0
- package/dist/emit/diffJson.d.ts +1 -1
- package/dist/emit/diffJson.js +8 -1
- package/dist/emit/diffJson.js.map +1 -1
- package/dist/emit/engagementJson.d.ts +8 -0
- package/dist/emit/engagementJson.js +46 -0
- package/dist/emit/engagementJson.js.map +1 -0
- package/dist/emit/glossaryJson.d.ts +1 -1
- package/dist/emit/glossaryJson.js +4 -1
- package/dist/emit/glossaryJson.js.map +1 -1
- package/dist/emit/json.d.ts +17 -1
- package/dist/emit/json.js +30 -1
- package/dist/emit/json.js.map +1 -1
- package/dist/emit/metricsJson.d.ts +1 -1
- package/dist/emit/metricsJson.js +4 -1
- package/dist/emit/metricsJson.js.map +1 -1
- package/dist/emit/queryJson.d.ts +1 -1
- package/dist/emit/queryJson.js +7 -1
- package/dist/emit/queryJson.js.map +1 -1
- package/dist/emit/scopeJson.d.ts +12 -0
- package/dist/emit/scopeJson.js +26 -0
- package/dist/emit/scopeJson.js.map +1 -0
- package/dist/emit/skillCheckJson.d.ts +3 -2
- package/dist/emit/skillCheckJson.js +10 -2
- package/dist/emit/skillCheckJson.js.map +1 -1
- package/dist/emit/statusJson.d.ts +1 -1
- package/dist/emit/statusJson.js +15 -1
- package/dist/emit/statusJson.js.map +1 -1
- package/dist/emit/systemJson.d.ts +15 -8
- package/dist/emit/systemJson.js +28 -13
- package/dist/emit/systemJson.js.map +1 -1
- package/dist/emit/validateJson.d.ts +3 -3
- package/dist/emit/validateJson.js +10 -4
- package/dist/emit/validateJson.js.map +1 -1
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.js +163 -31
- package/dist/mcp/server.js.map +1 -1
- package/dist/model/diff.d.ts +4 -1
- package/dist/model/diff.js +44 -8
- package/dist/model/diff.js.map +1 -1
- package/dist/model/glossary.d.ts +2 -0
- package/dist/model/glossary.js +1 -0
- package/dist/model/glossary.js.map +1 -1
- package/dist/model/model.d.ts +55 -2
- package/dist/model/model.js +57 -0
- package/dist/model/model.js.map +1 -1
- package/dist/model/qualifiedRef.d.ts +18 -0
- package/dist/model/qualifiedRef.js +26 -0
- package/dist/model/qualifiedRef.js.map +1 -1
- package/dist/model/queryIndex.d.ts +9 -1
- package/dist/model/queryIndex.js +18 -2
- package/dist/model/queryIndex.js.map +1 -1
- package/dist/model/rules.d.ts +143 -2
- package/dist/model/rules.js +162 -3
- package/dist/model/rules.js.map +1 -1
- package/dist/model/validate.d.ts +40 -0
- package/dist/model/validate.js +147 -1
- package/dist/model/validate.js.map +1 -1
- package/dist/parser/ast.d.ts +31 -3
- package/dist/parser/parser.js +148 -14
- package/dist/parser/parser.js.map +1 -1
- package/dist/query/format.js +1 -1
- package/dist/query/format.js.map +1 -1
- package/dist/query/verbs.d.ts +6 -0
- package/dist/query/verbs.js +6 -2
- package/dist/query/verbs.js.map +1 -1
- package/dist/system/adaptation.d.ts +14 -0
- package/dist/system/adaptation.js +49 -0
- package/dist/system/adaptation.js.map +1 -0
- package/dist/system/codeowners.d.ts +77 -0
- package/dist/system/codeowners.js +242 -0
- package/dist/system/codeowners.js.map +1 -0
- package/dist/system/manifest.d.ts +10 -5
- package/dist/system/manifest.js +54 -26
- package/dist/system/manifest.js.map +1 -1
- package/dist/system/migrateManifest.d.ts +28 -0
- package/dist/system/migrateManifest.js +155 -0
- package/dist/system/migrateManifest.js.map +1 -0
- package/dist/system/scope.d.ts +46 -0
- package/dist/system/scope.js +132 -0
- package/dist/system/scope.js.map +1 -0
- package/dist/system/verify.d.ts +50 -13
- package/dist/system/verify.js +179 -58
- package/dist/system/verify.js.map +1 -1
- package/dist/templates.d.ts +2 -2
- package/dist/templates.js +23 -2
- package/dist/templates.js.map +1 -1
- package/package.json +5 -2
- package/plugin/.claude-plugin/plugin.json +18 -0
- package/plugin/agents/em-critic.md +27 -0
- package/plugin/agents/em-implementer.md +35 -0
- package/plugin/agents/em-reviewer.md +32 -0
- package/plugin/agents/em-validator.md +28 -0
- package/plugin/shared/reference/em-dsl.md +866 -0
- package/plugin/shared/reference/methodology.md +329 -0
- package/plugin/shared/reference/operating-principles.md +222 -0
- package/plugin/shared/reference/slice-doc-schema.md +566 -0
- package/plugin/shared/templates/conformance-report.md +85 -0
- package/plugin/shared/templates/constitution.md +136 -0
- package/plugin/shared/templates/model-readme.md +46 -0
- package/plugin/shared/templates/slice.md +251 -0
- package/plugin/shared/templates/state.md +77 -0
- package/plugin/skills/conform/SKILL.md +93 -0
- package/plugin/skills/conform/reference/conform.md +428 -0
- package/plugin/skills/design/SKILL.md +297 -0
- package/plugin/skills/discover/SKILL.md +90 -0
- package/plugin/skills/discover/reference/extract.md +147 -0
- package/plugin/skills/engagement/SKILL.md +193 -0
- package/plugin/skills/engagement/reference/engagement.md +144 -0
- package/plugin/skills/event-modeling/SKILL.md +132 -0
- package/plugin/skills/implement/SKILL.md +61 -0
- package/plugin/skills/implement/reference/implement.md +439 -0
- package/plugin/skills/review/SKILL.md +74 -0
- package/dist/emit/typespec.d.ts +0 -26
- package/dist/emit/typespec.js +0 -356
- 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.
|