@eventmodelers/cli 1.0.39 → 1.0.41
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/stacks/modeling-kit/templates/.claude/skills/eventmodeling-core-rules/SKILL.md +11 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-automation-chains/SKILL.md +5 -2
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md +1 -1
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/SKILL.md +3 -1
package/package.json
CHANGED
|
@@ -69,6 +69,7 @@ Slices are not placed directly — they're **derived** from a completed model. E
|
|
|
69
69
|
- Invalid: `OrderSummaryProjector`, `InvoiceListRepository` — implementation detail leaking into the model
|
|
70
70
|
- Optional — nothing requires a READMODEL to exist for a COMMAND or a downstream consumer.
|
|
71
71
|
- Never drives COMMAND validation directly — a COMMAND is checked against its own documented preconditions, not a read model.
|
|
72
|
+
- **List-shaped read models**: when the whole READMODEL represents multiple rows (each row shaped by its defined fields), not a single projected record, set `meta.listElement: true` on the node itself — this is a node-level flag, distinct from a field's own `cardinality: "List"` (a single field holding multiple values). Every todo-list read model (see `eventmodeling-designing-automation-chains`) is list-shaped and always gets `listElement: true`; the same applies to any other READMODEL whose query naturally returns a set of rows (e.g. `ProductList`, `InvoiceList`). `eventmodeling-elaborating-scenarios` reads this flag to decide whether a scenario needs row-level `examples`/`expectEmptyList` instead of a single accumulated result.
|
|
72
73
|
|
|
73
74
|
### SCREEN
|
|
74
75
|
- Represents what a user sees and can act on.
|
|
@@ -111,6 +112,16 @@ A connection either goes **downward within the same column** (actor → interact
|
|
|
111
112
|
|
|
112
113
|
When an element's natural column is already occupied by something else, insert a new column immediately before or after (whichever keeps every connection forward) rather than wiring across the gap. See `place-element` for the mechanical insertion rules and each step's own placement section for where "before" vs. "after" applies.
|
|
113
114
|
|
|
115
|
+
## Linked Copies
|
|
116
|
+
|
|
117
|
+
A **linked copy** is a COMMAND/EVENT/READMODEL node that mirrors another node elsewhere on the board — most often needed because `set_connection`/auto-connect only ever pairs nodes on the same timeline, so a node on a different timeline can't be wired to directly.
|
|
118
|
+
|
|
119
|
+
To make one: place a normal new node of the same type (COMMAND/EVENT/READMODEL only) at the target spot, then link it to the origin (`link_element`, or the REST `.../nodes/:nodeId/link` fallback). This replaces the new node's `meta` with a full copy of the origin's and sets `meta.linkedTo` to the origin's node id — the authoritative pointer (a `data.linkedTo` also exists but is a rendering mirror only, not the source of truth). Wire the resulting copy to its own neighbors normally afterward.
|
|
120
|
+
|
|
121
|
+
Never delete the original (the node with no `linkedTo`) once copies of it exist — copies reference it, and removing it breaks every copy. `eventmodeling-checking-completeness` already treats any `linkedTo`-marked node as an intentional copy, never a duplicate or missing-slice gap to flag.
|
|
122
|
+
|
|
123
|
+
See `place-element` Step 6a for the mechanical linking steps.
|
|
124
|
+
|
|
114
125
|
## Translation Chain
|
|
115
126
|
|
|
116
127
|
An AUTOMATION reacting to an event from another system — external (a webhook/API) or second-swimlane (another team's own timeline) — needs a two-stage shape, never a direct reaction:
|
|
@@ -69,6 +69,8 @@ A todo-list read model's fields describe the pending item — the identity it's
|
|
|
69
69
|
|
|
70
70
|
Every field must set a `mapping` per `eventmodeling-identifying-outputs`'s field data lineage rules (`"<EventTitle>.<fieldName>"`, `"latest:..."`, `"aggregate:..."`, `"derived:..."`), and `"cardinality"` (`"Single"` unless the field genuinely holds a list).
|
|
71
71
|
|
|
72
|
+
A todo-list read model is always list-shaped — it's a queue of pending items, never a single record — so it always gets **`meta.listElement: true`** on the node itself (see `eventmodeling-core-rules`'s READMODEL section). This is a node-level flag separate from any field's own `cardinality`.
|
|
73
|
+
|
|
72
74
|
## Placement — todo-list READMODEL, one column before its automation
|
|
73
75
|
|
|
74
76
|
The todo-list read model goes in the interaction lane, **one column before** its automation (actor lane) — the automation's own column already holds the COMMAND it issues, so the read model can never share that column.
|
|
@@ -85,14 +87,14 @@ mcp__eventmodelers__place_element {
|
|
|
85
87
|
"columnIndex": <automationColumnIndex - 1>
|
|
86
88
|
}
|
|
87
89
|
```
|
|
88
|
-
Then set `meta.fields` on the returned node id:
|
|
90
|
+
Then set `meta.fields` and `meta.listElement: true` on the returned node id:
|
|
89
91
|
```
|
|
90
92
|
mcp__eventmodelers__submit_node_events {
|
|
91
93
|
"boardId": "<BOARD_ID>",
|
|
92
94
|
"events": [{
|
|
93
95
|
"id": "<event-uuid>", "eventType": "node:changed", "nodeId": "<returned-node-id>",
|
|
94
96
|
"boardId": "<BOARD_ID>", "timestamp": 1234567890,
|
|
95
|
-
"meta": {"type": "READMODEL", "title": "NotificationsToSend", "fields": [...]}
|
|
97
|
+
"meta": {"type": "READMODEL", "title": "NotificationsToSend", "fields": [...], "listElement": true}
|
|
96
98
|
}]
|
|
97
99
|
}
|
|
98
100
|
```
|
|
@@ -145,4 +147,5 @@ List the result (connected / chain-resolved) for every automation checked — th
|
|
|
145
147
|
- [ ] Every **worker**-stage todo-list read model's opening and closing events are identified, including the automation's own resulting event as a closing event
|
|
146
148
|
- [ ] Every **translation**-stage todo-list read model has an opening event (the external EVENT) and **no closing event at all** — no backward arrow from its own internal EVENT back to its own todo list
|
|
147
149
|
- [ ] No todo-list read model uses a `status` field instead of list membership
|
|
150
|
+
- [ ] Every todo-list read model has `meta.listElement: true` set on the node
|
|
148
151
|
- [ ] Every todo-list read model sits one column before its automation, never sharing its column
|
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md
CHANGED
|
@@ -386,7 +386,7 @@ Pass `compact: true` — you already hold every scenario object you sent, so the
|
|
|
386
386
|
|
|
387
387
|
### Step 4b — List-type readmodel scenario fields
|
|
388
388
|
|
|
389
|
-
When `then` contains a READMODEL whose `listElement` property is `true
|
|
389
|
+
When `then` contains a READMODEL whose `listElement` property is `true` — set on the node when it was created, per `eventmodeling-core-rules`'s READMODEL section and `eventmodeling-identifying-outputs`/`eventmodeling-designing-automation-chains` — add two fields **at the scenario level** (not inside `then`):
|
|
390
390
|
|
|
391
391
|
**`examples`** — one object per expected row, keyed by the readmodel's snake_case field names. Without this the spec node renders with no expected output and the scenario is unverifiable.
|
|
392
392
|
|
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/SKILL.md
CHANGED
|
@@ -222,6 +222,8 @@ Set the field's `generated` property according to this table. Fields projected d
|
|
|
222
222
|
|
|
223
223
|
Every field must also set `"cardinality"` — use `"Single"` unless the field genuinely holds a list of values (e.g. line items, a collection projected from multiple events), in which case use `"List"`. Default to `"Single"` when unsure. Include only the fields the consuming SCREEN or AUTOMATION actually displays or needs — do not add speculative fields; enrich later via `/attributes`.
|
|
224
224
|
|
|
225
|
+
If the READMODEL itself represents a set of rows rather than a single record — e.g. `InvoiceList`, `ProductList` — also set `meta.listElement: true` on the node (see `eventmodeling-core-rules`'s READMODEL section). This is separate from a field's `cardinality`: `listElement` says the whole read model is a list of elements shaped by its defined fields, `cardinality: "List"` says one field on an otherwise-single-record read model holds multiple values.
|
|
226
|
+
|
|
225
227
|
```json
|
|
226
228
|
{
|
|
227
229
|
"type": "READMODEL",
|
|
@@ -259,7 +261,7 @@ mcp__eventmodelers__place_element {
|
|
|
259
261
|
"columnIndex": <consumerScreenColumnIndex> // or <automationColumnIndex - 1> for an AUTOMATION consumer
|
|
260
262
|
}
|
|
261
263
|
```
|
|
262
|
-
Then set `meta.fields` (with `mapping`/`generated`/`cardinality`) on the returned node id:
|
|
264
|
+
Then set `meta.fields` (with `mapping`/`generated`/`cardinality`) — and `meta.listElement: true` if this read model is list-shaped — on the returned node id:
|
|
263
265
|
```
|
|
264
266
|
mcp__eventmodelers__submit_node_events {
|
|
265
267
|
"boardId": "<BOARD_ID>",
|