@eventmodelers/cli 1.0.36 → 1.0.37
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/package.json +1 -1
- package/shared/skills/learn-eventmodelers-api/SKILL.md +12 -10
- package/stacks/modeling-kit/templates/.claude/skills/add-next-slice/SKILL.md +2 -23
- package/stacks/modeling-kit/templates/.claude/skills/add-next-slice/references/api-fallback.md +11 -0
- package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/SKILL.md +6 -57
- package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/references/api-fallback.md +68 -0
- package/stacks/modeling-kit/templates/.claude/skills/attributes/SKILL.md +4 -61
- package/stacks/modeling-kit/templates/.claude/skills/attributes/references/api-fallback.md +39 -0
- package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/SKILL.md +9 -53
- package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/references/api-fallback.md +63 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-applying-conways-law/SKILL.md +9 -319
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-applying-conways-law/references/examples.md +329 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/SKILL.md +23 -199
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/references/api-fallback.md +97 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/references/examples.md +35 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/SKILL.md +13 -410
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/references/api-fallback.md +22 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/references/examples.md +397 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-automation-chains/SKILL.md +132 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-automation-chains/references/api-fallback.md +21 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-event-models/SKILL.md +9 -236
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-event-models/references/examples.md +257 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md +28 -302
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/references/api-fallback.md +31 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/references/examples.md +216 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/SKILL.md +30 -343
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/references/api-fallback.md +79 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/references/examples.md +282 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/SKILL.md +51 -400
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/references/api-fallback.md +67 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/references/examples.md +273 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/SKILL.md +45 -152
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/domain-patterns.md +49 -90
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/patterns.md +64 -137
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/SKILL.md +74 -65
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/references/api-fallback.md +51 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/SKILL.md +1 -5
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/references/api-fallback.md +10 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/SKILL.md +19 -36
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/api-fallback.md +41 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/examples.md +12 -9
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/patterns.md +1 -10
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/SKILL.md +26 -332
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/references/api-fallback.md +77 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/references/examples.md +271 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-translating-external-events/SKILL.md +9 -294
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-translating-external-events/references/examples.md +306 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/SKILL.md +12 -11
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/references/api-fallback.md +14 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/SKILL.md +6 -36
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/references/api-fallback.md +14 -0
- package/stacks/modeling-kit/templates/.claude/skills/examples/SKILL.md +3 -110
- package/stacks/modeling-kit/templates/.claude/skills/examples/references/api-fallback.md +118 -0
- package/stacks/modeling-kit/templates/.claude/skills/handle-comment/SKILL.md +5 -25
- package/stacks/modeling-kit/templates/.claude/skills/handle-comment/references/api-fallback.md +35 -0
- package/stacks/modeling-kit/templates/.claude/skills/html-screen/SKILL.md +9 -44
- package/stacks/modeling-kit/templates/.claude/skills/html-screen/references/api-fallback.md +51 -0
- package/stacks/modeling-kit/templates/.claude/skills/place-element/SKILL.md +23 -183
- package/stacks/modeling-kit/templates/.claude/skills/place-element/references/api-fallback.md +193 -0
- package/stacks/modeling-kit/templates/.claude/skills/storyboard/SKILL.md +14 -81
- package/stacks/modeling-kit/templates/.claude/skills/storyboard/references/api-fallback.md +74 -0
- package/stacks/modeling-kit/templates/.claude/skills/storyboard-screen/SKILL.md +4 -45
- package/stacks/modeling-kit/templates/.claude/skills/storyboard-screen/references/api-fallback.md +44 -0
- package/stacks/modeling-kit/templates/.claude/skills/timeline/SKILL.md +19 -88
- package/stacks/modeling-kit/templates/.claude/skills/timeline/references/api-fallback.md +91 -0
- package/stacks/modeling-kit/templates/.claude/skills/update-prompt-status/SKILL.md +1 -9
- package/stacks/modeling-kit/templates/.claude/skills/update-prompt-status/references/api-fallback.md +14 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-integrating-legacy-systems/SKILL.md +0 -674
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/snapshotting.md +0 -204
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/SKILL.md
CHANGED
|
@@ -174,181 +174,13 @@ InventoryLevelView
|
|
|
174
174
|
|
|
175
175
|
## Workflow
|
|
176
176
|
|
|
177
|
-
Given commands and events, identify all outputs:
|
|
178
|
-
|
|
179
|
-
### 1. Map Event Data to UI Screens
|
|
180
|
-
For each screen, identify source events:
|
|
181
|
-
|
|
182
|
-
```
|
|
183
|
-
Screen: Order Status View
|
|
184
|
-
Displays data from events:
|
|
185
|
-
orderId ← OrderCreated event
|
|
186
|
-
customerId ← OrderCreated event
|
|
187
|
-
items ← OrderCreated event
|
|
188
|
-
total ← OrderCreated event
|
|
189
|
-
status ← OrderConfirmed event (or OrderCancelled)
|
|
190
|
-
confirmedAt ← OrderConfirmed event
|
|
191
|
-
paymentId ← PaymentAuthorized event
|
|
192
|
-
shipmentId ← OrderShipped event
|
|
193
|
-
shippedAt ← OrderShipped event
|
|
194
|
-
|
|
195
|
-
This screen is a projection of these events:
|
|
196
|
-
- OrderCreated
|
|
197
|
-
- OrderConfirmed
|
|
198
|
-
- PaymentAuthorized
|
|
199
|
-
- OrderShipped
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
### 2. Define Read Models
|
|
203
|
-
Create optimized views from event data:
|
|
204
|
-
|
|
205
|
-
```
|
|
206
|
-
ReadModel: OrderStatusView
|
|
207
|
-
Purpose: UI displays current order status
|
|
208
|
-
Events subscribed: OrderCreated, OrderConfirmed, PaymentAuthorized, OrderShipped, OrderCancelled
|
|
209
|
-
Data:
|
|
210
|
-
{
|
|
211
|
-
orderId: string (from OrderCreated)
|
|
212
|
-
customerId: string (from OrderCreated)
|
|
213
|
-
status: enum (from events: Draft → Confirmed → Authorized → Shipped → Delivered)
|
|
214
|
-
createdAt: Date (from OrderCreated)
|
|
215
|
-
confirmedAt: Date (from OrderConfirmed)
|
|
216
|
-
paymentId: string (from PaymentAuthorized)
|
|
217
|
-
shipmentId: string (from OrderShipped)
|
|
218
|
-
shippedAt: Date (from OrderShipped)
|
|
219
|
-
}
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
### 3. Document Event → Data Mapping
|
|
223
|
-
Show exactly what data each event provides:
|
|
224
|
-
|
|
225
|
-
```
|
|
226
|
-
Event: OrderCreated
|
|
227
|
-
Provides to UI/Processors:
|
|
228
|
-
orderId
|
|
229
|
-
customerId
|
|
230
|
-
items[]
|
|
231
|
-
total
|
|
232
|
-
shippingAddress
|
|
233
|
-
createdAt
|
|
234
|
-
|
|
235
|
-
Event: OrderConfirmed
|
|
236
|
-
Provides to UI/Processors:
|
|
237
|
-
orderId (link to stream)
|
|
238
|
-
paymentMethod (user selected method)
|
|
239
|
-
confirmedAt (timestamp)
|
|
240
|
-
paymentId (payment system reference)
|
|
241
|
-
|
|
242
|
-
Event: PaymentAuthorized
|
|
243
|
-
Provides to UI/Processors:
|
|
244
|
-
orderId (link to stream)
|
|
245
|
-
paymentId
|
|
246
|
-
authCode
|
|
247
|
-
authorizedAt (timestamp)
|
|
248
|
-
amount (verified amount)
|
|
249
|
-
|
|
250
|
-
Event: OrderShipped
|
|
251
|
-
Provides to UI/Processors:
|
|
252
|
-
orderId (link to stream)
|
|
253
|
-
shipmentId
|
|
254
|
-
shippedAt (timestamp)
|
|
255
|
-
carrier (shipping company)
|
|
256
|
-
trackingNumber (for delivery tracking)
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
### 4. Create Output Catalog
|
|
260
|
-
List all read models:
|
|
261
|
-
|
|
262
|
-
```
|
|
263
|
-
ReadModel Catalog: Order System
|
|
264
|
-
|
|
265
|
-
1. OrderStatusReadModel
|
|
266
|
-
Purpose: UI shows current order status
|
|
267
|
-
Events: OrderCreated, OrderConfirmed, PaymentAuthorized, OrderShipped, OrderCancelled
|
|
268
|
-
Data: orderId, status, createdAt, confirmedAt, paymentId, shipmentId
|
|
269
|
-
Consumed by:
|
|
270
|
-
- Order Status screen (UI)
|
|
271
|
-
- Customer Dashboard (UI)
|
|
272
|
-
- Order Processing Processor (decides if can ship)
|
|
273
|
-
|
|
274
|
-
2. OrderListReadModel
|
|
275
|
-
Purpose: UI lists all orders for a customer
|
|
276
|
-
Events: OrderCreated, OrderConfirmed, OrderCancelled
|
|
277
|
-
Data: orderId, customerId, total, status, createdAt
|
|
278
|
-
Consumed by:
|
|
279
|
-
- Customer Order History (UI)
|
|
280
|
-
- Order Search/Filter (UI)
|
|
281
|
-
|
|
282
|
-
3. PaymentStatusReadModel
|
|
283
|
-
Purpose: UI shows payment status
|
|
284
|
-
Events: OrderConfirmed, PaymentAuthorized, PaymentFailed
|
|
285
|
-
Data: orderId, paymentId, status, authCode, failureReason, timestamp
|
|
286
|
-
Consumed by:
|
|
287
|
-
- Payment Status screen (UI)
|
|
288
|
-
- Accounting Processor (reconciliation)
|
|
289
|
-
|
|
290
|
-
4. ShipmentTrackingReadModel
|
|
291
|
-
Purpose: UI shows tracking information
|
|
292
|
-
Events: OrderShipped, DeliveryConfirmed
|
|
293
|
-
Data: orderId, shipmentId, trackingNumber, carrier, shippedAt, estimatedDelivery
|
|
294
|
-
Consumed by:
|
|
295
|
-
- Order Tracking screen (UI)
|
|
296
|
-
- Customer notifications (Processor)
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
### 5. Identify Missing Data
|
|
300
|
-
Check if all UI needs are covered:
|
|
301
|
-
|
|
302
|
-
```
|
|
303
|
-
Question: What if UI needs "estimated delivery date"?
|
|
304
|
-
Event: OrderShipped has carrier + trackingNumber
|
|
305
|
-
Action needed: Add estimatedDelivery to OrderShipped event
|
|
306
|
-
(or compute from carrier info)
|
|
307
|
-
|
|
308
|
-
Question: What if UI needs to show "payment method" on status?
|
|
309
|
-
Event: OrderConfirmed has paymentMethod
|
|
310
|
-
Action needed: Include paymentMethod in relevant read models
|
|
311
|
-
|
|
312
|
-
Question: What if UI needs "item descriptions"?
|
|
313
|
-
Event: OrderCreated has items[]
|
|
314
|
-
But: items[] only has productId
|
|
315
|
-
Action needed: Enrich with product descriptions from catalog
|
|
316
|
-
(via join with product service)
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
### 6. Processor Outputs
|
|
320
|
-
Identify what processors consume:
|
|
321
|
-
|
|
322
|
-
```
|
|
323
|
-
Processor: Inventory System
|
|
324
|
-
Consumes from read models:
|
|
325
|
-
- Orders in "PaymentAuthorized" status
|
|
326
|
-
- Items and quantities needed
|
|
327
|
-
Produces commands:
|
|
328
|
-
- ReserveInventory
|
|
329
|
-
|
|
330
|
-
Processor: Fulfillment System
|
|
331
|
-
Consumes from read models:
|
|
332
|
-
- Orders in "InventoryReserved" status
|
|
333
|
-
- Items and quantities
|
|
334
|
-
- Shipping address
|
|
335
|
-
Produces commands:
|
|
336
|
-
- CreateShipment
|
|
337
|
-
|
|
338
|
-
Processor: Notification System
|
|
339
|
-
Consumes from read models:
|
|
340
|
-
- OrderCreated (sends confirmation)
|
|
341
|
-
- OrderConfirmed (sends receipt)
|
|
342
|
-
- OrderShipped (sends tracking)
|
|
343
|
-
- DeliveryConfirmed (sends thank you)
|
|
344
|
-
Does not produce commands (info-only)
|
|
345
|
-
```
|
|
177
|
+
Given commands and events, identify all outputs: for each screen, trace which events its data comes from; define a read model per component; document the event→data mapping; catalog the resulting read models; check for UI needs with no event source; and identify what processors consume. A full worked example of this reasoning (Order/Payment domain) is in `references/examples.md`.
|
|
346
178
|
|
|
347
179
|
## Output Format
|
|
348
180
|
|
|
349
|
-
Instead of writing a markdown document, **place each READMODEL (and any missing AUTOMATION) on the board** using the `node:created` API. Screens are typically already placed from Step 3 (storyboarding) as one plain screen per screen state — Step 3 does not pre-split anything, so working out how many components a screen actually has is this step's job (Step 5a below), not something to re-derive from storyboarding's output. Do not re-place an existing screen unless one is clearly missing. Automations are placed here
|
|
181
|
+
Instead of writing a markdown document, **place each READMODEL (and any missing AUTOMATION) on the board** using the `node:created` API. Screens are typically already placed from Step 3 (storyboarding) as one plain screen per screen state — Step 3 does not pre-split anything, so working out how many components a screen actually has is this step's job (Step 5a below), not something to re-derive from storyboarding's output. Do not re-place an existing screen unless one is clearly missing. Automations are placed here only in the rare case where analysis reveals a processor that reads state and issues commands but was missed entirely in Step 4 — if that happens, immediately apply `eventmodeling-designing-automation-chains`'s rules to it (todo-list read model, translation chain if externally triggered) before continuing, rather than leaving it for Step 5i's defensive re-check to catch.
|
|
350
182
|
|
|
351
|
-
This step proceeds as a sequence of lettered sub-steps: identify each screen's components (5a),
|
|
183
|
+
This step proceeds as a sequence of lettered sub-steps: identify each screen's components (5a), break multi-component screens apart into copies (5c), then design and place one read model per component (5d onward). Every automation's todo-list read model and any translation chain is designed in **Step 4b** (`eventmodeling-designing-automation-chains`), immediately after Step 4 — not here. This step only ever designs screen-facing read models; Step 5i below re-checks automations defensively, not as this step's primary job.
|
|
352
184
|
|
|
353
185
|
> **CRITICAL: Every READMODEL node MUST include `meta.fields` with a `mapping` on every field.** A read model without fields — or with fields that lack `mapping` — has no data lineage and cannot be traced back to its source events.
|
|
354
186
|
|
|
@@ -372,7 +204,7 @@ Automations follow:
|
|
|
372
204
|
READ MODEL → AUTOMATION → COMMAND → EVENT
|
|
373
205
|
```
|
|
374
206
|
|
|
375
|
-
Treat any screen or automation without an incoming read model as a gap. A screen may be exempt if it provably needs no prior state at all (e.g., a blank registration form) — an **automation is never exempt**: every automation gets a todo-list read model, even a trivial one that opens and closes within the same slice (
|
|
207
|
+
Treat any screen or automation without an incoming read model as a gap. A screen may be exempt if it provably needs no prior state at all (e.g., a blank registration form) — an **automation is never exempt**: every automation gets a todo-list read model, even a trivial one that opens and closes within the same slice. For automations, that read model was already designed in **Step 4b** (`eventmodeling-designing-automation-chains`); this step's job is limited to the defensive re-check in Step 5i below.
|
|
376
208
|
|
|
377
209
|
### Step 5a — Enumerate consumers and identify components
|
|
378
210
|
|
|
@@ -380,7 +212,7 @@ Treat any screen or automation without an incoming read model as a gap. A screen
|
|
|
380
212
|
|
|
381
213
|
Read models exist to serve the elements already on the board:
|
|
382
214
|
- Every **view screen** (output/read model screen) needs at least one read model to supply its data.
|
|
383
|
-
- Every **automation** needs at least one read model to read from — its todo list. This applies to every automation without exception, not only ones that visibly "decide" something;
|
|
215
|
+
- Every **automation** needs at least one read model to read from — its todo list. This applies to every automation without exception, not only ones that visibly "decide" something; this was already handled in Step 4b (`eventmodeling-designing-automation-chains`) for every automation placed in Step 4.
|
|
384
216
|
- Every **command/input screen** needs a read model unless it is a blank creation form with no prior state to display (this is the rare exception, not the rule).
|
|
385
217
|
|
|
386
218
|
For each SCREEN node, look at its rendered layout and its `meta.fields` and identify its components — groups of fields/UI elements a user would perceive as one area: a stats tile, a list below it, a summary card, a detail panel, a status column in a table, etc. **Most screens genuinely have exactly one component — do not force a split.** A screen has more than one component only when a user would point at two separate areas and describe them as different things.
|
|
@@ -391,42 +223,9 @@ Once a component's fields are genuinely homogeneous (every field needs the same
|
|
|
391
223
|
|
|
392
224
|
After this step is done, **every SCREEN and every AUTOMATION on the board must be connected to at least one read model** via a `READMODEL → SCREEN` or `READMODEL → AUTOMATION` connection, and every screen identified above as having 2+ components must have been broken apart per Step 5c before any read model is placed. If a screen or automation has no incoming read model connection, it is a gap — either a read model is missing or the connection arrow is missing.
|
|
393
225
|
|
|
394
|
-
> **Placement rule**: A read model must be placed
|
|
395
|
-
|
|
396
|
-
### Step 5b — Identify todo-list read models for automations
|
|
397
|
-
|
|
398
|
-
**Every AUTOMATION needs a todo-list read model — this is not optional, and there is no "pure relay" exemption.** An automation is a processor: it reacts to events, decides what work is outstanding, and issues commands to get that work done. The read model that tells it what's outstanding is its **todo list** — a queue of pending work items, not a snapshot of current entity state.
|
|
399
|
-
|
|
400
|
-
**The pattern**: one or more events *open* an item on the todo list (something now needs doing); one or more events *close* it (the work is done — remove the item). A todo list can be opened and closed by more than one event type on either side, and the set of opening events doesn't need to match the set of closing events in count or shape — whatever the domain calls for.
|
|
401
|
-
|
|
402
|
-
**Worked example**: an automation that reacts to `CustomerRegistered` by sending a welcome notification.
|
|
403
|
-
- Todo list: **NotificationsToSend** — one row per pending notification.
|
|
404
|
-
- `CustomerRegistered` **opens** a row (a notification now needs sending).
|
|
405
|
-
- `NotificationSent` (the event this automation's own resulting command produces) **closes** that row (removes it — the list only ever shows outstanding work).
|
|
406
|
-
- The automation (`Send Welcome Notification`) reads `NotificationsToSend`, and for every open row issues `SendNotification`.
|
|
226
|
+
> **Placement rule**: A read model must be placed immediately upstream of the SCREEN or AUTOMATION it serves — sharing that column when possible (a SCREEN with a free interaction row), or one column to the left when not (any AUTOMATION; a SCREEN whose column is already occupied). Do not place a read model with no screen or automation in the very next column — doing so creates an orphaned read model that will never have a consumer.
|
|
407
227
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
**There is no such thing as an invisible or informal "signal" — a trigger is always a real EVENT node, placed in a second swimlane when it belongs to another system.**
|
|
411
|
-
|
|
412
|
-
**An automation can only ever be directly triggered by an internal event — never by another system's event.** A "trigger" arriving from a second swimlane is not itself the thing that drives your domain's work; it first has to be *translated* into an internal event. Do not model this as one automation whose todo list is opened by the external EVENT and that also does the real work (e.g. an automation reading a todo list opened by `ReservationRequested` from another system's swimlane and directly issuing `ReserveCopy`) — that lets an external system trigger domain work with no translation step, which this model doesn't allow. **Apply this now, in Step 5** — Conway's Law (Step 6) only confirms the boundary, it doesn't introduce the chain. When an integration trigger comes from another team's system, model it as **two chained automations**, never one:
|
|
413
|
-
|
|
414
|
-
1. **Translation automation** — converts the external fact into an internal one; the other system's own decision logic is out of scope. Its todo list is opened by the external EVENT and closed by the internal EVENT its own command produces — the one and only place an external EVENT may open a todo list.
|
|
415
|
-
- **Three separate columns**, left to right: `[external EVENT] → [todo-list READMODEL] → [AUTOMATION + COMMAND + internal EVENT]`. Never crammed into one or two — the "one EVENT per column" rule applies here too.
|
|
416
|
-
- **Name the internal EVENT for its business meaning, not the transport** — usually the same name as the external EVENT (e.g. external `CopyReserved` → internal `CopyReserved`; the swimlane already shows which is which), never a mechanical `<X>SignalReceived`/`<X>RequestReceived` suffix. Same for the automation/command: `Record Reservation`/`RecordReservation`, not `Record Reservation Signal`/`RecordReservationSignal`.
|
|
417
|
-
- Its command and event carry no business decision — they only exist to produce the internal fact the next automation needs.
|
|
418
|
-
2. **Worker automation** — the one that does the actual work (the domain reaction the process is really about, e.g. `ReserveCopy`). Its todo list is opened **only** by the internal EVENT the translation automation produced (this chapter's own swimlane) — never by the external EVENT directly — and closed by whatever event marks that work done.
|
|
419
|
-
|
|
420
|
-
Wire the todo lists the standard way for each automation separately: for the translation automation, the external EVENT (second swimlane) **opens** the row and its own resulting internal EVENT **closes** it; for the worker automation, that same internal EVENT **opens** its row and its own resulting EVENT **closes** it. `EVENT → READMODEL` connections from both swimlanes are unaffected by which swimlane the event sits in.
|
|
421
|
-
|
|
422
|
-
**Fields**: a todo-list read model's fields describe the pending item — the identity it's about (e.g. `customerId`) plus enough context to act on it (e.g. `email`, `notificationType`). Do not add a `status` field to mark items done — a todo list's "open" state is *membership in the list itself* (the row exists at all), not a status flag on a row that never leaves. If the same underlying data is also useful with an explicit status column for a different consumer, that is a different read model, not this one.
|
|
423
|
-
|
|
424
|
-
**Connections and placement**: the todo-list read model goes in the interaction lane, same column as its automation (actor lane) — the same `READMODEL → AUTOMATION` placement covered in Step 5f below.
|
|
425
|
-
- Connect READMODEL → AUTOMATION **first** (the automation reads its own todo list) — the closing connection below is only accepted once this edge exists.
|
|
426
|
-
- Connect every **opening** EVENT → READMODEL.
|
|
427
|
-
- Connect every **closing** EVENT → READMODEL — including the automation's own resulting event, even though that event is produced by the command this same automation issues. This is not a backward arrow: `EVENT → READMODEL` connections are exempt from column ordering when the read model already feeds an AUTOMATION (see `learn-eventmodelers-api` §3) — this todo-list read model qualifies because of the `READMODEL → AUTOMATION` edge above. A read model in this shape is a live projection, not a frozen snapshot — a later event closing an earlier-opened item is the normal case, not an exception to reach for only when convenient.
|
|
428
|
-
|
|
429
|
-
After this step, every AUTOMATION on the board must have an incoming `READMODEL → AUTOMATION` connection to a todo-list read model — treat a connectionless automation exactly like a connectionless screen with no exemption: a gap to fix, not something to note and move past.
|
|
228
|
+
> **Automation todo-list read models are designed in Step 4b, not here.** The full pattern — every automation's todo-list read model, the "no invisible signal" rule, and the two-chained-automation translation requirement for externally-triggered automations — now lives in `eventmodeling-designing-automation-chains` (Step 4b), which runs immediately after Step 4, before this step. If Step 4b ran, every automation already on the board has its todo-list read model wired; Step 5i below only re-checks this defensively. The rare exception is an automation discovered only now, during output analysis (see "Output Format" above) — if that happens, apply `eventmodeling-designing-automation-chains`'s rules to it directly rather than re-deriving them here.
|
|
430
229
|
|
|
431
230
|
### Step 5c — Break apart multi-component screens into copies
|
|
432
231
|
|
|
@@ -435,7 +234,7 @@ After this step, every AUTOMATION on the board must have an incoming `READMODEL
|
|
|
435
234
|
- Each copy is the **same page**, so it **keeps the same screen name/title** — do not rename copies after their component (e.g. don't title one copy `"Librarian Dashboard — Statistics"` and another `"Librarian Dashboard — Recently Added"`; both stay `"Librarian Dashboard"`).
|
|
436
235
|
- Distinguish copies visually, not by name: **mark/highlight the one component each copy is about**, using the `html-screen` skill's native Marks feature — see that skill's "Marks" section for the full mechanism. In short: the component of interest gets `data-em-mark-id`/`em-mark em-mark-<colorhex>` baked onto it in the page HTML, paired with a matching `meta.marks` entry (`{id, color, pageIndex, blurOutside: true}`) that blurs every other top-level section. Never hand-roll this with inline `filter`/`outline`/`opacity` CSS — that does not match how the app itself renders a mark.
|
|
437
236
|
- Use the `html-screen` skill to produce each copy: pass it the original screen's markup, explicitly asking it to mark/highlight the one component to keep crisp and blur the rest (this satisfies `html-screen`'s "only when the user explicitly asks for one" condition for its Marks feature — the ask comes from this step). This is the skill's job — don't hand-roll the markup here.
|
|
438
|
-
- Place each copy in its own column,
|
|
237
|
+
- Place each copy in its own column, the **same column** as the read model that will feed it (insert a new column immediately before if that copy's column is already occupied) — this is normally a different column per copy, since each component typically has a different natural source event.
|
|
439
238
|
|
|
440
239
|
Why this matters, beyond tidiness:
|
|
441
240
|
- **It prevents backward arrows.** A single screen-wide read model is forced to aggregate from whatever events each of its components needs, which often means reaching back across many columns to events scattered throughout the timeline — and the read model can only sit in one column, so some of those connections end up spanning a wide gap or, worse, pushing the read model's column later than some of its screen's other consumers require. Splitting by component lets each narrower read model sit close to its own natural source event(s), keeping every `EVENT → READMODEL` arrow short and forward.
|
|
@@ -489,9 +288,9 @@ Every field must also set `"cardinality"` — use `"Single"` unless the field ge
|
|
|
489
288
|
}
|
|
490
289
|
```
|
|
491
290
|
|
|
492
|
-
Read models go in the `interaction` lane
|
|
291
|
+
Read models go in the `interaction` lane. For a **SCREEN**, the primary read model shares the SCREEN's column (READMODEL in interaction row, SCREEN in actor row of the same column) — unless that column's interaction row is already occupied (e.g. a command/input screen, where the COMMAND already sits there), in which case the read model goes one column to the left instead. For an **AUTOMATION**, the read model always goes one column to the left, never the same column — an automation's own column already holds the COMMAND it issues. If a consumer needs more than one read model, only the primary one gets this placement; every additional read model goes further left still.
|
|
493
292
|
|
|
494
|
-
> **Timeline alignment rule**: Place the read model in the same column as its consumer screen
|
|
293
|
+
> **Timeline alignment rule**: Place the read model in the same column as its consumer screen when that column is free, or one column before its consumer (screen or automation) when it isn't. Insert the new column immediately **before** the consumer's column (`{"index": consumerColumnIndex}`, shifting the consumer right) rather than after — the read model must sit upstream of (to the left of) the element it feeds. Do not append read model columns to the end of the timeline — doing so severs the visual left→right flow from data projection to UI consumption.
|
|
495
294
|
|
|
496
295
|
### Step 5f — Placing READMODEL and AUTOMATION nodes with `cellId` (Mandatory)
|
|
497
296
|
|
|
@@ -499,14 +298,16 @@ Read models go in the `interaction` lane — **in the same column as the SCREEN
|
|
|
499
298
|
|
|
500
299
|
**For a READMODEL** (interaction lane):
|
|
501
300
|
|
|
502
|
-
**Prefer MCP** — `place_element` collapses finding/creating the empty interaction cell
|
|
301
|
+
**Prefer MCP** — `place_element` collapses finding/creating the empty interaction cell and creating the node into one call, but it does **not** resolve an occupied target column for you: passing an already-occupied `columnIndex` fails outright rather than auto-inserting a column before it (it only auto-extends when `columnIndex` is *past* the current column count — a different situation). **Target column depends on the consumer type**:
|
|
302
|
+
- **SCREEN**: target the screen's own column index. First check whether that column's interaction row is already occupied (e.g. a command/input screen) — `get_node` with `projection: "cells"` on the chapter. If it is, insert a new column immediately before the screen first (`add_column` with `beforeNodeId` set to the screen's own node id), then pass *that* new column's index to `place_element` instead of the screen's original one.
|
|
303
|
+
- **AUTOMATION**: target `consumerColumnIndex - 1` (one column to the *left* of the automation) — never the automation's own column index, since it already holds the COMMAND the automation issues.
|
|
503
304
|
```
|
|
504
305
|
mcp__eventmodelers__place_element {
|
|
505
306
|
"boardId": "<BOARD_ID>",
|
|
506
307
|
"timelineId": "<CHAPTER_ID>",
|
|
507
308
|
"elementType": "READMODEL",
|
|
508
309
|
"title": "ActiveReservationView",
|
|
509
|
-
"columnIndex": <
|
|
310
|
+
"columnIndex": <consumerScreenColumnIndex> // or <automationColumnIndex - 1> for an AUTOMATION consumer
|
|
510
311
|
}
|
|
511
312
|
```
|
|
512
313
|
Then set `meta.fields` (with `mapping`/`generated`/`cardinality`) on the returned node id:
|
|
@@ -520,45 +321,17 @@ mcp__eventmodelers__submit_node_events {
|
|
|
520
321
|
}]
|
|
521
322
|
}
|
|
522
323
|
```
|
|
523
|
-
To determine the consumer's column index beforehand, or to check whether a specific interaction cell is already occupied (there is no `cellId` filter on `get_nodes` — see the note under "Wire connections" below), fetch the chapter and read its cell map:
|
|
324
|
+
To determine the consumer's column index beforehand, or to check whether a specific interaction cell is already occupied (there is no `cellId` filter on `get_nodes` — see the note under "Wire connections" below), fetch the chapter and read its cell map — `projection: "cells"` returns just `{rows, columns, cells}`, not the whole chapter node:
|
|
524
325
|
```
|
|
525
|
-
mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<CHAPTER_ID>" }
|
|
526
|
-
# →
|
|
326
|
+
mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<CHAPTER_ID>", "projection": "cells" }
|
|
327
|
+
# → rows (find "interaction"/"actor" rows) and cells (sparse; absent id = empty)
|
|
527
328
|
```
|
|
528
329
|
|
|
529
|
-
**Fallback (no MCP)
|
|
330
|
+
**Fallback (no MCP):** see `references/api-fallback.md` — "Step 5f — Placing a READMODEL node (full manual sequence)".
|
|
530
331
|
|
|
531
|
-
|
|
532
|
-
```bash
|
|
533
|
-
curl -s -H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" \
|
|
534
|
-
"$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID"
|
|
535
|
-
# → timelineData.rows — find the row where type === "interaction"
|
|
536
|
-
```
|
|
537
|
-
2. Check if the interaction cell is already occupied (existing COMMAND):
|
|
538
|
-
```bash
|
|
539
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?cellId=<interactionRowId>-<columnId>" \
|
|
540
|
-
-H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID"
|
|
541
|
-
```
|
|
542
|
-
If a COMMAND occupies that cell, insert a new column immediately after (`{"index": currentIndex + 1}`) and use that column's ID instead.
|
|
543
|
-
3. `cellId = interactionRow.id + "-" + columnId`
|
|
544
|
-
4. Create the READMODEL:
|
|
545
|
-
```bash
|
|
546
|
-
curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/events" \
|
|
547
|
-
-H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" -H "x-user-id: identifying-outputs" \
|
|
548
|
-
-H "Content-Type: application/json" \
|
|
549
|
-
-d '[{
|
|
550
|
-
"id": "<event-uuid>",
|
|
551
|
-
"eventType": "node:created",
|
|
552
|
-
"nodeId": "<node-uuid>",
|
|
553
|
-
"boardId": "<BOARD_ID>",
|
|
554
|
-
"timestamp": 1234567890,
|
|
555
|
-
"chapterId": "<CHAPTER_ID>",
|
|
556
|
-
"cellId": "<interactionRowId>-<columnId>",
|
|
557
|
-
"meta": {"type": "READMODEL", "title": "ActiveReservationView", "fields": [...]}
|
|
558
|
-
}]'
|
|
559
|
-
```
|
|
332
|
+
**Pass `autoConnect: false` on every `place_element` / `node:created` call in this step.** Output read-model columns are inserted right next to automation-chain and command columns whose events are *not* the read model's sources — the default auto-connect would wire the new READMODEL to the nearest event on its left, which is exactly the stray-edge cleanup this step has repeatedly generated. Suppress it here and wire every `EVENT → READMODEL` / `READMODEL → SCREEN` / `READMODEL → AUTOMATION` edge explicitly in Step 5h's `set_connections` batch. After the batch, run `validate_model` (`{boardId, chapterId}`) — its `backward-arrows` and `readmodel-sources` findings confirm the wiring is what you intended and no stray edge slipped in.
|
|
560
333
|
|
|
561
|
-
**For an AUTOMATION** (actor lane
|
|
334
|
+
**For an AUTOMATION** (actor lane) — its READMODEL always goes **one column to its left**, never the same column: the automation's own column already holds the COMMAND it issues (interaction row), so the read model can't also live there.
|
|
562
335
|
|
|
563
336
|
**Prefer MCP** — same `place_element` call with `"elementType": "AUTOMATION"`, then `submit_node_events` for fields, as above.
|
|
564
337
|
|
|
@@ -571,27 +344,26 @@ mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<CHAPTER_ID>"
|
|
|
571
344
|
|
|
572
345
|
### Step 5g — Preventing backward arrows (mandatory pre-placement check)
|
|
573
346
|
|
|
574
|
-
The timeline must always progress left-to-right. A `READMODEL → SCREEN` connection going right-to-left is a layout error.
|
|
347
|
+
The timeline must always progress left-to-right, or downward within the same column. A `READMODEL → SCREEN` connection going right-to-left is a layout error.
|
|
575
348
|
|
|
576
|
-
The correct layout is: **READMODEL
|
|
349
|
+
The correct layout is: **READMODEL and its (primary) view SCREEN share the same column** — READMODEL in the interaction row, SCREEN in the actor row, a downward connection. The read model only sits in a separate column, immediately **before** the screen's, when the screen's column is already unavailable to it (its interaction row is occupied by something else, e.g. a COMMAND on a command/input screen). If a screen displays more than one read model, only the primary read model shares the screen's column — every additional read model goes further left, never to the right. Before placing each read model, find the view screen it serves and verify the column order:
|
|
577
350
|
|
|
578
351
|
```
|
|
579
|
-
For each view screen S that queries this read model:
|
|
580
|
-
If column(S)
|
|
581
|
-
→
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
→ Gap between read model and screen. Move the read model to column(S) - 1, or move the screen to column(READMODEL) + 1.
|
|
352
|
+
For each view screen S that queries this read model as its primary read model:
|
|
353
|
+
If column(S)'s interaction row is free:
|
|
354
|
+
→ Place the READMODEL in column(S) itself (same column as S). No adjustment needed.
|
|
355
|
+
If column(S)'s interaction row is already occupied:
|
|
356
|
+
→ Insert a new column immediately before column(S) and place the READMODEL there instead.
|
|
357
|
+
mcp__eventmodelers__add_column { "boardId": "<BOARD_ID>", "timelineId": "<TL>", "beforeNodeId": "<S's node id>" }
|
|
358
|
+
(fallback, no MCP: POST /timelines/:tl/columns {"index": N} with N computed by hand),
|
|
359
|
+
then node:changed to update any node whose cell needs to move.
|
|
360
|
+
If the read model ends up more than one column away from S with nothing in between:
|
|
361
|
+
→ Gap between read model and screen. Close it: move the read model into column(S) (if free) or column(S) - 1.
|
|
590
362
|
```
|
|
591
363
|
|
|
592
|
-
**View screens
|
|
364
|
+
**View screens normally share the column of the (primary) read model they display** — either because they were placed there in Step 3, or because the read model was placed into the screen's own column just now (per the check above). A screen's own position is never moved to resolve a read model placement conflict — when the shared column isn't available, the **read model** gets a new column immediately before the screen's, not the other way around (this preserves the screen's narrative order from Step 3).
|
|
593
365
|
|
|
594
|
-
**The same rule applies to `EVENT → READMODEL`.** If a later event needs to update data a read model already feeds to a SCREEN, do not connect that later event back into the existing read model — the platform only accepts an `EVENT → READMODEL` backward connection when the read model already has a `READMODEL → AUTOMATION` edge (the todo-list pattern from Step
|
|
366
|
+
**The same rule applies to `EVENT → READMODEL`.** If a later event needs to update data a read model already feeds to a SCREEN, do not connect that later event back into the existing read model — the platform only accepts an `EVENT → READMODEL` backward connection when the read model already has a `READMODEL → AUTOMATION` edge (the todo-list pattern from Step 4b, `eventmodeling-designing-automation-chains`). For any read model feeding a SCREEN, resolve the update the same way Step 5c resolves multi-component screens: place a **new copy of the read model** in (or immediately after) the later event's column, connect the later event forward into that copy, and place a matching copy of the same screen there — same title, updated data, optionally re-marked/highlighted via `html-screen`'s Marks feature. Never link the new copy back to the earlier read model instance.
|
|
595
367
|
|
|
596
368
|
### Step 5h — Wire connections after placing each READMODEL (and its SCREEN)
|
|
597
369
|
|
|
@@ -599,43 +371,26 @@ After `place-element` returns the READMODEL node ID, create the arrows that comp
|
|
|
599
371
|
|
|
600
372
|
1. **EVENT → READMODEL** — find the primary source EVENT node in the swimlane row of the same column.
|
|
601
373
|
|
|
602
|
-
**Prefer MCP** — `get_nodes` has no `cellId` filter (only `type`); look up occupancy via the chapter's cell map instead, then connect with the type-checked edge tool (auto-corrects direction, skips duplicates):
|
|
374
|
+
**Prefer MCP** — `get_nodes` has no `cellId` filter (only `type`); look up occupancy via the chapter's cell map instead (`projection: "cells"`), then connect with the type-checked edge tool (auto-corrects direction, skips duplicates):
|
|
603
375
|
```
|
|
604
|
-
mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<CHAPTER_ID>" }
|
|
605
|
-
# → read
|
|
376
|
+
mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<CHAPTER_ID>", "projection": "cells" }
|
|
377
|
+
# → read cells["<swimlaneRowId>-<columnId>"] for the occupying node id
|
|
606
378
|
mcp__eventmodelers__set_connection { "boardId": "<BOARD_ID>", "source": "<eventNodeId>", "target": "<readmodelNodeId>", "action": "connect" }
|
|
607
379
|
```
|
|
608
380
|
|
|
609
|
-
**Fallback (no MCP):**
|
|
610
|
-
```bash
|
|
611
|
-
curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?cellId=<swimlaneRowId>-<columnId>" \
|
|
612
|
-
-H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" -H "x-user-id: eventmodeling-identifying-outputs"
|
|
613
|
-
```
|
|
614
|
-
Connect it:
|
|
615
|
-
```bash
|
|
616
|
-
curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/connections" \
|
|
617
|
-
-H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" -H "x-user-id: eventmodeling-identifying-outputs" \
|
|
618
|
-
-H "Content-Type: application/json" \
|
|
619
|
-
-d '{"source":"<eventNodeId>","target":"<readmodelNodeId>"}'
|
|
620
|
-
```
|
|
381
|
+
**Fallback (no MCP):** see `references/api-fallback.md` — "Step 5h.1 — Wire EVENT → READMODEL".
|
|
621
382
|
|
|
622
|
-
2. **READMODEL → SCREEN** — connect to the existing SCREEN node in the actor row of the
|
|
383
|
+
2. **READMODEL → SCREEN** — connect to the existing SCREEN node in the actor row of the same column (or the column immediately after, only when that column wasn't available to the read model — screens are typically already placed from Step 3).
|
|
623
384
|
|
|
624
385
|
**Prefer MCP:**
|
|
625
386
|
```
|
|
626
387
|
mcp__eventmodelers__set_connection { "boardId": "<BOARD_ID>", "source": "<readmodelNodeId>", "target": "<screenNodeId>", "action": "connect" }
|
|
627
388
|
```
|
|
628
389
|
|
|
629
|
-
**Fallback (no MCP):**
|
|
630
|
-
```bash
|
|
631
|
-
curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/connections" \
|
|
632
|
-
-H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" -H "x-user-id: eventmodeling-identifying-outputs" \
|
|
633
|
-
-H "Content-Type: application/json" \
|
|
634
|
-
-d '{"source":"<readmodelNodeId>","target":"<screenNodeId>"}'
|
|
635
|
-
```
|
|
390
|
+
**Fallback (no MCP):** see `references/api-fallback.md` — "Step 5h.2 — Wire READMODEL → SCREEN".
|
|
636
391
|
|
|
637
392
|
3. **READMODEL → AUTOMATION** — if the read model is consumed by an automatic process (scheduler, background job, external trigger), place the AUTOMATION node and connect it:
|
|
638
|
-
- Place the AUTOMATION in the automation lane, in the column immediately to the right of its read model
|
|
393
|
+
- Place the AUTOMATION in the automation lane, in the column immediately to the right of its read model — unlike a view screen, this is unconditional for automations: the automation's own column always holds the COMMAND it issues, so its read model never shares that column.
|
|
639
394
|
- Then connect:
|
|
640
395
|
|
|
641
396
|
**Prefer MCP:**
|
|
@@ -643,16 +398,12 @@ After `place-element` returns the READMODEL node ID, create the arrows that comp
|
|
|
643
398
|
mcp__eventmodelers__set_connection { "boardId": "<BOARD_ID>", "source": "<readmodelNodeId>", "target": "<automationNodeId>", "action": "connect" }
|
|
644
399
|
```
|
|
645
400
|
|
|
646
|
-
**Fallback (no MCP):**
|
|
647
|
-
```bash
|
|
648
|
-
curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/connections" \
|
|
649
|
-
-H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" -H "x-user-id: eventmodeling-identifying-outputs" \
|
|
650
|
-
-H "Content-Type: application/json" \
|
|
651
|
-
-d '{"source":"<readmodelNodeId>","target":"<automationNodeId>"}'
|
|
652
|
-
```
|
|
401
|
+
**Fallback (no MCP):** see `references/api-fallback.md` — "Step 5h.3 — Wire READMODEL → AUTOMATION".
|
|
653
402
|
|
|
654
403
|
Skip a connection silently if the target cell is empty. Log each created arrow: `→ connected EVENT→READMODEL "OrderPlaced"→"OrderStatusView"`, `→ connected READMODEL→SCREEN "OrderStatusView"→"Order Status Screen"`, or `→ connected READMODEL→AUTOMATION "OrderStatusView"→"Fulfillment Processor"`.
|
|
655
404
|
|
|
405
|
+
If this step is processing more than one read model in the same pass, collect every connection resolved above (across all of them) into one `set_connections` call instead of one `set_connection` per pair — this was the single largest source of individual tool calls in this step. Pass `compact: true` on that call: with `autoConnect: false` set at placement, every edge here is one you're deliberately creating, so a `{connected, existed, removed, notFound, failed, errors}` tally is all you need back — not a row per edge.
|
|
406
|
+
|
|
656
407
|
4. **Document the reasoning for each connected event** — for every EVENT → READMODEL edge wired in step 1 (including any added later, e.g. via Step 5g's backward-connection exemption or a Step 5c/copy pattern), record why that event feeds this read model: which field(s) it sets or updates, and why. Use one MARKDOWN note per read model, in that read model's own column (same feedback-lane + MARKDOWN mechanics as `eventmodeling-orchestrating-event-modeling`'s "Documenting decisions inline, at any step" / Step 11 — add the chapter's feedback lane first if it doesn't already exist, then place the note at `cellId = "<feedbackLaneId>-<readModelColumnId>"`). Extend the existing note (don't create a second one) when the read model later gains another connected event.
|
|
657
408
|
|
|
658
409
|
Example body:
|
|
@@ -670,133 +421,33 @@ Do not declare Step 5 complete on the strength of the read models you happened t
|
|
|
670
421
|
|
|
671
422
|
1. Does it now have an incoming `READMODEL → SCREEN` or `READMODEL → AUTOMATION` connection?
|
|
672
423
|
2. If it's a SCREEN and not connected — is it a provably blank creation form with no prior state? State the reason in one line (e.g. `"Register Account" screen: blank form, no prior state — exempt`). This exemption applies to screens only.
|
|
673
|
-
3. If it's an AUTOMATION and not connected, it is **never** exempt —
|
|
424
|
+
3. If it's an AUTOMATION and not connected, it is **never** exempt — this should already be resolved by Step 4b (`eventmodeling-designing-automation-chains`); if it isn't, apply that skill's rules now to identify its todo-list read model.
|
|
674
425
|
4. If a SCREEN is neither connected nor exempt, it is an **unresolved gap**. Fix it now: design the missing read model (pulling from its `meta.fields`/`mapping` as above) and wire the connection. Do not move to Step 6 with an unresolved gap silently carried forward — either fix it or explicitly flag it to the user as accepted debt.
|
|
675
426
|
5. Does any screen still carry more than one component undivided (a Step 5a/5c miss)? If so, break it apart now per Step 5c before counting it as resolved.
|
|
676
427
|
6. **Re-fetch every READMODEL too** and run the >3-events heuristic (`eventmodeling-orchestrating-event-modeling`) on each one field by field — including read models a MARKDOWN note already justified as a roll-up. A prior note documents one field's irreducible fan-in; it does not exempt the rest of that node's fields from this check. The failure mode this catches: a wide-fan-in field (e.g. live per-copy availability) bundled together with a cheap, low-fan-in identity/fact field (e.g. a title set by 1-2 events) that has nothing to do with the roll-up — that pairing is always two read models, never one, no matter how the note reads.
|
|
677
428
|
7. **Every READMODEL has its event-reasoning MARKDOWN note (Step 5h.4)**, and that note accounts for *every* inbound `EVENT → READMODEL` edge on the node — not just the one from when it was first placed. If a read model gained a connected event later and the note wasn't extended, fix it now rather than carrying the gap forward.
|
|
678
429
|
|
|
430
|
+
Run `validate_model` (`{boardId, chapterId}`) once as the mechanical half of this pass — its `readmodel-sources` findings list every READMODEL with no inbound EVENT, `screen-collisions` flags any column that ended up with two screens, and `backward-arrows` catches a `READMODEL → SCREEN` that points left. Then do the judgement checks above (blank-form exemptions, the >3-events heuristic, note completeness) that the tool can't make.
|
|
431
|
+
|
|
679
432
|
List the result of this pass (connected / exempt / fixed) for every screen and automation checked — this list is the evidence the orchestrator's Step 5 gate ("every screen data need is satisfied by a read model") actually holds, not just an assumption.
|
|
680
433
|
|
|
681
434
|
After all read models, screens, automations, and connections are in place, present the Read Model Catalog summary as text to the user.
|
|
682
435
|
|
|
683
436
|
---
|
|
684
437
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
```markdown
|
|
688
|
-
# Outputs: [Domain Name]
|
|
689
|
-
|
|
690
|
-
## Read Models Summary
|
|
691
|
-
|
|
692
|
-
| ReadModel | Purpose | Events | Consumed By |
|
|
693
|
-
|-----------|---------|--------|-------------|
|
|
694
|
-
| OrderStatus | Show order state | OrderCreated, OrderConfirmed | UI, Processor |
|
|
695
|
-
| OrderList | List orders | OrderCreated, OrderCancelled | UI |
|
|
696
|
-
| PaymentStatus | Payment info | OrderConfirmed, PaymentAuthorized | UI, Accounting |
|
|
697
|
-
| Shipment Tracking | Track delivery | OrderShipped, DeliveryConfirmed | UI, Notifications |
|
|
698
|
-
|
|
699
|
-
---
|
|
700
|
-
|
|
701
|
-
## Detailed Read Models
|
|
702
|
-
|
|
703
|
-
### ReadModel: OrderStatusView
|
|
704
|
-
|
|
705
|
-
**Purpose**: Order Status screen displays current order state
|
|
706
|
-
|
|
707
|
-
**Events subscribed**:
|
|
708
|
-
- OrderCreated
|
|
709
|
-
- OrderConfirmed
|
|
710
|
-
- PaymentAuthorized
|
|
711
|
-
- OrderShipped
|
|
712
|
-
- OrderCancelled
|
|
713
|
-
- DeliveryConfirmed
|
|
714
|
-
|
|
715
|
-
**Data**:
|
|
716
|
-
```
|
|
717
|
-
{
|
|
718
|
-
orderId: string
|
|
719
|
-
customerId: string
|
|
720
|
-
status: 'Draft' | 'Confirmed' | 'Authorized' | 'Shipped' | 'Delivered' | 'Cancelled'
|
|
721
|
-
items: Array<{productId, quantity, unitPrice}>
|
|
722
|
-
total: number
|
|
723
|
-
shippingAddress: Address
|
|
724
|
-
|
|
725
|
-
createdAt: Date
|
|
726
|
-
confirmedAt: Date
|
|
727
|
-
paymentId: string
|
|
728
|
-
paymentMethod: 'card' | 'transfer'
|
|
729
|
-
authorizedAt: Date
|
|
730
|
-
|
|
731
|
-
shipmentId: string
|
|
732
|
-
carrier: string
|
|
733
|
-
trackingNumber: string
|
|
734
|
-
shippedAt: Date
|
|
735
|
-
estimatedDelivery: Date
|
|
736
|
-
}
|
|
737
|
-
```
|
|
738
|
-
|
|
739
|
-
**Update Logic**:
|
|
740
|
-
- OrderCreated: Insert with status='Draft'
|
|
741
|
-
- OrderConfirmed: Update status='Confirmed'
|
|
742
|
-
- PaymentAuthorized: Update status='Authorized', set paymentId
|
|
743
|
-
- OrderShipped: Update status='Shipped', set shipmentId, carrier, trackingNumber
|
|
744
|
-
- DeliveryConfirmed: Update status='Delivered'
|
|
745
|
-
- OrderCancelled: Update status='Cancelled'
|
|
746
|
-
|
|
747
|
-
**Consumed By**:
|
|
748
|
-
- Order Status Screen (displays)
|
|
749
|
-
- Order Processing Processor (checks status)
|
|
750
|
-
- Notification System (sends updates)
|
|
751
|
-
|
|
752
|
-
--- [Repeat for each read model]
|
|
753
|
-
|
|
754
|
-
---
|
|
755
|
-
|
|
756
|
-
## Data Completeness Check
|
|
757
|
-
|
|
758
|
-
### Events → UI Needs
|
|
759
|
-
|
|
760
|
-
Verify all UI needs have event sources:
|
|
761
|
-
|
|
762
|
-
| UI Need | Event Source | Status |
|
|
763
|
-
|---------|-------------|--------|
|
|
764
|
-
| Order status | OrderConfirmed, OrderShipped | |
|
|
765
|
-
| Tracking number | OrderShipped | |
|
|
766
|
-
| Order items | OrderCreated | |
|
|
767
|
-
| Estimated delivery | OrderShipped | |
|
|
768
|
-
| Cancellation reason | OrderCancelled | |
|
|
769
|
-
|
|
770
|
-
### Missing Data
|
|
771
|
-
|
|
772
|
-
Identify UI needs without event sources:
|
|
773
|
-
- None identified
|
|
774
|
-
|
|
775
|
-
---
|
|
776
|
-
|
|
777
|
-
## Processor Consumption
|
|
778
|
-
|
|
779
|
-
### Processors and their reads:
|
|
780
|
-
|
|
781
|
-
| Processor | Reads From | Writes Commands |
|
|
782
|
-
|-----------|-----------|-----------------|
|
|
783
|
-
| Inventory | OrderStatusView (Authorized) | ReserveInventory |
|
|
784
|
-
| Fulfillment | OrderStatusView (InventoryReserved) | CreateShipment |
|
|
785
|
-
| Notification | OrderStatusView (all) | None (info-only) |
|
|
786
|
-
| Accounting | PaymentStatusView | None (reporting) |
|
|
787
|
-
```
|
|
438
|
+
Older versions of this skill wrote the read model catalog as a markdown document rather than placing nodes on the board — that legacy template is kept in `references/examples.md` for reference only; it is not the actual output mechanism.
|
|
788
439
|
|
|
789
440
|
## Quality Checklist
|
|
790
441
|
|
|
791
442
|
### Read Model Design
|
|
792
443
|
- [ ] **Typical pattern applied**: most screens follow `READ MODEL → SCREEN → COMMAND → EVENT`
|
|
793
444
|
- [ ] **Every SCREEN from storyboarding is connected to at least one read model** (via `READMODEL → SCREEN`); only blank creation forms may be exempt — verified via the mandatory per-node pass above, not assumed
|
|
794
|
-
- [ ] **Every AUTOMATION from storyboarding is connected to at least one todo-list read model** (via `READMODEL → AUTOMATION`, Step
|
|
795
|
-
- [ ] **No automation's todo list is opened directly by another system's (second-swimlane) EVENT unless that automation is itself the translation automation** (Step
|
|
445
|
+
- [ ] **Every AUTOMATION from storyboarding is connected to at least one todo-list read model** (via `READMODEL → AUTOMATION`, Step 4b) — no exemption for automations, unlike screens; even a simple relay automation gets one
|
|
446
|
+
- [ ] **No automation's todo list is opened directly by another system's (second-swimlane) EVENT unless that automation is itself the translation automation** (Step 4b) — an automation doing the actual domain work is only ever opened by an internal event; a second-swimlane EVENT feeding straight into a work automation's todo list is a missing translation automation
|
|
796
447
|
- [ ] **No read model is placed without a connected SCREEN or AUTOMATION consumer**
|
|
797
448
|
- [ ] **No read model spans more than one component** — a screen with N distinct components gets N read models and N highlighted screen copies (same screen name, one component crisp per copy), not one screen-wide read model
|
|
798
449
|
- [ ] **Every multi-component screen was broken apart in Step 5c** — each copy keeps the original screen's name, differs only in which component is marked/highlighted
|
|
799
|
-
- [ ] **Every automation's todo-list read model identifies its opening and closing events** (Step
|
|
450
|
+
- [ ] **Every automation's todo-list read model identifies its opening and closing events** (Step 4b) — including the automation's own resulting event as a closing event where applicable, not just the triggering event
|
|
800
451
|
- [ ] **A field's genuinely irreducible wide fan-in is documented per-field** (inline MARKDOWN note, per "Documenting decisions inline") — and that note is never treated as clearing every other field on the same read model from the >3-events check; a cheap identity/fact field bundled alongside a wide roll-up field is always split out, never excused by the roll-up's own note
|
|
801
452
|
- [ ] **Every read model has an event-reasoning MARKDOWN note** (Step 5h.4) covering every connected event and which field(s) it sets or updates
|
|
802
453
|
- [ ] Every read model has clear purpose
|