@milehimikey/em 1.7.0 → 1.7.2

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.
@@ -1,19 +1,13 @@
1
1
  ---
2
2
  name: event-modeling
3
- em-version: 1.7.0
3
+ em-version: 1.7.2
4
4
  description: >-
5
- Guide a user and their team through building rigorous Event Models with the `em` CLI, using
6
- the 7 steps of Event Modeling and the 4 patterns (State Change, State View, Automation,
7
- Translation), and produce implementation-ready slice docs. Use when the user wants
8
- to event-model a business process or system, do event modeling / event storming, design slices,
9
- build or edit an `.em` model, run the em tool, extract / reverse-engineer a current-state
10
- event model from an existing system or codebase (event-driven or legacy/procedural), or check
11
- a ratified model for drift against the codebase that implements it. Works in resumable phases
12
- via an argument: `discover` (steps 1-4, greenfield), `extract` (current-state model of an
13
- existing system), `model` (steps 5-7), `slice` (deep slice specs), `implement` (build one
14
- ratified slice into code, following the bundled agent guide), `conform` (drift check
15
- against the codebase), plus `watch`, `review` (stakeholder walkthrough), and `validate`.
16
- With no argument it resumes from the saved state file.
5
+ Use when the user wants to event-model a business process or system: building, editing,
6
+ updating, or maintaining a model — extending or detailing an existing one into slices,
7
+ extracting a current-state model from an existing codebase, implementing a ratified slice, or
8
+ checking a model for drift against the code. Drives the `em` CLI through resumable phases —
9
+ discover, extract, model, slice, implement, conform, watch, review, validate — or resumes
10
+ automatically with no argument.
17
11
  ---
18
12
 
19
13
  # Event Modeling with `em`
@@ -59,23 +53,24 @@ vendored alongside this skill.
59
53
  keep a sub-flow that detours into another context together, not parked at the end of the model).
60
54
  Declare every instance after the first with
61
55
  **`view X again from "..."`** — `again` instances are exempt from the duplicate-name warning even
62
- when referenced, and each reference resolves to the right instance. Slice order matters: a
63
- reaction must be directly followed by its command slice, and a read slice must not be immediately
64
- followed by a command slice (see `em-dsl.md`).
56
+ when referenced, and each reference resolves to the right instance. A reaction's command is
57
+ wired by same-slice presence, exactly like `ui → command` — no positional/adjacency rule to get
58
+ right (see `em-dsl.md`).
65
59
  See `reference/methodology.md` (State View) and `reference/em-dsl.md`.
66
60
  - **Never connect two instances of one read model.** The repeat is a timeline device, not a flow:
67
61
  continuity is implied by the shared name, and the events arriving at each instance are what show
68
62
  it changing. An arrow between instances says the read model feeds itself, and is an error.
69
63
  - **Every slice is a COMPLETE pattern, never a half-slice.** A State Change is
70
64
  `ui → command → event`; a State View is `event → read model → ui` (or `→ reaction`). So: a
71
- command needs something that **triggers** it (the `ui` it's issued from, or the reaction in the
72
- slice before it), its event needs a **reader**, and that read model needs a **consumer** of its
73
- own. A command nothing points at is a write nobody can start; an event nothing projects is a
74
- write nobody can see; a read model nothing displays is information dropped on the floor. Write
75
- the whole chain together rather than sweeping up dangling ends later. Reactions don't count as
76
- readers of *events* — they read views. **Every instance** of a repeated read model needs its own
77
- consumer: if you repeat a view next to an event just to keep the arrow short, bring its screen
78
- along, or don't add the instance.
65
+ command needs something that **triggers** it (the `ui` it's issued from, or the reaction that
66
+ triggers it, also in this slice), its event needs a **reader**, and that read model needs a
67
+ **consumer** of its own. A command nothing points at is a write nobody can start; a reaction
68
+ that triggers no command is a decision the system never acts on; an event nothing projects is
69
+ a write nobody can see; a read model nothing displays is information dropped on the floor.
70
+ Write the whole chain together rather than sweeping up dangling ends later. Reactions don't
71
+ count as readers of *events* — they read views. **Every instance** of a repeated read model
72
+ needs its own consumer: if you repeat a view next to an event just to keep the arrow short,
73
+ bring its screen along, or don't add the instance.
79
74
  - **Only six connections are legal:** `ui → command`, `command → event`, `event → read model`,
80
75
  `read model → ui`, `read model → reaction`, `reaction → command`. Reaching for anything else —
81
76
  above all `command → read model` (the CQRS violation) or `read model → command` — means the model
@@ -150,8 +145,9 @@ phase applies to field tables, below.
150
145
  3. **Inputs (step 3).** For each event, find the **command** that causes it (imperative name).
151
146
  Form `command → event` slices (State Change pattern). Every command needs a **trigger** in the
152
147
  same breath: the screen the user issues it from (a `ui` in the slice), or — for the Automation
153
- and Translation patterns — the reaction in the slice before it. A command nothing points at is
154
- a write nobody can start. *"Who does this, and where are they when they do it?"*
148
+ and Translation patterns — the reaction that triggers it, also in this slice. A command
149
+ nothing points at is a write nobody can start. *"Who does this, and where are they when they
150
+ do it?"*
155
151
  4. **Outputs (step 4).** Identify the **read models / views** consumers need and wire them with
156
152
  `from "Event"` (State View pattern), each with the screen (or reaction) that consumes it — a
157
153
  read model nothing displays is information dropped on the floor. In a **headless/API** model
@@ -194,23 +190,23 @@ Goal: a structurally complete, **validated** model with correct patterns and swi
194
190
 
195
191
  1. **Swimlanes & patterns (step 5).** Group events into **contexts** (bounded contexts /
196
192
  aggregates) — ask which events share a consistency boundary and who owns them. Classify each
197
- slice as one of the 4 patterns. **Split every automation _and every translation_ across two
198
- slices** (the reaction — processor/translation — plus its read model in one slice; the
199
- triggered command + its event in the *next* slice). A translation or automation is a reaction:
200
- it **triggers a command and never records an event directly**. Add `translation` slices for
201
- external inputs (externally triggered: external → translation → command → event) and for the
202
- system reacting to its own state (internally triggered: read model → translation → command →
203
- event).
193
+ slice as one of the 4 patterns. **Share the slice between every automation or translation and
194
+ the command it triggers** (the reaction — processor/translation — together with the command
195
+ and event it produces; the read model it watches, if any, stays in the slice before). A
196
+ translation or automation is a reaction: it **triggers a command and never records an event
197
+ directly**. Add `translation` slices for external inputs (externally triggered: external →
198
+ translation → command → event, no slice before) and for the system reacting to its own state
199
+ (internally triggered: read model in the slice before → translation → command → event).
204
200
  2. **Elaborate scenarios — first pass (step 6).** For each slice, capture the happy-path
205
201
  Given/When/Then and the obvious invariants as short notes. (The full spec is the `slice` phase.)
206
202
  3. **Evaluate completeness (step 7).** Walk the model: every slice is a **complete** pattern, not a
207
203
  half-slice — **every command has something that triggers it**, every command emits an event,
208
204
  **every event is read by a read model**, **every read model has a consumer**, every view has a
209
205
  source, every UI is reachable, every connection is
210
- one of the six legal pairs, and automations **and translations** split correctly (each reaction
211
- triggers a command — none wired straight to an event). Run `em validate` and resolve all errors
212
- and warnings — but check the reaction→command→event split by hand, since `em validate` does
213
- **not** flag a translation/automation that emits an event directly (see `reference/em-dsl.md`).
206
+ one of the six legal pairs, and every automation **and** translation triggers a command (none
207
+ wired straight to an event, none with no command at all). Run `em validate` and resolve all
208
+ errors and warnings — it catches a reaction wired straight to an event too now (see
209
+ `reference/em-dsl.md`), so a clean run covers this case.
214
210
  For an unread event, don't just bolt on a view to silence the warning: ask the user who looks at
215
211
  this fact and what they do with it. The honest answers are "here's the read model we missed" or
216
212
  "nobody — so why are we recording it", and both improve the model.
@@ -368,8 +364,8 @@ rule (see `reference/em-dsl.md`) and proposing the fix. Apply fixes on agreement
368
364
 
369
365
  | Diagnostic | Fix |
370
366
  |---|---|
371
- | automation/translation shares a slice with a command | Split the command into the next slice |
372
- | **command has nothing that triggers it** | Add the `ui` it's issued from — or, if the system issues it, the reaction in the slice before it. Ask *"who does this, and where are they when they do it?"* |
367
+ | reaction triggers no command | Add the command it issues, in this same slice, or an explicit `arrow` to one elsewhere |
368
+ | **command has nothing that triggers it** | Add the `ui` it's issued from — or, if the system issues it, the reaction that triggers it, also in this slice. Ask *"who does this, and where are they when they do it?"* |
373
369
  | **read model has no consumer** | Add the screen that displays it, or the reaction that watches it. If it's a repeat added only to shorten an arrow and nothing looks at it there, drop the instance |
374
370
  | read model has no source | Add the missing `from "Event"` |
375
371
  | command produces no event | Give the command its event, or drop the command |
@@ -383,10 +379,11 @@ relevant element over a `# TBD` comment — it shows up as a red marker on the r
383
379
  `em validate` tracks it as an open-issue warning, so it isn't lost once rendered. `em validate
384
380
  --list-issues` gives a quick sweep of everything still open.
385
381
 
386
- Then do one check `em validate` can't: scan every `translation`/`processor`/`automation` slice and
387
- confirm none contains an `event` — each reaction must trigger a `command` in the next slice
388
- (`reaction → command → event`). If you find a reaction wired straight to an event, split it into
389
- two slices and route it through a command.
382
+ `em validate` now catches a reaction wired straight to an event too — any reaction with no
383
+ `command` in its own slice, and no explicit arrow to one, warns "triggers no command". A
384
+ `translation`/`processor`/`automation` should always trigger a `command` in its own slice
385
+ (`reaction → command → event`); if one instead records an event directly, route it through a
386
+ command instead.
390
387
 
391
388
  ---
392
389
 
@@ -153,6 +153,11 @@ type Name { field: Type, ... } # named structured type, reusable from any f
153
153
  declared `type` (see Named types below), in which case it resolves to a structured
154
154
  reference. Keep these light; full field specs with rules live in the slice doc.
155
155
  - **Comments:** `# ...` anywhere outside quotes (full-line or trailing).
156
+ - **Quoted strings:** everything between a `"` and its matching `"` is literal — `{`, `}`, `#`
157
+ included, never re-interpreted as a field block or comment — so REST path-template
158
+ placeholders are safe as-is: `issue "PUT v3/widgets/{widgetId}/suspend"`. Escape a literal
159
+ `"` or `\` inside a string with `\"`/`\\`; no other backslash sequence is special. An
160
+ unterminated string (missing closing `"`) is a parse error naming the clause it broke in.
156
161
  - **`source "url"` on a slice header** (the only slice-level clause — everything above is
157
162
  per-element) links the slice back to the ticket/conversation it traces to, e.g.
158
163
  `slice "Checkout" source "https://linear.app/team/issue/MIL-60" { ... }`. Exports as
@@ -203,12 +208,12 @@ slice "Open Orders" {
203
208
  ui Order List @Customer
204
209
  }
205
210
 
206
- # 3. Automation: split across TWO slices (plus the read slice that consumes the event)
211
+ # 3. Automation: read model in the slice before, reaction + command + event together
207
212
  slice "Orders To Fulfill" {
208
213
  view Orders To Fulfill from "Order Placed"
209
- processor Fulfillment Service
210
214
  }
211
- slice "Ship Order" { # the triggered command goes in the NEXT slice
215
+ slice "Ship Order" { # reaction, command, and event all share this one slice
216
+ processor Fulfillment Service from "Orders To Fulfill"
212
217
  command Ship Order
213
218
  event Order Shipped @Shipping
214
219
  }
@@ -218,10 +223,8 @@ slice "Open Orders — shipped" {
218
223
  }
219
224
 
220
225
  # 4a. Translation (external trigger, no durable artifact): external call -> translation -> command -> event
221
- slice "Carrier Webhook" {
226
+ slice "Confirm Delivery" {
222
227
  translation Carrier Adapter # inbound from outside the model; no internal `from`
223
- }
224
- slice "Confirm Delivery" { # the triggered command goes in the NEXT slice
225
228
  command Confirm Delivery
226
229
  event Delivery Confirmed @Shipping
227
230
  }
@@ -238,9 +241,9 @@ slice "Receive Carrier Event" { # ingest: persist the raw webhook befo
238
241
  }
239
242
  slice "Inbound Carrier Events" {
240
243
  view Inbound Carrier Events from "Carrier Event Received" # the persisted inbound queue
241
- translation Carrier Adapter
242
244
  }
243
245
  slice "Acknowledge Carrier Event" {
246
+ translation Carrier Queue Adapter from "Inbound Carrier Events" # different producer than 4a's Carrier Adapter, so a distinct name
244
247
  command Acknowledge Carrier Event
245
248
  event Carrier Event Acknowledged @Shipping
246
249
  }
@@ -251,15 +254,15 @@ slice "Inbound Carrier Events — processed" {
251
254
 
252
255
  # 4c. Translation (internal trigger, durable artifact): read model -> translation -> command -> event
253
256
  slice "Accept Quote" {
254
- ui Quote Screen @Customer # every command needs a trigger: a ui, or a reaction before it
257
+ ui Quote Screen @Customer # every command needs a trigger: a ui, or a reaction, in this slice
255
258
  command Accept Quote
256
259
  event Quote Accepted @Quote
257
260
  }
258
261
  slice "Quotes To Sync" {
259
262
  view Accepted Quotes from "Quote Accepted"
260
- translation CRM Sync # reacts to our own state via the read model in this slice
261
263
  }
262
264
  slice "Record Sync" {
265
+ translation CRM Sync from "Accepted Quotes" # reacts to our own state via the read model
263
266
  command Record Crm Sync
264
267
  event Quote Synced @Quote
265
268
  }
@@ -278,8 +281,11 @@ the context/lane whose fact it represents (e.g. `Carrier Event Received @Shippin
278
281
  Anti-Corruption-Layer event in DDD) instead of a technical artifact leaking foreign vocabulary
279
282
  into the model.
280
283
 
281
- A `translation` (like a `processor`) is a **reaction**: it triggers a command and never carries an
282
- `event` in its own slice. Same two-slice split as the Automation pattern above.
284
+ A `translation` (like a `processor`) is a **reaction**: it triggers a command in the same slice —
285
+ same shape as the Automation pattern above, and the same shape `ui` already uses in State Change.
286
+ The reaction never records the event directly (there's no `reaction -> event` edge); the command
287
+ it triggers is what records it, and that event happens to live in the reaction's own slice now
288
+ because the command does.
283
289
 
284
290
  Note the read slice closing each pattern: **every event must be read by some read model**
285
291
  (warning 4 below). A command slice is not finished until the slice that projects its event
@@ -314,8 +320,9 @@ slice "Read Quote — created" {
314
320
  request/response API call — that's Pattern 1 (State Change) with an API persona, exactly like the
315
321
  `Create Quote` slice above.
316
322
  - **Internal-only commands and views (no public route) carry no `ui` at all.** They follow the
317
- ordinary Automation pattern already documented: the reaction sits in the previous slice, and an
318
- internal-only read model is consumed by that reaction, not by a screen or an API query.
323
+ ordinary Automation pattern already documented: the reaction shares the command's slice, and an
324
+ internal-only read model is consumed by that reaction (from the slice before), not by a screen
325
+ or an API query.
319
326
  - **Repeat the read model** in every slice where it's read so the timeline flows left-to-right, and
320
327
  **prefer `view X again`** for every instance after the first (see Clauses) — **each instance
321
328
  carries its own consumer** (the `ui` that reads it, or a reaction), not just the last one. `again`
@@ -337,23 +344,19 @@ slice "Read Quote — created" {
337
344
  Keep the read model adjacent to its event, and keep a sub-flow that detours into another context
338
345
  together rather than parking it at the end of the model.
339
346
 
340
- ### Slice-ordering gotchas (edge inference)
341
-
342
- `em` infers cross-slice arrows positionally, so **slice order matters**:
343
- - A **reaction** (`processor`/`translation` that triggers a command) wires to the command in the
344
- **immediately next** slice. So a reaction slice must be *directly* followed by its command slice —
345
- don't insert a read slice between them.
346
- - A read slice whose consumer is a **reaction** (`view` + `processor`/`translation`, no command in
347
- the same slice) must **not** be immediately followed by a `command` slice, or the reaction will be
348
- mis-wired to that command. Put it after a command+event slice, or before another read/reaction
349
- slice instead. (A `ui`-consumed read slice has no such risk — `ui` never wires as a reaction.)
350
- - A read model fed by an early event (e.g. a queue or to-do view) can't always sit directly after
351
- its source event when a reaction must immediately precede its command slice — placing the read
352
- later, in narrative order with a longer arrow, is the correct trade-off, not something to force-fix.
353
- - A `ui` never triggers a reaction — no pattern has a `ui` wired to `processor`/`automation`/
354
- `saga`/`translation`, only to `command`. A `ui` left in the reaction's own slice (instead of the
355
- read-model or command slice) renders with no outgoing edge, disconnected, and `em validate` now
356
- warns on it.
347
+ ### Reaction wiring — no positional gotcha
348
+
349
+ A reaction's command is inferred from **same-slice presence**, exactly like `ui -> command`: put
350
+ `processor`/`translation` in the same slice as the command it triggers, in either order, and `em`
351
+ wires it correctly regardless of what any other slice contains or where it sits in the file. There's
352
+ no adjacency to get right and no reordering hazard — the read model it watches (if any) is named
353
+ explicitly via `from "View"`, resolved by name to the nearest instance at or before its own slice,
354
+ not by position either.
355
+
356
+ The one thing that still doesn't wire on its own: a `ui` never triggers a reaction — no pattern has
357
+ a `ui` wired to `processor`/`automation`/`saga`/`translation`, only to `command`. A `ui` left in a
358
+ reaction's slice renders with no outgoing edge, disconnected, and `em validate` warns on it — move
359
+ it to the slice that displays the read model, or drop it.
357
360
 
358
361
  ---
359
362
 
@@ -388,15 +391,16 @@ slice "Read Quote — created" {
388
391
  real split/merge (see the four `lineage-*` codes in the table below).
389
392
 
390
393
  **Warnings (should fix):**
391
- 1. **Automation/translation shares slice with its command** — both `automation`/`processor` and
392
- `translation` are reactions; put the triggered command in the *next* slice.
393
- 2. **`ui` shares slice with a reaction, no command** — a `ui` only ever wires to a `command`; no
394
+ 1. **Reaction with no command** — a `processor`/`automation`/`saga`/`translation` that triggers
395
+ nothing. It never records an event itself; add the command it issues in this same slice, or an
396
+ explicit `arrow` to one elsewhere.
397
+ 2. **`ui` shares slice with a reaction** — a `ui` only ever wires to a `command` a person issues; no
394
398
  pattern has a `ui` triggering an automation/processor/saga/translation. Left in the reaction's
395
- own slice it renders disconnected, with no edge. Move it to the read-model slice, or to the
396
- slice with the command this eventually triggers.
397
- 3. **Command with no trigger** — nothing issues it. A command needs a `ui` in its slice, or a
398
- reaction (automation/processor/saga/translation) in the slice *before* it. The input-side mirror
399
- of (5): a command nothing points at is a write nobody can start.
399
+ own slice it renders disconnected, with no edge, whether or not that slice also has the
400
+ reaction's own command. Move it to the slice that displays the read model, or drop it.
401
+ 3. **Command with no trigger** — nothing issues it. A command needs a `ui` in its slice, or the
402
+ reaction (automation/processor/saga/translation) that triggers it, *also* in this slice. The
403
+ input-side mirror of (5): a command nothing points at is a write nobody can start.
400
404
  4. **Command without event** — every command should record at least one event.
401
405
  5. **Event nobody reads** — the mirror of (4): recording an event no read model projects is a
402
406
  write with no reader. Follow every write slice with the read slice that consumes its event.
@@ -450,12 +454,12 @@ not the prose above has caught up yet. `--slice-ready <key>`-only codes are excl
450
454
  | `arrow-backward` | error | Backward arrow | Restructure so the target comes later. |
451
455
  | `arrow-unresolved-source` | error | Arrow source unresolved | Fix the arrow's source name. |
452
456
  | `arrow-unresolved-target` | error | Arrow target unresolved | Fix the arrow's target name. |
453
- | `automation-shares-slice-with-command` | warning | Automation shares slice with its command | Put the triggered command in the next slice. |
454
457
  | `binding-missing-file` | warning | Doc binding points at a missing file | Create the slice doc, or fix the `note` path. |
455
458
  | `both-ends-of-a-flow/command-no-event` | warning | Command without event | Add the event this command records. |
456
- | `both-ends-of-a-flow/command-untriggered` | warning | Command with no trigger | Add a `ui` in this slice, or a reaction in the previous slice. |
459
+ | `both-ends-of-a-flow/command-untriggered` | warning | Command with no trigger | Add a `ui` or the reaction that issues it, both in this slice. |
457
460
  | `both-ends-of-a-flow/event-unproduced` | warning | Event with no producing command | Add the command that records it, or an explicit arrow from one. |
458
461
  | `both-ends-of-a-flow/event-unread` | warning | Event nobody reads | Project it into a view, or reconsider recording it. |
462
+ | `both-ends-of-a-flow/reaction-no-command` | warning | Reaction with no command | Add the command it triggers, in this slice, or an explicit arrow to one. |
459
463
  | `both-ends-of-a-flow/ui-unbacked` | warning | `ui` with no read model or command | Add a `view` it displays, or the command it triggers. |
460
464
  | `both-ends-of-a-flow/view-unconsumed` | warning | Read model with no consumer | Add a `ui` or reaction that consumes it, or drop this instance. |
461
465
  | `connection-legality/illegal-pair` | error | Illegal connection | Only ui→command→event→view→ui and view→reaction→command are legal — the message names the missing step. |
@@ -478,7 +482,7 @@ not the prose above has caught up yet. `--slice-ready <key>`-only codes are excl
478
482
  | `reaction-from-unresolved` | error | Unknown read-model source | Project the event into a view first, or fix the `from` reference. |
479
483
  | `translation-name-collision` | warning | Translation name reused for different producers | Use a distinct name per producer to avoid confusion. |
480
484
  | `type-cycle` | error | Cyclic type reference | Break the cycle, or route the self/mutual reference through an array. |
481
- | `ui-shares-slice-with-automation` | warning | `ui` shares slice with a reaction, no command | Move the `ui` to the read-model slice, or to the slice with the command this triggers. |
485
+ | `ui-shares-slice-with-automation` | warning | `ui` shares slice with a reaction | Move the `ui` to the slice that displays the read model, or drop it. |
482
486
  | `view-again-without-earlier` | error | `again` without an earlier declaration | Declare the view plainly the first time it appears. |
483
487
  | `view-from-future-event` | error | Backward timeline (view reads a future event) | Move the source to a later `view X again` instance. |
484
488
  | `view-from-unresolved` | error | Unknown event source | Fix the `from` reference to name an existing event. |
@@ -488,8 +492,8 @@ not the prose above has caught up yet. `--slice-ready <key>`-only codes are excl
488
492
  **Design rules that keep models valid:**
489
493
  - One element per band per slice (multiple personas/contexts are fine — they're different rows).
490
494
  - Every `command` slice includes its `event`. Every `view` has a `from` source.
491
- - **Every `command` has a trigger.** A `ui` in its slice, or a reaction in the slice *before* it.
492
- A command nothing points at is a write nobody can start.
495
+ - **Every `command` has a trigger.** A `ui` in its slice, or the reaction that triggers it, also in
496
+ its slice. A command nothing points at is a write nobody can start.
493
497
  - **Every `event` has a reader.** A command slice isn't finished until the read slice that projects
494
498
  its event exists. Reactions don't count — they read views, not events. Pair each write slice with
495
499
  its read slice as you go rather than sweeping up dangling events at the end.
@@ -502,16 +506,17 @@ not the prose above has caught up yet. `--slice-ready <key>`-only codes are excl
502
506
  `command → view` (the CQRS violation: an event has to sit between them) and `view → command`
503
507
  (a reaction has to sit between them). If you reach for an arrow the patterns don't allow, the
504
508
  model is missing an element, not an arrow.
505
- - Automations **and translations** are always two slices: the reaction (plus its read model, if
506
- internally triggered) in one slice, the triggered `command` + its `event` in the next. A
507
- translation/automation slice **never contains an `event`** — reactions trigger commands, not
508
- events. Externally-triggered reactions start from outside the model (no `from`);
509
- internally-triggered ones read a **view** (read model) — `from "X"` must resolve to a view.
509
+ - A reaction (`processor`/`automation`/`saga`/`translation`) shares its slice with the `command` it
510
+ triggers and that command's `event` — the same shape `ui` already uses in State Change. If it's
511
+ internally triggered, the **view** (read model) it watches lives in the slice before, named via
512
+ `from "X"` (which must resolve to a view, never an event directly). Externally-triggered reactions
513
+ start from outside the model — no `from` at all, and no slice before them.
510
514
  - Name everything uniquely and consistently.
511
515
  - Events are past tense; commands are imperative; views name the thing shown.
512
516
 
513
- > ⚠️ **Validator blind spot:** `em validate` does **not** flag a translation/automation that emits
514
- > an event directly (e.g. `translation T` and `event E` in the same slice with no command). It only
515
- > warns when a reaction shares a slice with a *command*. So enforce the two-slice
516
- > `reaction → command → event` split **by construction** — never rely on `em validate` to catch a
517
- > reaction wired straight to an event.
517
+ A reaction that emits an event with no command anywhere between them — `translation T` and
518
+ `event E` in the same slice, nothing issuing it — is exactly what
519
+ `both-ends-of-a-flow/reaction-no-command` (above) catches: any reaction with no `command` in its
520
+ own slice, and no explicit arrow to one, warns. Still route every reaction through a real command
521
+ by construction (`reaction → command → event`) rather than an explicit `arrow "Reaction" -> "Event"`
522
+ — that shape is its own `connection-legality` error (a reaction never records an event itself).
@@ -3,8 +3,8 @@
3
3
  Read this **before doing any extract work**. It governs the `extract` phase and **overrides
4
4
  `methodology.md` step 1's stance** for the duration of extraction: you are capturing how the
5
5
  system behaves **today**, not modeling what the business needs. Everything else in the
6
- methodology — the 4 patterns, the "is it an event?" test, the Socratic stance, the two-slice
7
- reaction split — still applies.
6
+ methodology — the 4 patterns, the "is it an event?" test, the Socratic stance, the
7
+ reaction-shares-a-slice-with-its-command shape — still applies.
8
8
 
9
9
  ## Stance override — current-state-only
10
10
 
@@ -81,7 +81,7 @@ it is fine to finish a round with `# TBD`s still parked.
81
81
  `em render` here.**
82
82
  - **R3 — Commands / inputs.** For each event, the request that actually produces it today
83
83
  (imperative name), *and what triggers it* — the real screen, or the scheduler/listener that
84
- fires it (model that as a reaction in the preceding slice, per R5). Unclear trigger → `# TBD`,
84
+ fires it (model that as a reaction sharing this same slice, per R5). Unclear trigger → `# TBD`,
85
85
  not a plausible guess; expect a *command has nothing that triggers it* warning until R5 fills
86
86
  it in, and don't invent a screen to silence it.
87
87
  - **R4 — Read models / outputs.** The projections/queries the system actually serves; wire
@@ -91,16 +91,18 @@ it is fine to finish a round with `# TBD`s still parked.
91
91
  a **finding about the system**, not a modelling mistake — never invent a reader to silence it
92
92
  (that would be desired state). Park it in the Decisions log and carry the warning.
93
93
  - **R5 — Boundaries & reactions.** Current automations and integrations as-is: schedulers,
94
- listeners, outbound syncs, inbound webhooks. Model each as a reaction with the two-slice
95
- `reaction → command → event` split and the slice-ordering rules in `em-dsl.md` — even when
96
- the current code wires it as one procedural step (note the as-built shape in the slice's
97
- `# TBD`/Decisions if it differs).
94
+ listeners, outbound syncs, inbound webhooks. Model each as a reaction sharing its slice with
95
+ the `command` + `event` it triggers (`reaction → command → event`, per `em-dsl.md`) — the
96
+ read model it watches, if any, lives in the slice before, named via the reaction's own
97
+ `from`. This matches how the current code usually already reads (scheduler/listener → one
98
+ procedural step); note any real as-built difference in the slice's `# TBD`/Decisions.
98
99
  - **R6 — Gap & TBD reconciliation.** Walk every parked `# TBD` with the user. Resolve only
99
100
  what they can confirm **as a current-state fact**; everything else stays parked. Explicitly
100
101
  refuse to resolve a TBD by inventing desired behavior.
101
- - **R7 — Convergence.** Final `em render` + `em validate` (fix errors and warnings; remember
102
- the validator's blind spot — check the reaction split by hand). User confirms the model is a
103
- faithful picture of today. Mark extraction complete in the state file and hand off.
102
+ - **R7 — Convergence.** Final `em render` + `em validate` (fix errors and warnings — this now
103
+ catches a reaction wired straight to an event too, via *reaction triggers no command*). User
104
+ confirms the model is a faithful picture of today. Mark extraction complete in the state file
105
+ and hand off.
104
106
 
105
107
  Seven rounds is the default shape, not a quota — collapse rounds for a small system, repeat
106
108
  one for a sprawling one. Record any deviation in the Decisions log.
@@ -25,16 +25,16 @@ tempting shortcuts:
25
25
  - **`command → read model`** — the CQRS violation. A write is only ever visible to a reader
26
26
  through the event it recorded. Put the event between them.
27
27
  - **`read model → command`** — reads never drive a write directly. A processor or translation
28
- watches the read model and issues the command, across two slices.
28
+ watches the read model and issues the command that sits in the same slice.
29
29
 
30
30
  If you find yourself wanting an arrow the patterns don't allow, the model is missing an
31
31
  **element**, not an arrow.
32
32
 
33
33
  **Automations and translations are not exceptions to rule 1.** They are *reactions* — a processor
34
34
  or an adapter that wakes up, decides something must change, and **issues a command** to do it. They
35
- never record an event themselves. A reaction box always points at a **command** (in the next
36
- slice), and that command produces the event. If you ever see a translation or automation wired
37
- straight to an event, the model is wrong.
35
+ never record an event themselves. A reaction box always points at a **command in its own slice** —
36
+ the same shape a `ui` already uses in State Change — and that command produces the event. If you
37
+ ever see a translation or automation wired straight to an event, the model is wrong.
38
38
 
39
39
  Events are **immutable past-tense facts** ("Order Placed", "Payment Captured"). They are the
40
40
  spine of the model. Everything else hangs off the events.
@@ -52,8 +52,8 @@ Every slice is exactly one of these. In `em`, each maps to a specific shape (see
52
52
  **UI → Command → Event.** A user (or automation) submits a command; the system validates it
53
53
  against invariants and records one or more events.
54
54
  - **A State Change never travels alone, and it's joined at both ends.** Something must *trigger*
55
- the command — the `ui` it's issued from, or (patterns 3 and 4) the reaction in the slice before
56
- it — and its event needs a read model that projects it. So the unit of work is a State Change
55
+ the command — the `ui` it's issued from, or (patterns 3 and 4) the reaction that triggers it,
56
+ also in this slice — and its event needs a read model that projects it. So the unit of work is a State Change
57
57
  slice *plus* the State View slice that reads its event. Write them together; a command nothing
58
58
  points at is a write nobody can start, an unread event is a write nobody can see, and
59
59
  `em validate` warns on each.
@@ -114,16 +114,17 @@ displays. Read-only; changes no state.
114
114
  expectation?"*
115
115
 
116
116
  ### 3. Automation (Processor pattern)
117
- **Read Model → Processor → (next slice) Command → Event.** The system reacts on its own: a
118
- processor watches a read model (a "to-do list" of work) and **issues a command** — it never
119
- records an event directly.
120
- - `em` shape: **two slices**, then the read slice that consumes the event — three in all:
117
+ **Read Model → Processor → Command → Event, the last three together in one slice.** The system
118
+ reacts on its own: a processor watches a read model (a "to-do list" of work) and **issues a
119
+ command** — it never records an event directly.
120
+ - `em` shape: the read model in the slice before, then the reaction with its command and event —
121
+ plus the read slice that consumes the event — three slices in all:
121
122
  ```em
122
123
  slice "Todo" {
123
124
  view Todo from "..."
124
- processor P
125
125
  }
126
- slice "Do It" { # the triggered command goes in the NEXT slice
126
+ slice "Do It" { # processor, command, and event all share this slice
127
+ processor P from "Todo"
127
128
  command C
128
129
  event E @Context
129
130
  }
@@ -132,9 +133,10 @@ records an event directly.
132
133
  ui Work Board @Persona # ...and every read model needs a consumer
133
134
  }
134
135
  ```
135
- The processor band holds no event; the command in the next slice produces it. Keeping the
136
- triggered command in the same slice as the processor is a validation warning — always split it.
137
- Forgetting the third slice leaves `E` unread, which is also a warning.
136
+ The processor never records the event directly — the command it triggers does, and that event
137
+ happens to live in the processor's own slice now because the command does. A processor with no
138
+ command in its slice (and no explicit arrow to one) is a validation warning: a decision the
139
+ system never acts on. Forgetting the third slice leaves `E` unread, which is also a warning.
138
140
  - **Naming:** name the read model after the pending work — `Payments To Process`,
139
141
  `Orders To Fulfill`, `Pending Approvals` — never after the triggering event. The event is what
140
142
  happened; the view is what's left to do. Reusing the event's name also collides in the shared
@@ -144,10 +146,11 @@ records an event directly.
144
146
  list does the processor watch? What command does it fire? What if it fails or retries?"*
145
147
 
146
148
  ### 4. Translation
147
- **Boundary crossing → command → event.** An adapter translates data across a boundary (an external
148
- system, or another bounded context) into the model's own language. Like an automation, a
149
- translation is a *reaction*: it **triggers a command**, never records an event directly. Two
150
- independent questions shape which form it takes — don't conflate them:
149
+ **Boundary crossing → command → event, the reaction and what it triggers together in one slice.**
150
+ An adapter translates data across a boundary (an external system, or another bounded context) into
151
+ the model's own language. Like an automation, a translation is a *reaction*: it **triggers a
152
+ command**, never records an event directly. Two independent questions shape which form it takes —
153
+ don't conflate them:
151
154
  - **Trigger source:** does the input come from outside the model, or from the model's own state
152
155
  pushed back out?
153
156
  - **Durable artifact:** is there a queryable, persisted thing behind the trigger (a queue, topic,
@@ -167,13 +170,14 @@ The `em` shape only tracks the second question — trigger source doesn't change
167
170
  it's scoped to the context/lane whose fact it represents, not by who committed it — the same
168
171
  move as an Anti-Corruption-Layer boundary event in DDD — and stays legitimate as long as it
169
172
  doesn't leak into the model's own domain vocabulary.
170
- - `em` shape: **two slices**, exactly like an automation, plus the read slice for the event:
173
+ - `em` shape: exactly like an automation — the read model (durable-artifact form only) in the
174
+ slice before, then the reaction with its command and event, plus the read slice for the event:
171
175
  ```em
172
176
  slice "Boundary" {
173
177
  view Source from "..." # durable-artifact form only; omit if there's nothing persisted
174
- translation T from "Source"
175
178
  }
176
- slice "Record It" {
179
+ slice "Record It" { # translation, command, and event all share this slice
180
+ translation T from "Source" # omit `from` entirely for the no-durable-artifact form
177
181
  command C
178
182
  event E @Context
179
183
  }
@@ -182,7 +186,7 @@ The `em` shape only tracks the second question — trigger source doesn't change
182
186
  ui Status Screen @Persona # ...and every read model needs a consumer
183
187
  }
184
188
  ```
185
- The translation band never carries an event.
189
+ The translation never records the event directly — the command it triggers does.
186
190
  - Socratic prompts: *"What boundary are we crossing, and which way? What outside system or context
187
191
  feeds us (or do we feed)? In what format? How do we know its data is trustworthy/complete? What
188
192
  internal **command** does it trigger, and what event does that command record?"*
@@ -237,9 +241,9 @@ each view to the events that feed it.
237
241
  ### Step 5 — Swimlanes & Apply the Patterns *(model)*
238
242
  Organize elements into swimlanes: one **persona** row per actor, one **context** (bounded
239
243
  context / aggregate) row per event family. Classify every slice as one of the **4 patterns** and
240
- wire them correctly — especially **split both automations and translations across two slices**
241
- (the reaction in one, the command + event in the next). A translation or automation never records
242
- an event directly; it triggers a command.
244
+ wire them correctly — especially **share the slice between every automation or translation and the
245
+ command it triggers** (the read model it watches, if any, stays in the slice before). A translation
246
+ or automation never records an event directly; it triggers a command.
243
247
  - Prompts: *"Which events belong together as one consistency boundary (aggregate)? Who owns this
244
248
  data? Is this slice a state change, a view, an automation, or a translation?"*
245
249
  - Output: a structurally complete model with swimlanes and pattern-correct slices.
@@ -258,8 +262,9 @@ dedicated `slice` phase writes the full rich spec (see `templates/slice.md`).
258
262
 
259
263
  ### Step 7 — Evaluate Completeness *(model)*
260
264
  Walk the whole model with stakeholders and check for loose ends:
261
- - Every **command** has something that **triggers** it — a `ui` in its slice, or a reaction in the
262
- slice before it. A command nothing points at is a write nobody can start. `em validate` warns.
265
+ - Every **command** has something that **triggers** it — a `ui` in its slice, or the reaction that
266
+ triggers it, also in its slice. A command nothing points at is a write nobody can start.
267
+ `em validate` warns.
263
268
  - Every **command** produces at least one **event**.
264
269
  - Every **view** has at least one source event, **and a consumer** — a `ui` (headless: the `ui`
265
270
  tagged to the API-caller persona), or a reaction watching it. Every *instance* of a repeated
@@ -273,11 +278,13 @@ Walk the whole model with stakeholders and check for loose ends:
273
278
  - Every connection is one of the six legal pairs (`ui → command`, `command → event`,
274
279
  `event → read model`, `read model → ui`, `read model → reaction`, `reaction → command`). No
275
280
  arrow between two instances of one read model.
276
- - Automations **and** translations are split correctly (reaction → command → event, never a
277
- reaction wired straight to an event); translations cover every external input.
278
- - Run `em validate` and resolve all errors and warnings. It now catches illegal `arrow` kind pairs
279
- (error) and unread events (warning), but it still does **not** catch a translation/automation that
280
- emits an event directly — verify the two-slice split by hand.
281
+ - Automations **and** translations are wired correctly (reaction → command → event, all in one
282
+ slice — never a reaction wired straight to an event, and never a reaction with no command at
283
+ all); translations cover every external input.
284
+ - Run `em validate` and resolve all errors and warnings. It catches illegal `arrow` kind pairs
285
+ (error), unread events (warning), and a reaction with no command in its slice (warning) — a
286
+ reaction wired straight to an event with no command anywhere is exactly what that last check
287
+ catches, so a clean `em validate` run does cover this case.
281
288
  - Prompts: *"Is there any event nobody reads — and if the business genuinely never looks at it, why
282
289
  are we recording it? Any command that just happens, with nobody and nothing asking for it? Any
283
290
  screen with no way in or out? Any command that records nothing? Any