@milehimikey/em 1.4.0 → 1.5.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.
@@ -48,11 +48,31 @@ validation rules) before doing real work — they are the source of truth. Templ
48
48
  triggers **no** command — the analog of `view → ui`). **Repeat read models** across slices so the
49
49
  timeline flows left-to-right — put each repeat **right after the event that feeds it**, sourcing
50
50
  only that one adjacent event, so every arrow is short (a read model far from its source events
51
- draws long arrows that look like a forbidden read→read link). Repeats render cleanly — em only
52
- warns on a duplicate name that's *referenced* by `from`/`arrow`. Slice order matters: a reaction
53
- must be directly followed by its command slice, and a read slice must not be immediately
51
+ draws a long arrow whose head lands columns away, making the *write* slice read as dangling —
52
+ keep a sub-flow that detours into another context together, not parked at the end of the model).
53
+ Declare every instance after the first with
54
+ **`view X again from "..."`** — `again` instances are exempt from the duplicate-name warning even
55
+ when referenced, and each reference resolves to the right instance. Slice order matters: a
56
+ reaction must be directly followed by its command slice, and a read slice must not be immediately
54
57
  followed by a command slice (see `em-dsl.md`).
55
58
  See `reference/methodology.md` (State View) and `reference/em-dsl.md`.
59
+ - **Never connect two instances of one read model.** The repeat is a timeline device, not a flow:
60
+ continuity is implied by the shared name, and the events arriving at each instance are what show
61
+ it changing. An arrow between instances says the read model feeds itself, and is an error.
62
+ - **Every slice is a COMPLETE pattern, never a half-slice.** A State Change is
63
+ `ui → command → event`; a State View is `event → read model → ui` (or `→ reaction`). So: a
64
+ command needs something that **triggers** it (the `ui` it's issued from, or the reaction in the
65
+ slice before it), its event needs a **reader**, and that read model needs a **consumer** of its
66
+ own. A command nothing points at is a write nobody can start; an event nothing projects is a
67
+ write nobody can see; a read model nothing displays is information dropped on the floor. Write
68
+ the whole chain together rather than sweeping up dangling ends later. Reactions don't count as
69
+ readers of *events* — they read views. **Every instance** of a repeated read model needs its own
70
+ consumer: if you repeat a view next to an event just to keep the arrow short, bring its screen
71
+ along, or don't add the instance.
72
+ - **Only six connections are legal:** `ui → command`, `command → event`, `event → read model`,
73
+ `read model → ui`, `read model → reaction`, `reaction → command`. Reaching for anything else —
74
+ above all `command → read model` (the CQRS violation) or `read model → command` — means the model
75
+ is missing an **element**, not an arrow.
56
76
  - **Validate continuously.** Run `em validate` and fix errors/warnings as you go (see DSL ref).
57
77
  - **Save state at the end of every session** so work resumes cleanly.
58
78
 
@@ -117,12 +137,20 @@ phase applies to field tables, below.
117
137
  **callers** at each step instead; they become the inbound write translations and read
118
138
  translations (see `reference/methodology.md`).
119
139
  3. **Inputs (step 3).** For each event, find the **command** that causes it (imperative name).
120
- Form `command → event` slices (State Change pattern).
140
+ Form `command → event` slices (State Change pattern). Every command needs a **trigger** in the
141
+ same breath: the screen the user issues it from (a `ui` in the slice), or — for the Automation
142
+ and Translation patterns — the reaction in the slice before it. A command nothing points at is
143
+ a write nobody can start. *"Who does this, and where are they when they do it?"*
121
144
  4. **Outputs (step 4).** Identify the **read models / views** consumers need and wire them with
122
- `from "Event"` (State View pattern). In a **headless/API** model there is no UI — a read model is
145
+ `from "Event"` (State View pattern), each with the screen (or reaction) that consumes it — a
146
+ read model nothing displays is information dropped on the floor. In a **headless/API** model there is no UI — a read model is
123
147
  consumed by an **external read translation** (the API query, triggers no command), and read
124
- models are **repeated** in each slice where they're read so the timeline flows (see
125
- `reference/methodology.md`).
148
+ models are **repeated** in each slice where they're read (use `view X again` after the first) so
149
+ the timeline flows (see `reference/methodology.md`).
150
+ **Close the loop before leaving this step:** every event from step 3 must be read by at least one
151
+ read model. Walk the event list and ask, for each one, *"who looks at this, and what do they do
152
+ with it?"* An event with no answer is either missing its read model or shouldn't be recorded —
153
+ both are worth raising with the user rather than leaving dangling.
126
154
 
127
155
  End of phase: write/refresh the `.em`, render it, update `.event-modeling.md` (steps done,
128
156
  decisions, open questions) and `README.md`'s slice index. Tell the user they can stop here and
@@ -164,12 +192,17 @@ Goal: a structurally complete, **validated** model with correct patterns and swi
164
192
  event).
165
193
  2. **Elaborate scenarios — first pass (step 6).** For each slice, capture the happy-path
166
194
  Given/When/Then and the obvious invariants as short notes. (The full spec is the `slice` phase.)
167
- 3. **Evaluate completeness (step 7).** Walk the model: every command emits an event, every view
168
- has a source, no orphan events, every UI is reachable, automations **and translations** split
169
- correctly (each reaction triggers a command — none wired straight to an event). Run
170
- `em validate` and resolve all errors and warnings — but check the reaction→command→event split
171
- by hand, since `em validate` does **not** flag a translation/automation that emits an event
172
- directly (see `reference/em-dsl.md`).
195
+ 3. **Evaluate completeness (step 7).** Walk the model: every slice is a **complete** pattern, not a
196
+ half-slice — **every command has something that triggers it**, every command emits an event,
197
+ **every event is read by a read model**, **every read model has a consumer**, every view has a
198
+ source, every UI is reachable, every connection is
199
+ one of the six legal pairs, and automations **and translations** split correctly (each reaction
200
+ triggers a command — none wired straight to an event). Run `em validate` and resolve all errors
201
+ and warnings — but check the reaction→command→event split by hand, since `em validate` does
202
+ **not** flag a translation/automation that emits an event directly (see `reference/em-dsl.md`).
203
+ For an unread event, don't just bolt on a view to silence the warning: ask the user who looks at
204
+ this fact and what they do with it. The honest answers are "here's the read model we missed" or
205
+ "nobody — so why are we recording it", and both improve the model.
173
206
 
174
207
  End of phase: render, update state, suggest `/event-modeling slice` to write implementation specs.
175
208
 
@@ -181,7 +214,9 @@ timeline). Check `README.md`'s slice index for what's already done.
181
214
  This is also where **branch / unhappy-path events** are discovered and added to the model — as a
182
215
  slice's alternate/error flows surface (a rejection, removal, cancellation, decline, expiry), add
183
216
  the corresponding event/slice to the `.em` and re-render. The happy-path spine from earlier
184
- phases is the starting point, not the finished event set.
217
+ phases is the starting point, not the finished event set. **Every new event needs its reader too**
218
+ — a rejection or cancellation that nothing projects will warn, and usually the missing piece is
219
+ real (someone has to see that the request was declined).
185
220
 
186
221
  For each slice:
187
222
  1. Hold a Socratic deep-dive to fill every section of `templates/slice.md`: intent, trigger/actor,
@@ -241,8 +276,19 @@ editing of `live.html` is needed — the `?svg=` query picks the model.
241
276
  ## Phase: `validate`
242
277
 
243
278
  Run `em validate <model-name>.em` and walk through each diagnostic with the user, explaining the
244
- rule (see `reference/em-dsl.md`) and proposing the fix (e.g. split an automation's command into
245
- the next slice, add a missing `from`, give a command its event). Apply fixes on agreement.
279
+ rule (see `reference/em-dsl.md`) and proposing the fix. Apply fixes on agreement. Common ones:
280
+
281
+ | Diagnostic | Fix |
282
+ |---|---|
283
+ | automation/translation shares a slice with a command | Split the command into the next slice |
284
+ | **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?"* |
285
+ | **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 |
286
+ | read model has no source | Add the missing `from "Event"` |
287
+ | command produces no event | Give the command its event, or drop the command |
288
+ | **event is not read by any read model** | Add the read slice that projects it — but ask *who looks at this and what do they do with it* first. "Nobody" is a real answer, and then the question is why it's recorded at all |
289
+ | **illegal `arrow` kind pair** | Don't reroute it — the model is missing an element. `command → view` needs the event between them; `view → command` needs a reaction. Between two instances of one read model, just delete the arrow: the repeat needs no connection |
290
+ | `view X again` with no earlier declaration | Declare the view plainly the first time it appears |
291
+ | event feeding an earlier view instance | Add a `view X again` where the event lands and move the source there |
246
292
 
247
293
  For anything genuinely unresolved rather than a rule violation, prefer `issue "text"` on the
248
294
  relevant element over a `# TBD` comment — it shows up as a red marker on the rendered diagram and
@@ -135,7 +135,9 @@ what every finding in the report will cite — file paths, not vibes.
135
135
  First `em validate <model-name>-asis.em`. `em diff` refuses when either side has validation
136
136
  *errors* (warnings are fine), so a scratch model that doesn't compile stops the run cold. The
137
137
  usual cause is a slice that depends on one you rewrote incompletely — a `view X again` whose
138
- first declaration you dropped, or an automation `from` a read model that no longer exists.
138
+ first declaration you dropped, an automation `from` a read model that no longer exists, or an
139
+ `arrow` you wrote joining kinds the patterns don't connect. (An unread event is only a *warning*,
140
+ so it never blocks the diff — and in an as-is scratch model it's expected.)
139
141
  Seeding from the canonical model (step 2) prevents this; if it still fails, **fix the scratch
140
142
  model and report the failure as a tooling problem in the run metadata — never as drift.** A
141
143
  model you couldn't compile is an uncertainty about your own scratch file, not a finding
@@ -64,9 +64,10 @@ arrow From Element -> To Element # explicit cross-slice edge (overrides infer
64
64
  - **`again`** (views only): `view <Name> again [from "Event", …]` declares a later instance of an
65
65
  already-declared read model — the forward-only device for a view that evolves as later events
66
66
  land. Instances are ONE logical view: the first declaration owns the `note` binding; each
67
- instance's `from` lists only the NEW events landing at that point (not cumulative); instances are
68
- linked left-to-right with a continuity arrow; a reaction (`from "View"`) reads the nearest
69
- instance at-or-before its own slice. `again` with no earlier declaration is a validation error.
67
+ instance's `from` lists only the NEW events landing at that point (not cumulative); a reaction
68
+ (`from "View"`) reads the nearest instance at-or-before its own slice. Instances are NEVER
69
+ connected to one another — continuity is implied by the shared name, and the events reaching each
70
+ instance are what show the view changing. `again` with no earlier declaration is a validation error.
70
71
  Use `again` (not a plain repeated `view` name) whenever the view is referenced by a
71
72
  `from`/`arrow` — plain repeats are only warning-free while unreferenced.
72
73
  - **`note "path.md"`** on ANY element links a markdown doc. Relative to the `.em` file. Renders as
@@ -106,7 +107,7 @@ slice "Open Orders" {
106
107
  ui Order List @Customer
107
108
  }
108
109
 
109
- # 3. Automation: split across TWO slices
110
+ # 3. Automation: split across TWO slices (plus the read slice that consumes the event)
110
111
  slice "Orders To Fulfill" {
111
112
  view Orders To Fulfill from "Order Placed"
112
113
  processor Fulfillment Service
@@ -115,6 +116,10 @@ slice "Ship Order" { # the triggered command goes in the NEXT slice
115
116
  command Ship Order
116
117
  event Order Shipped @Shipping
117
118
  }
119
+ slice "Open Orders — shipped" {
120
+ view Open Orders again from "Order Shipped" # every event needs a reader
121
+ ui Order List @Customer # ...and every read model needs a consumer
122
+ }
118
123
 
119
124
  # 4a. Translation (external trigger): external input -> translation -> command -> event
120
125
  slice "Carrier Webhook" {
@@ -124,8 +129,17 @@ slice "Confirm Delivery" { # the triggered command goes in the NEXT s
124
129
  command Confirm Delivery
125
130
  event Delivery Confirmed @Shipping
126
131
  }
132
+ slice "Open Orders — delivered" {
133
+ view Open Orders again from "Delivery Confirmed"
134
+ ui Order List @Customer
135
+ }
127
136
 
128
137
  # 4b. Translation (internal trigger): read model -> translation -> command -> event
138
+ slice "Accept Quote" {
139
+ ui Quote Screen @Customer # every command needs a trigger: a ui, or a reaction before it
140
+ command Accept Quote
141
+ event Quote Accepted @Quote
142
+ }
129
143
  slice "Quotes To Sync" {
130
144
  view Accepted Quotes from "Quote Accepted"
131
145
  translation CRM Sync # reacts to our own state via the read model in this slice
@@ -134,40 +148,66 @@ slice "Record Sync" {
134
148
  command Record Crm Sync
135
149
  event Quote Synced @Quote
136
150
  }
151
+ slice "Accepted Quotes — synced" {
152
+ view Accepted Quotes again from "Quote Synced"
153
+ ui Sync Status @Customer
154
+ }
137
155
  ```
138
156
 
139
157
  A `translation` (like a `processor`) is a **reaction**: it triggers a command and never carries an
140
158
  `event` in its own slice. Same two-slice split as the Automation pattern above.
141
159
 
160
+ Note the read slice closing each pattern: **every event must be read by some read model**
161
+ (warning 4 below). A command slice is not finished until the slice that projects its event
162
+ exists. Reactions don't count — they read *views*, not events.
163
+
142
164
  ### Headless / API systems (no UI) & repeated read models
143
165
 
144
166
  When the system is headless (no UI — clients call an API), drop `ui`/`persona` entirely:
145
167
 
168
+ A slice body is always multi-line — there is **no one-line `slice "X" { … }` form**, and writing
169
+ one is a parse error (`'slice' is not valid inside a slice`).
170
+
146
171
  ```em
147
172
  # Write: external translation -> command -> event (name the inbound adapter for the caller/role)
148
- slice "Create Quote (request)" { translation SalesRep BU Create Quote }
149
- slice "Create Quote" { command Create Quote event QuoteCreated @Quote }
173
+ slice "Create Quote (request)" {
174
+ translation SalesRep BU Create Quote
175
+ }
176
+ slice "Create Quote" {
177
+ command Create Quote
178
+ event QuoteCreated @Quote
179
+ }
150
180
 
151
- # Read: event(s) -> read model -> READ translation (the external caller's API query, replaces UI)
152
- slice "Read Quote — created" { view Quote from "QuoteCreated" translation SalesRep BU Read Quote }
181
+ # Read: event(s) -> read model -> READ translation (the external caller's API query, replaces UI).
182
+ # This slice is also what makes QuoteCreated a *read* event — without it the model warns.
183
+ slice "Read Quote — created" {
184
+ view Quote from "QuoteCreated"
185
+ translation SalesRep BU Read Quote
186
+ }
153
187
  ```
154
188
 
155
189
  - A **read translation triggers no command** (it returns data outbound) — it is the headless analog
156
190
  of `view → ui`, *not* a reaction. Only *reaction* translations/processors trigger commands. A read
157
191
  slice has no `command` and no `event`, so `em validate` stays quiet about it.
158
- - **Repeat the read model** in every slice where it's read so the timeline flows left-to-right.
159
- Recurring `view`/`translation` names **render cleanly and stay warning-free** as long as nothing
160
- references the repeated name via `from`/`arrow` (the duplicate-name warning fires only for a
161
- *referenced* duplicate, resolving to the first). **Wire each event to a read model exactly once:**
162
- a repeated instance's `from` lists only the **new** events since the previous instance (not
163
- cumulative), or the event draws a duplicate arrow to the same read model at every repeat. (An
164
- event may still feed several *different* read models, once each.) When a repeated view **must be
165
- referenced** — e.g. a reaction reads it via `from` — declare its later instances with
166
- `view X again` instead (see Clauses); instances resolve properly and draw continuity arrows.
192
+ - **Repeat the read model** in every slice where it's read so the timeline flows left-to-right, and
193
+ **prefer `view X again`** for every instance after the first (see Clauses). `again` instances are
194
+ exempt from the duplicate-name warning even when referenced, and each reference resolves to the
195
+ right instance — a plain repeat only stays warning-free while nothing references it by name, and
196
+ resolves to the *first* declaration when something does. **Wire each event to a read model exactly
197
+ once:** a repeated instance's `from` lists only the **new** events since the previous instance
198
+ (not cumulative), or the event draws a duplicate arrow to the same read model at every repeat. (An
199
+ event may still feed several *different* read models, once each.)
200
+ - **Instances are never joined to one another.** No arrow between two instances of one read model,
201
+ ever — an explicit one is a validation error. The repeat is a timeline device: continuity is
202
+ implied by the shared name, and the events arriving at each instance are what show it changing.
167
203
  - **Keep arrows span-1: put each repeat right after its feeding event.** Place a read-model instance
168
- immediately after the event that updates it, sourcing only that single adjacent event. A read
169
- model far from its source events draws long arrows that cross the read-model row and read as
170
- read→read (an Event Modeling violation) — repeat the read model next to each event instead.
204
+ immediately after the event that updates it, sourcing only that single adjacent event. The
205
+ renderer routes a long arrow *around* intervening boxes rather than through them, so it no longer
206
+ reads as a forbidden read→read link — but distance is still a real problem: the arrowhead lands
207
+ columns away from the event that produced it, so **the write slice reads as dangling**, as if
208
+ nothing consumed its event. You have to trace a line across the diagram to see the connection.
209
+ Keep the read model adjacent to its event, and keep a sub-flow that detours into another context
210
+ together rather than parking it at the end of the model.
171
211
 
172
212
  ### Slice-ordering gotchas (edge inference)
173
213
 
@@ -197,20 +237,33 @@ slice "Read Quote — created" { view Quote from "QuoteCreated" translation S
197
237
  there), a reaction reading a view before any instance of it exists, or an explicit backward
198
238
  `arrow`.
199
239
  6. **`again` without an earlier declaration** — declare the view plainly the first time it appears.
240
+ 7. **Illegal connection** — an `arrow` joining kinds the patterns don't connect. Only
241
+ `ui -> command`, `command -> event`, `event -> view`, `view -> ui`, `view -> reaction`, and
242
+ `reaction -> command` are allowed. A command straight to a view is the CQRS violation (an event
243
+ has to sit between them); a view straight to a command needs a reaction between them; and two
244
+ instances of one view are never connected at all. Inferred edges are always legal, so this only
245
+ ever fires on a hand-written `arrow`.
200
246
 
201
247
  **Warnings (should fix):**
202
248
  1. **Automation/translation shares slice with its command** — both `automation`/`processor` and
203
249
  `translation` are reactions; put the triggered command in the *next* slice.
204
- 2. **Command without event** — every command should record at least one event.
205
- 3. **Read model without source** — add `from "Event"` or place the view in a slice with an event.
206
- 4. **Duplicate name** — the same name defined N times; references resolve to the first. Rename.
207
- 5. **Open issue** — an element carries `issue "text"`; resolve the question, then remove the
250
+ 2. **Command with no trigger** — nothing issues it. A command needs a `ui` in its slice, or a
251
+ reaction (automation/processor/saga/translation) in the slice *before* it. The input-side mirror
252
+ of (4): a command nothing points at is a write nobody can start.
253
+ 3. **Command without event** — every command should record at least one event.
254
+ 4. **Event nobody reads** — the mirror of (3): recording an event no read model projects is a
255
+ write with no reader. Follow every write slice with the read slice that consumes its event.
256
+ Counts as read when a `view` names it in `from` (any `again` instance will do), when a
257
+ fieldless-`from` view sits in its slice, or via an explicit `event -> view` arrow.
258
+ 5. **Read model without source** — add `from "Event"` or place the view in a slice with an event.
259
+ 6. **Duplicate name** — the same name defined N times; references resolve to the first. Rename.
260
+ 7. **Open issue** — an element carries `issue "text"`; resolve the question, then remove the
208
261
  clause. `em validate --list-issues` prints just these; `--fail-on-issues` (opt-in) makes CI
209
262
  fail while any remain open.
210
- 6. **View field with no source** — a `view` field whose name matches no field on any instance
263
+ 8. **View field with no source** — a `view` field whose name matches no field on any instance
211
264
  of its source events. Only checked once BOTH the view and at least one source event declare
212
265
  `{ fields }`.
213
- 7. **Event field not from a command** — an `event` field whose name matches no field on any
266
+ 9. **Event field not from a command** — an `event` field whose name matches no field on any
214
267
  command in the same slice. Only checked once BOTH the event and at least one same-slice
215
268
  command declare `{ fields }`. This is the payoff of the fields feature for slicing rigor:
216
269
  once fields are written down, `em validate` checks that data flows forward consistently.
@@ -218,6 +271,20 @@ slice "Read Quote — created" { view Quote from "QuoteCreated" translation S
218
271
  **Design rules that keep models valid:**
219
272
  - One element per band per slice (multiple personas/contexts are fine — they're different rows).
220
273
  - Every `command` slice includes its `event`. Every `view` has a `from` source.
274
+ - **Every `command` has a trigger.** A `ui` in its slice, or a reaction in the slice *before* it.
275
+ A command nothing points at is a write nobody can start.
276
+ - **Every `event` has a reader.** A command slice isn't finished until the read slice that projects
277
+ its event exists. Reactions don't count — they read views, not events. Pair each write slice with
278
+ its read slice as you go rather than sweeping up dangling events at the end.
279
+ - **Every `view` has a consumer.** A `ui` in its slice, a reaction watching it, or (headless) a read
280
+ translation. Every *instance* of a repeat, not just the last — a bare `view X again` slice is a
281
+ half-slice, not a State View.
282
+ - **Only six connections are legal**, and only these are ever inferred:
283
+ `ui → command`, `command → event`, `event → view`, `view → ui`, `view → reaction`,
284
+ `reaction → command`. Anything else in an explicit `arrow` is an error — above all
285
+ `command → view` (the CQRS violation: an event has to sit between them) and `view → command`
286
+ (a reaction has to sit between them). If you reach for an arrow the patterns don't allow, the
287
+ model is missing an element, not an arrow.
221
288
  - Automations **and translations** are always two slices: the reaction (plus its read model, if
222
289
  internally triggered) in one slice, the triggered `command` + its `event` in the next. A
223
290
  translation/automation slice **never contains an `event`** — reactions trigger commands, not
@@ -70,10 +70,16 @@ it is fine to finish a round with `# TBD`s still parked.
70
70
  *actual* actors/callers and the *real* UI or API surfaces (headless systems: no
71
71
  `ui`/`persona` — use write/read translations per `em-dsl.md`). **First `em render` here.**
72
72
  - **R3 — Commands / inputs.** For each event, the request that actually produces it today
73
- (imperative name). Unclear trigger → `# TBD`, not a plausible guess.
73
+ (imperative name), *and what triggers it* — the real screen, or the scheduler/listener that
74
+ fires it (model that as a reaction in the preceding slice, per R5). Unclear trigger → `# TBD`,
75
+ not a plausible guess; expect a *command has nothing that triggers it* warning until R5 fills
76
+ it in, and don't invent a screen to silence it.
74
77
  - **R4 — Read models / outputs.** The projections/queries the system actually serves; wire
75
- `from "Event"` (repeat read models span-1 per `em-dsl.md`). **Start running `em validate`**
76
- each round from here.
78
+ `from "Event"` (repeat read models span-1 per `em-dsl.md`, using `view X again` after the
79
+ first). **Start running `em validate`** each round from here. Expect *event is not read by any
80
+ read model* warnings: real systems do record fire-and-forget events. In an as-is model that is
81
+ a **finding about the system**, not a modelling mistake — never invent a reader to silence it
82
+ (that would be desired state). Park it in the Decisions log and carry the warning.
77
83
  - **R5 — Boundaries & reactions.** Current automations and integrations as-is: schedulers,
78
84
  listeners, outbound syncs, inbound webhooks. Model each as a reaction with the two-slice
79
85
  `reaction → command → event` split and the slice-ordering rules in `em-dsl.md` — even when
@@ -103,8 +109,9 @@ one for a sprawling one. Record any deviation in the Decisions log.
103
109
  ## Completion & handoff
104
110
 
105
111
  Extraction is complete when: the model renders; `em validate` is clean (or remaining warnings
106
- are consciously accepted in the Decisions log); every unknown is a parked `# TBD`; and the
107
- user has confirmed the model reflects today's behavior. Then:
112
+ are consciously accepted in the Decisions log — unread events in an as-is model are the usual
113
+ accepted case, and each one is worth naming explicitly as a finding); every unknown is a parked
114
+ `# TBD`; and the user has confirmed the model reflects today's behavior. Then:
108
115
 
109
116
  1. State file: set phase progress (extraction rounds checked), log the completion decision.
110
117
  Seed `README.md`'s **slice index** (every slice, pattern, status `none`).
@@ -16,6 +16,20 @@ three ways:
16
16
  3. There is **no other way** for information to flow. Every box on the diagram is one of:
17
17
  UI screen, command, event, read model, or automation/processor.
18
18
 
19
+ Concretely, that leaves exactly six legal connections, and `em` infers only these:
20
+ `ui → command`, `command → event`, `event → read model`, `read model → ui`,
21
+ `read model → reaction`, `reaction → command`. Every other pair is a rule violation, and an
22
+ explicit `arrow` writing one is a validation error. The two worth naming, because they are the
23
+ tempting shortcuts:
24
+
25
+ - **`command → read model`** — the CQRS violation. A write is only ever visible to a reader
26
+ through the event it recorded. Put the event between them.
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.
29
+
30
+ If you find yourself wanting an arrow the patterns don't allow, the model is missing an
31
+ **element**, not an arrow.
32
+
19
33
  **Automations and translations are not exceptions to rule 1.** They are *reactions* — a processor
20
34
  or an adapter that wakes up, decides something must change, and **issues a command** to do it. They
21
35
  never record an event themselves. A reaction box always points at a **command** (in the next
@@ -37,7 +51,24 @@ Every slice is exactly one of these. In `em`, each maps to a specific shape (see
37
51
  ### 1. State Change (Command pattern)
38
52
  **UI → Command → Event.** A user (or automation) submits a command; the system validates it
39
53
  against invariants and records one or more events.
40
- - `em` shape: `slice { ui X @Persona command Y event Z @Context }`
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
57
+ slice *plus* the State View slice that reads its event. Write them together; a command nothing
58
+ points at is a write nobody can start, an unread event is a write nobody can see, and
59
+ `em validate` warns on each.
60
+ - `em` shape (one element per line — there is no one-line slice form):
61
+ ```em
62
+ slice "Do The Thing" {
63
+ ui X @Persona
64
+ command Y
65
+ event Z @Context
66
+ }
67
+ slice "See The Result" { # the State View slice that reads Z — pattern 2, but required here
68
+ view V from "Z"
69
+ ui Screen @Persona
70
+ }
71
+ ```
41
72
  - This is where **invariants** live — the command is rejected if a rule is violated.
42
73
  - Socratic prompts: *"What does the user do here? What request are they making? What must be
43
74
  true for it to succeed? What fact gets recorded when it does? What gets rejected and why?"*
@@ -55,19 +86,30 @@ displays. Read-only; changes no state.
55
86
  - **Repeat read models across the timeline.** A read model is drawn fresh in **every** slice where
56
87
  it is read — after the events that update it, before the actions that consume it — so the diagram
57
88
  shows state flowing left-to-right (the information-completeness staircase). The same read-model
58
- name recurring is intentional and **renders cleanly**: `em validate` only warns about a duplicate
59
- name that is *also referenced* by a `from`/`arrow` (it resolves to the first), so a repeated read
60
- model that nothing references by name produces **no warning**. **Wire each event to a 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
61
94
  exactly once:** a repeated instance's `from` lists only the **new** events since the previous
62
95
  instance (not cumulative) — otherwise an event draws a duplicate arrow to the same read model at
63
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.
64
102
  - **Place each repeat immediately after its feeding event (span-1).** The cleanest staircase puts a
65
103
  read-model instance right after each event that updates it, sourcing **only that one adjacent
66
104
  event** — every `event → read model` arrow is then short and forward-flowing. A read model sitting
67
105
  far from its source events (e.g. a "list/queue" read placed early but fed by late events) draws
68
- long arrows that cross the read-model row and *look* like read→read connections — which Event
69
- Modeling forbids. The fix is never a `view → view` edge; it's to repeat the read model next to each
70
- event that feeds it.
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.
71
113
  - Socratic prompts: *"What does the consumer need to see to make their next decision? Which past
72
114
  events provide that information? Is this a screen or an API read? What's the freshness/consistency
73
115
  expectation?"*
@@ -76,10 +118,24 @@ displays. Read-only; changes no state.
76
118
  **Read Model → Processor → (next slice) Command → Event.** The system reacts on its own: a
77
119
  processor watches a read model (a "to-do list" of work) and **issues a command** — it never
78
120
  records an event directly.
79
- - `em` shape: **two slices.** First `slice { view "Todo" from "..." processor P }`, then the
80
- **next** `slice { command C event E @Context }`. The processor band holds no event; the command
81
- in the next slice produces it. Keeping the triggered command in the same slice as the processor
82
- is a validation warning — always split it.
121
+ - `em` shape: **two slices**, then the read slice that consumes the event — three in all:
122
+ ```em
123
+ slice "Todo" {
124
+ view Todo from "..."
125
+ processor P
126
+ }
127
+ slice "Do It" { # the triggered command goes in the NEXT slice
128
+ command C
129
+ event E @Context
130
+ }
131
+ slice "Todo — done" {
132
+ view Todo again from "E" # every event needs a reader; here it clears the to-do list
133
+ ui Work Board @Persona # ...and every read model needs a consumer
134
+ }
135
+ ```
136
+ The processor band holds no event; the command in the next slice produces it. Keeping the
137
+ triggered command in the same slice as the processor is a validation warning — always split it.
138
+ Forgetting the third slice leaves `E` unread, which is also a warning.
83
139
  - Socratic prompts: *"What should happen without a human? What condition triggers it? What work
84
140
  list does the processor watch? What command does it fire? What if it fails or retries?"*
85
141
 
@@ -92,9 +148,22 @@ trigger forms:
92
148
  from outside the model, so the translation has no internal `from`.
93
149
  - **Internally triggered** (reacting to the modeled system's own state, e.g. pushing data out):
94
150
  `read model → translation → command → event`. The translation reads a **view** via `from`.
95
- - `em` shape: **two slices**, exactly like an automation. First
96
- `slice { [view "Source" from "..."] translation T [from "Source"] }`, then the **next**
97
- `slice { command C event E @Context }`. The translation band never carries an event.
151
+ - `em` shape: **two slices**, exactly like an automation, plus the read slice for the event:
152
+ ```em
153
+ slice "Boundary" {
154
+ view Source from "..." # internally triggered only; omit for an external trigger
155
+ translation T from "Source"
156
+ }
157
+ slice "Record It" {
158
+ command C
159
+ event E @Context
160
+ }
161
+ slice "Source — recorded" {
162
+ view Source again from "E" # every event needs a reader
163
+ ui Status Screen @Persona # ...and every read model needs a consumer
164
+ }
165
+ ```
166
+ The translation band never carries an event.
98
167
  - Socratic prompts: *"What boundary are we crossing, and which way? What outside system or context
99
168
  feeds us (or do we feed)? In what format? How do we know its data is trustworthy/complete? What
100
169
  internal **command** does it trigger, and what event does that command record?"*
@@ -170,17 +239,30 @@ dedicated `slice` phase writes the full rich spec (see `templates/slice.md`).
170
239
 
171
240
  ### Step 7 — Evaluate Completeness *(model)*
172
241
  Walk the whole model with stakeholders and check for loose ends:
242
+ - Every **command** has something that **triggers** it — a `ui` in its slice, or a reaction in the
243
+ slice before it. A command nothing points at is a write nobody can start. `em validate` warns.
173
244
  - Every **command** produces at least one **event**.
174
- - Every **view** has at least one source event.
175
- - Every **event** is shown somewhere or consumed by something (no orphan facts).
245
+ - Every **view** has at least one source event, **and a consumer** — a `ui`, a reaction watching it,
246
+ or (headless) a read translation. Every *instance* of a repeated view, not just the last one.
247
+ A read model nothing displays is information projected out and then dropped. `em validate` warns.
248
+ - Every **event** is **read by a read model** — a `view` naming it in `from` (any `again` instance
249
+ counts), or a `view` with no `from` sitting in its slice. A reaction consuming it does **not**
250
+ count: reactions read views, not events. An event nothing projects is a write nobody can see, so
251
+ there was no point recording it. `em validate` warns on each one.
176
252
  - Every **UI** is reachable and leads somewhere.
253
+ - Every connection is one of the six legal pairs (`ui → command`, `command → event`,
254
+ `event → read model`, `read model → ui`, `read model → reaction`, `reaction → command`). No
255
+ arrow between two instances of one read model.
177
256
  - Automations **and** translations are split correctly (reaction → command → event, never a
178
257
  reaction wired straight to an event); translations cover every external input.
179
- - Run `em validate` and resolve all errors and warnings. Note: `em validate` does **not** catch a
180
- translation/automation that emits an event directly — verify the two-slice split by hand.
181
- - Prompts: *"Is there any event nobody sees? Any screen with no way in or out? Any command that
182
- records nothing? Any external system we haven't translated? Any translation or automation that
183
- records an event instead of triggering a command?"*
258
+ - Run `em validate` and resolve all errors and warnings. It now catches illegal `arrow` kind pairs
259
+ (error) and unread events (warning), but it still does **not** catch a translation/automation that
260
+ emits an event directly — verify the two-slice split by hand.
261
+ - Prompts: *"Is there any event nobody reads — and if the business genuinely never looks at it, why
262
+ are we recording it? Any command that just happens, with nobody and nothing asking for it? Any
263
+ screen with no way in or out? Any command that records nothing? Any
264
+ external system we haven't translated? Any translation or automation that records an event instead
265
+ of triggering a command?"*
184
266
 
185
267
  ---
186
268
 
@@ -21,6 +21,15 @@ Static render: `em render {{model-name}}.em -o {{model-name}}.svg`
21
21
  - **Automation** — Read Model → Processor → (next slice) Command → Event
22
22
  - **Translation** — External input (or Read Model) → Translation → (next slice) Command → Event
23
23
 
24
+ Between them these are the only legal connections: `ui → command`, `command → event`,
25
+ `event → read model`, `read model → ui`, `read model → reaction`, `reaction → command`. A command
26
+ never reaches a read model directly — the event goes between them. Every slice is joined up at
27
+ both ends: something triggers each command (the screen it's issued from, or the reaction in the
28
+ slice before it), and every event a command records is read by some read model — so each State
29
+ Change slice is paired with the State View slice that projects its event. A read model repeated
30
+ along the timeline (`view X again`) shows the same projection at a later point; the instances are
31
+ never connected to one another.
32
+
24
33
  ## Slices
25
34
  <!-- The canonical slice index — the ONE place slices are enumerated (the state file
26
35
  points here rather than keeping its own copy). Keep it in sync as slice docs are
@@ -32,9 +32,20 @@ an event directly.}}
32
32
  |-------|------|----------|--------------------|
33
33
  | {{field}} | {{Type}} | {{yes/no}} | {{constraints, formats, ranges}} |
34
34
 
35
+ ## Trigger
36
+ <!-- What issues this slice's command. Required: a command nothing points at is a write nobody
37
+ can start. Either the screen the user acts on (a `ui` in this slice), or the reaction that
38
+ issues it (an automation/processor/translation in the PREVIOUS slice). -->
39
+ **Triggered by:** {{screen `X` @Persona | processor `Y` in slice "Z"}}
40
+
35
41
  ## Event(s) Emitted
36
42
  <!-- The immutable facts recorded. List each event and its payload. -->
37
43
  **Event:** `{{Event Name}}` → context `{{Context}}`
44
+ **Read by:** {{which read model projects this event, and in which slice}}
45
+ <!-- Required, not optional. Every event must be read by a read model — an event nothing
46
+ projects is a write nobody can see, and `em validate` warns on it. A reaction consuming
47
+ it does NOT count: reactions read views, not events. If the honest answer is "nothing
48
+ reads it", that's a question for the business, not a field to leave blank. -->
38
49
 
39
50
  | Field | Type | Immutable Fact? | Source / Notes |
40
51
  |-------|------|-----------------|----------------|
@@ -43,7 +54,10 @@ an event directly.}}
43
54
  ## Read Model / View
44
55
  <!-- For State View slices, and any read model this slice produces or feeds. -->
45
56
  - **View:** `{{View Name}}` built from events: {{"Event A", "Event B"}}
46
- - **Consumed by:** {{which UI screen or automation}}
57
+ - **Consumed by:** {{which UI screen, reaction, or read translation}}
58
+ <!-- "Consumed by" is required, not optional. A read model nothing displays or watches is
59
+ information projected out of the system and then dropped, and `em validate` warns. Every
60
+ instance of a repeated view needs its own consumer, not just the last one. -->
47
61
  - **Freshness / consistency expectation:** {{real-time | eventual | on-demand}}
48
62
 
49
63
  ## Invariants / Business Rules