@eventmodelers/cli 1.0.148 → 1.0.150

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.
Files changed (19) hide show
  1. package/package.json +1 -1
  2. package/shared/skills/learn-eventmodelers-api/SKILL.md +1 -1
  3. package/stacks/axon/templates/.claude/skills/build-state-change/SKILL.md +3 -2
  4. package/stacks/modeling-kit/templates/.claude/skills/attributes/SKILL.md +3 -1
  5. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/SKILL.md +1 -1
  6. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-core-rules/SKILL.md +3 -4
  7. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-automation-chains/SKILL.md +1 -1
  8. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/SKILL.md +35 -40
  9. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/references/api-fallback.md +3 -3
  10. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/SKILL.md +11 -25
  11. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-information-completeness/SKILL.md +65 -0
  12. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/SKILL.md +15 -46
  13. package/stacks/modeling-kit/templates/.claude/skills/html-screen/SKILL.md +11 -14
  14. package/stacks/modeling-kit/templates/.claude/skills/html-screen/references/api-fallback.md +1 -1
  15. package/stacks/modeling-kit/templates/.claude/skills/place-element/SKILL.md +1 -1
  16. package/stacks/modeling-kit/templates/.claude/skills/storyboard-screen/SKILL.md +2 -2
  17. package/stacks/modeling-kit/templates/.claude/skills/storyboard-screen/references/api-fallback.md +1 -1
  18. package/stacks/modeling-kit/templates/kit/CLAUDE.md +3 -0
  19. package/stacks/umadb/templates/.claude/skills/build-state-change/SKILL.md +2 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eventmodelers/cli",
3
- "version": "1.0.148",
3
+ "version": "1.0.150",
4
4
  "description": "Eventmodelers CLI — real-time Claude agent + skills for Claude Code, for any stack (Node, Supabase, Axon, OpenCQRS, UmaDB, Kurrent, or modeling-only)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -164,7 +164,7 @@ UUID // universally unique identifier
164
164
  Custom // structured/nested value — use with `subfields` or `schema`
165
165
  ```
166
166
 
167
- Other `Field` properties: `name`, `example`, `subfields[]` (nested `Field`s), `mapping`, `optional`, `technicalAttribute`, `generated`, `idAttribute`, `pii`, `schema`, `cardinality` (`"List"` | `"Single"`).
167
+ Other `Field` properties: `name`, `example`, `subfields[]` (nested `Field`s), `mapping`, `optional`, `technicalAttribute`, `generated`, `idAttribute`, `pii`, `tag` (the field's value tags the event for Dynamic Consistency Boundaries — DCB; see https://dcb.events), `schema`, `cardinality` (`"List"` | `"Single"`).
168
168
 
169
169
  Use exactly these type names (case-sensitive) — not lowercase (`string`), synonyms (`Number`, `Text`, `Integer`), or types outside this set.
170
170
 
@@ -28,8 +28,9 @@ Read `.build-kit/.slices/{context}/{slicename}/slice.json`. Extract, and use **o
28
28
  - `commands[].fields[]` → Command record fields, in order
29
29
  - `events[].fields[]` → Event record fields, in order
30
30
  - `specifications[]` (GWT scenarios) → one test method per scenario
31
- - Which command field(s) have `idAttribute: true` — these carry `@TargetEntityId` (see Step 1) and the
32
- matching event field(s) carry `@EventTag`
31
+ - Which command field(s) have `idAttribute: true` — these carry `@TargetEntityId` (see Step 1)
32
+ - The event's DCB tags — **both** the field(s) matching the command's `idAttribute` field(s) **and**
33
+ every event field with `tag: true` carry `@EventTag` (see Step 2); each gets its own `EventTags` constant
33
34
  - `storylines[]` (optional, may be absent) → narrated walkthroughs with ordered `elements[]` "beats";
34
35
  see Step 7b for how a COMMAND beat in one of these can add a supplementary test
35
36
 
@@ -138,7 +138,7 @@ For each node, compute (don't write yet):
138
138
 
139
139
  ### If operation is `add`:
140
140
  - Check if a field with that name already exists in `meta.fields` — if so, skip this node (log it).
141
- - Otherwise append a new field:
141
+ - Otherwise append a new field, with its source set per `eventmodeling-information-completeness`. Along the chain, the same name carries the value from hop to hop. The **source element** is where it starts (`generated: true`), unless one of its own inbound neighbours already carries it, in which case the chain stops too early: extend it to the real origin.
142
142
  ```json
143
143
  {
144
144
  "name": "<attributeName>",
@@ -157,6 +157,7 @@ For each node, compute (don't write yet):
157
157
  ### If operation is `rename`:
158
158
  - Find the field where `name === oldName` (case-insensitive). If not found in this node, skip it (log it).
159
159
  - Update only the `name` property to `newName`. Leave all other field properties unchanged.
160
+ - Then fix the downstream fields outside the chain that lost their source through the rename (see `eventmodeling-information-completeness` → "Renames move the lineage too"), in the same batch.
160
161
 
161
162
  Collect one `node:changed` event per affected node, then send them together.
162
163
 
@@ -196,6 +197,7 @@ Tell the user:
196
197
  - **Chain**: list each element in order (type + title + cell)
197
198
  - **Updated**: which nodes were changed
198
199
  - **Skipped**: which nodes were skipped and why (field already exists / field not found / slice not `Created` — asked to confirm)
200
+ - **Lineage**: where the attribute starts (`generated` on which element) and any `mapping` you set for a renamed hop
199
201
 
200
202
  Example output:
201
203
  ```
@@ -127,7 +127,7 @@ Older versions of this skill wrote the completeness check as a markdown report r
127
127
  ## Completeness Criteria
128
128
 
129
129
  The model is **complete** when:
130
- Every event field has a source (command or system or is marked as generated)
130
+ Every field on every element has a source per `eventmodeling-information-completeness`
131
131
  Every command input becomes event/state data
132
132
  Every read model field has event source
133
133
  All state transitions are covered
@@ -56,6 +56,7 @@ Slices are not placed directly — they're **derived** from a completed model. E
56
56
  ### COMMAND
57
57
  - Represents business intent — what an actor wants to do.
58
58
  - Can be rejected; only succeeds if its documented preconditions hold.
59
+ - Carries every piece of information its validation needs that isn't in the stream's prior events. That includes context such as the current time or date, the acting user and the tenant. The decision never reads a clock or a session on its own. See `eventmodeling-identifying-inputs` → "Command input is self-contained".
59
60
  - Naming: imperative, business language.
60
61
  - Valid: `PlaceOrder`, `ConfirmPayment`, `CancelSubscription`
61
62
  - Invalid: `LoadOrders`, `FetchData`, `OpenDialog` — these are queries or UI-only actions, not business intent
@@ -100,11 +101,9 @@ Every event model needs one, established before brainstorming events (it's the f
100
101
 
101
102
  See `eventmodeling-brainstorming-events` for how the catalog is built, `eventmodeling-identifying-inputs` for attributing commands to it, and `eventmodeling-validating-event-models` for re-checking it holds once the model is complete.
102
103
 
103
- ## Field Lineage
104
+ ## Field Lineage (Information Completeness)
104
105
 
105
- Every field on a COMMAND or READMODEL must carry a `mapping` that traces it back to where its value actually comes from — user input, the session, a prior event's field, a derived/computed value, or an external webhook payload. **A field with no mapping is a gap, not a detail to fill in later.**
106
-
107
- See `eventmodeling-identifying-inputs` for the full COMMAND mapping vocabulary and `eventmodeling-identifying-outputs` for the READMODEL equivalent — this rule only states that every field must have one, not the mapping syntax itself.
106
+ Every field on every element has a known source: a same-named field on a connected upstream element, a `mapping` to a differently named field or a connected event, or `generated: true` where the information starts. Check it each time you add or rename a field. **A field with no source is a gap, not a detail to fill in later.** The full rule, per element type, is in `eventmodeling-information-completeness`.
108
107
 
109
108
  ## Connections Read Forward
110
109
 
@@ -69,7 +69,7 @@ When a worker automation *is* warranted, wire the todo lists differently for eac
69
69
 
70
70
  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.
71
71
 
72
- 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).
72
+ Every field needs a known source per `eventmodeling-information-completeness`, and `"cardinality"` (`"Single"` unless the field genuinely holds a list).
73
73
 
74
74
  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`.
75
75
 
@@ -67,6 +67,21 @@ Show which commands come from automation vs. user actions: catalog every command
67
67
  ### 3. Document Command Specifics
68
68
  For each command, define structure: its source, its typed input fields, the validation rules it enforces, the preconditions it checks against stream state, the success event it produces, and each distinct failure outcome. A full worked example (ConfirmOrder) is in `references/examples.md`.
69
69
 
70
+ ### 3b. Command input is self-contained
71
+
72
+ A command must carry **everything needed to validate it**. For each validation rule and precondition, list every value the rule reads. Each value is either:
73
+ - **a field on the command**, or
74
+ - **stream state**: data from prior events of this entity (step 6).
75
+
76
+ Nothing else. Watch for **contextual information** that is easy to overlook because "the system just knows it":
77
+ - the current time or date (`now`, `today`), for deadlines, expiry, "must be in the future", opening hours, cut-off dates
78
+ - the acting user, their role, and the tenant or organization, for "only the owner may…", limits per customer
79
+ - the timezone or locale, when a rule depends on the local date ("before midnight", "on a business day")
80
+
81
+ Each one becomes a command field, `generated: true` (it enters the model at the command, supplied when the command is issued, per `eventmodeling-information-completeness`). The decision then never reads a clock or session on its own, and the same command always gets the same outcome. That is also what lets a GWT scenario state it: *when `ReserveBike` with `requestedAt = 09:00`, then rejected, because `startTime` is in the past*.
82
+
83
+ A timestamp the event records (`reservedAt`) comes from that command field, not from a clock read when the event is stored.
84
+
70
85
  ### 4. Create Command Catalog
71
86
  List all commands the system accepts, grouped into UI-issued and processor-issued sections, each with its source, input fields, and the event it produces. A full worked example is in `references/examples.md`.
72
87
 
@@ -80,60 +95,39 @@ Document what comes from stream state beyond the command's explicit input: the p
80
95
 
81
96
  Instead of writing a markdown document, **place each COMMAND on the board** using the `node:created` API.
82
97
 
83
- > **CRITICAL: Every COMMAND node MUST include `meta.fields` with a `mapping` on every field.** A command without fields — or with fields that lack `mapping` — is an incomplete model that cannot be traced, validated, or turned into code.
84
-
85
- ### Field data lineage — the `mapping` attribute
98
+ > **CRITICAL: Every COMMAND node MUST include `meta.fields`, and every field must have a known source.** A command without fields — or with fields whose origin is unknown — is an incomplete model that cannot be traced, validated, or turned into code.
86
99
 
87
- Every field on a COMMAND must carry a `mapping` that says exactly where its value comes from. Use one of these forms:
100
+ ### Field data lineage — `mapping` and `generated`
88
101
 
89
- | `mapping` format | Meaning | `generated` | Example |
90
- |---|---|---|---|
91
- | `"user-input"` | User types or selects this value directly | `false` | `startTime` picked from a date picker |
92
- | `"session:<fieldName>"` | Read from the authenticated session | `false` | `session:customerId` |
93
- | `"<EventTitle>.<fieldName>"` | Taken from a previously emitted event | `false` | `BikeReserved.bikeId` |
94
- | `"derived:<expression>"` | Computed by the system | `true` | `derived:uuid4()`, `derived:now()` |
95
- | `"webhook:<payloadField>"` | Comes from an external webhook payload | `false` | `webhook:gateway_transaction_id` |
96
-
97
- Set the field's `generated` property according to this table. Fields typed by the user (typically from a screen form) are never generated — the user supplies the value. Fields with a `derived:` mapping are always generated — the system produces the value automatically.
98
-
99
- The `mapping` traces the value all the way back to its origin. **If you cannot write a mapping for a field, that field has an unknown origin — treat it as a gap and resolve it.**
102
+ Apply `eventmodeling-information-completeness` to every COMMAND field and every EVENT field as you add it. A command's upstream is the SCREEN or AUTOMATION issuing it. An event's upstream is the COMMAND producing it. User input is `generated` on the screen that captures it, not on the command. Contextual values the validation reads (current time, acting user, tenant) are `generated` on the command (see 3b). Only an id the system creates when recording the fact is `generated` on the event. **A field whose source you can't name is a gap. Resolve it before you place the field.**
100
103
 
101
104
  Every field must also set `"cardinality"` — use `"Single"` unless the field genuinely holds a list of values (e.g. multi-select), in which case use `"List"`. Default to `"Single"` when unsure. Only add fields the command actually needs to do its job — do not add speculative fields; enrich later via `/attributes`.
102
105
 
103
106
  ### Field traceability rule for events
104
107
 
105
- Every field in the resulting EVENT must either:
106
- - Appear in the command's `fields` (direct mapping — same name or documented rename), or
107
- - Be derivable from command fields + system state
108
-
109
- Example: `BikeReserved.reservedAt` is not in `ReserveBike` but derives from `derived:now()`. That is acceptable — document it. `BikeReserved.reservationId` is not in `ReserveBike` but derives from `derived:uuid4()`. Also acceptable.
110
-
111
- If an event field has **no** corresponding command field and **no** derivation rule, that is a gap — add it to the command or note it as a system-generated field.
112
-
113
- > **Connected-element rule**: A field `mapping` may only reference elements that are connected to this COMMAND via board arrows (`SCREEN → COMMAND`). If a field's value would come from something not yet connected, flag it as a gap. **If a mapping cannot be defined at all, it hints at missing data in the model or a modeling error** — a missing event field, a missing screen field, or a broken connection. Investigate before proceeding.
108
+ Every EVENT field comes from its COMMAND (same name, or `mapping` for a rename) or is created by the system when the event is recorded (`generated: true`). Anything else means the command, or the screen behind it, is missing that field.
114
109
 
115
110
  ```json
116
111
  {
117
112
  "type": "COMMAND",
118
113
  "title": "ReserveBike",
119
114
  "fields": [
120
- {"name": "customerId", "type": "String", "example": "cust-42", "mapping": "session:customerId", "generated": false},
121
- {"name": "bikeId", "type": "String", "example": "bike-17", "mapping": "user-input", "generated": false},
122
- {"name": "stationId", "type": "String", "example": "stn-03", "mapping": "user-input", "generated": false},
123
- {"name": "startTime", "type": "DateTime", "example": "2026-06-01T09:00:00Z", "mapping": "user-input", "generated": false},
124
- {"name": "endTime", "type": "DateTime", "example": "2026-06-01T17:00:00Z", "mapping": "user-input", "generated": false}
115
+ {"name": "customerId", "type": "String", "example": "cust-42", "generated": true},
116
+ {"name": "bikeId", "type": "String", "example": "bike-17"},
117
+ {"name": "stationId", "type": "String", "example": "stn-03"},
118
+ {"name": "startTime", "type": "DateTime", "example": "2026-06-01T09:00:00Z"},
119
+ {"name": "endTime", "type": "DateTime", "example": "2026-06-01T17:00:00Z", "mapping": "returnTime"},
120
+ {"name": "requestedAt", "type": "DateTime", "example": "2026-06-01T08:45:00Z", "generated": true}
125
121
  ]
126
122
  }
127
123
  ```
128
124
 
125
+ `customerId` (from the session) and `requestedAt` (the current time) are contextual: the rules "a customer may hold one active reservation" and "`startTime` must be after `requestedAt`" read them, so they are on the command, `generated: true`. `bikeId`, `stationId` and `startTime` come from same-named fields on the `Reserve a Bike` screen. `endTime` is called `returnTime` on that screen.
126
+
129
127
  Event fields produced by this command:
130
- - `reservationId` → `derived:uuid4()` → `generated: true`
131
- - `customerId` → `ReserveBike.customerId` → `generated: false`
132
- - `bikeId` → `ReserveBike.bikeId` → `generated: false`
133
- - `stationId` → `ReserveBike.stationId` → `generated: false`
134
- - `startTime` → `ReserveBike.startTime` → `generated: false`
135
- - `endTime` → `ReserveBike.endTime` → `generated: false`
136
- - `reservedAt` → `derived:now()` → `generated: true`
128
+ - `reservationId` → created by the system → `generated: true`
129
+ - `customerId`, `bikeId`, `stationId`, `startTime`, `endTime` → same name on `ReserveBike` → no mapping
130
+ - `reservedAt` → `mapping: "requestedAt"`, the command's current time, never a clock read when the event is stored
137
131
 
138
132
  Commands go in the `interaction` lane — same column as their resulting event.
139
133
 
@@ -167,9 +161,9 @@ mcp__eventmodelers__submit_node_events {
167
161
  "type": "COMMAND",
168
162
  "title": "ReserveBike",
169
163
  "fields": [
170
- {"name": "customerId", "type": "String", "example": "cust-42", "mapping": "session:customerId"},
171
- {"name": "bikeId", "type": "String", "example": "bike-17", "mapping": "user-input"},
172
- {"name": "startTime", "type": "DateTime", "example": "2026-06-01T09:00:00Z","mapping": "user-input"}
164
+ {"name": "customerId", "type": "String", "example": "cust-42", "generated": true},
165
+ {"name": "bikeId", "type": "String", "example": "bike-17"},
166
+ {"name": "startTime", "type": "DateTime", "example": "2026-06-01T09:00:00Z"}
173
167
  ]
174
168
  }
175
169
  }],
@@ -251,6 +245,7 @@ Older versions of this skill wrote the command catalog as a markdown document ra
251
245
  - [ ] Preconditions from stream state are explicit
252
246
  - [ ] Success and failure outcomes documented
253
247
  - [ ] Implicit context from stream state is identified
248
+ - [ ] **Every value a validation rule reads is a command field or stream state, including contextual information (current time/date, acting user, tenant)**
254
249
  - [ ] No undocumented commands exist
255
250
  - [ ] Command naming is consistent and clear
256
251
  - [ ] Processor triggers are explicit
@@ -262,7 +257,7 @@ Older versions of this skill wrote the command catalog as a markdown document ra
262
257
  ## Key Principles
263
258
 
264
259
  1. **Source Clarity**: Every command comes from UI or Processor
265
- 2. **Input Completeness**: All needed data captured
260
+ 2. **Input Completeness**: All data needed to validate is on the command, including contextual information like the current time
266
261
  3. **Validation Explicit**: All rules documented
267
262
  4. **State Awareness**: Preconditions from stream state clear
268
263
  5. **Event Mapping**: Every input becomes event data
@@ -40,9 +40,9 @@ curl -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/events" \
40
40
  "type": "COMMAND",
41
41
  "title": "ReserveBike",
42
42
  "fields": [
43
- {"name": "customerId", "type": "String", "example": "cust-42", "mapping": "session:customerId"},
44
- {"name": "bikeId", "type": "String", "example": "bike-17", "mapping": "user-input"},
45
- {"name": "startTime", "type": "DateTime", "example": "2026-06-01T09:00:00Z","mapping": "user-input"}
43
+ {"name": "customerId", "type": "String", "example": "cust-42", "generated": true},
44
+ {"name": "bikeId", "type": "String", "example": "bike-17"},
45
+ {"name": "startTime", "type": "DateTime", "example": "2026-06-01T09:00:00Z"}
46
46
  ]
47
47
  }
48
48
  }]'
@@ -131,7 +131,7 @@ Instead of writing a markdown document, **place each READMODEL (and any missing
131
131
 
132
132
  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.
133
133
 
134
- > **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.
134
+ > **CRITICAL: Every READMODEL node MUST include `meta.fields`, and every field must trace back to a connected source event (or be `generated`).** A read model without fields, or with fields of unknown origin, has no data lineage and cannot be traced back to its source events.
135
135
 
136
136
  ### The typical slice pattern
137
137
 
@@ -195,30 +195,15 @@ Before finalizing any read model, ask: "does this screen contain more than one c
195
195
 
196
196
  **Do not re-derive read model needs from a screen's title or description alone, and do not rely on the orchestrator's phase-summary handoff for this** — if you arrived here via `eventmodeling-orchestrating-event-modeling`, the handoff after Step 3 is a short hand-written prose summary (`.eventmodelers/interviews/.../EVENTMODELING.md`), not the actual field data. It will not reliably carry the per-field mappings forward. Go back to the board itself:
197
197
 
198
- For every SCREEN node, fetch it directly (`get_node`/`get_nodes`, never from memory) and read its `meta.fields` — a full read on purpose (`projection: "line"` carries field names only, not the `mapping` this step groups by); scope it to the chapter, e.g. `get_nodes { "boardId": "<BOARD_ID>", "type": "SCREEN", "chapterId": "<CHAPTER_ID>" }`, rather than board-wide. Step 3 already required every field to carry a `mapping`, and for view fields that mapping is already in the exact form `"<ReadModelTitle>.<fieldName>"` — recorded specifically so this step doesn't have to re-guess it.
198
+ For every SCREEN node, fetch it directly (`get_node`/`get_nodes`, never from memory) and read its `meta.fields` — a full read on purpose (`projection: "line"` carries field names only, not the `mapping` this step groups by); scope it to the chapter, e.g. `get_nodes { "boardId": "<BOARD_ID>", "type": "SCREEN", "chapterId": "<CHAPTER_ID>" }`, rather than board-wide. Step 3 already required every view field to name its read model, as `mapping: "<ReadModelTitle>"`. It was recorded so this step doesn't have to guess it again.
199
199
 
200
200
  - **Group the screen's fields by the `<ReadModelTitle>` already named in their `mapping`.** That grouping — not a fresh read of the screen's visuals — is the read model's title and field list. Build the READMODEL node from it directly.
201
- - If a field's `mapping` names a read model that isn't `"<CommandTitle>.<fieldName>"` or `"session:..."` or `"derived:..."`, it is a read-model reference — treat it as a requirement, not a suggestion.
201
+ - A field whose `mapping` is a title that isn't on the board yet is a read-model reference. Treat it as a requirement, not a suggestion. (Fields marked `generated: true` are input or display-only values. They need no read model.)
202
202
  - A screen with no fields, or with fields that carry no read-model-shaped mapping, is **not** evidence that it needs no read model. Re-check it against the three rules above (view screen / automation / command screen showing prior state) before concluding it's the rare blank-form exception — and say explicitly why it qualifies.
203
203
 
204
204
  ### Step 5e — Field data lineage — the `mapping` attribute on READMODEL fields
205
205
 
206
- Every field on a READMODEL must carry a `mapping` that says exactly which event (or command) field it is projected from. Use one of these forms:
207
-
208
- | `mapping` format | Meaning | `generated` | Example |
209
- |---|---|---|---|
210
- | `"<EventTitle>.<fieldName>"` | Projected directly from an event field | `false` | `"BikeReserved.customerId"` |
211
- | `"latest:<EventTitle>.<fieldName>"` | Latest value from the most recent event of this type | `false` | `"latest:BikeStatusChanged.toStatus"` |
212
- | `"aggregate:<EventTitle>.<fieldName>"` | Aggregated across multiple events of this type | `true` | `"aggregate:RentalEnded.durationMinutes"` |
213
- | `"derived:<expression>"` | Calculated from other read model fields | `true` | `"derived:sum(lineItems.amount)"` |
214
-
215
- Set the field's `generated` property according to this table. Fields projected directly from events are not generated — they carry a real domain value. Fields that are aggregated or calculated by the system are generated.
216
-
217
- **Field traceability rule**: Every field in a read model must trace back to at least one source event. If a field cannot be mapped to any event in the timeline, it is either:
218
- - A calculated/derived field — document the derivation expression, or
219
- - A missing event field — add it to the source event before proceeding.
220
-
221
- > **Connected-element rule**: A read model's field `mapping` may only reference EVENTs that are connected to this READMODEL via a board `EVENT → READMODEL` arrow. If a field needs data from an event that is not connected, either add the connection or flag it as a gap. **If a mapping cannot be defined for a field, it signals missing data in the model, a missing event, or a modeling error.** Do not leave unmapped fields without a note.
206
+ Apply `eventmodeling-information-completeness` to every READMODEL field as you add it. A read model's upstream is the EVENTs connected to it by `EVENT → READMODEL` arrows. A value the projection calculates itself (a count, a sum, a duration) is `generated: true`. A field no connected event supplies is a missing event field or a missing connection: add it before proceeding.
222
207
 
223
208
  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
209
 
@@ -229,12 +214,13 @@ If the READMODEL itself represents a set of rows rather than a single record —
229
214
  "type": "READMODEL",
230
215
  "title": "ActiveReservationView",
231
216
  "fields": [
232
- {"name": "reservationId", "type": "String", "example": "res-001", "mapping": "BikeReserved.reservationId", "generated": false},
233
- {"name": "customerId", "type": "String", "example": "cust-42", "mapping": "BikeReserved.customerId", "generated": false},
234
- {"name": "bikeId", "type": "String", "example": "bike-17", "mapping": "BikeReserved.bikeId", "generated": false},
235
- {"name": "stationId", "type": "String", "example": "stn-03", "mapping": "BikeReserved.stationId", "generated": false},
236
- {"name": "expiresAt", "type": "DateTime", "example": "2026-06-01T09:30:00Z", "mapping": "ReservationConfirmed.expiresAt", "generated": false},
237
- {"name": "status", "type": "String", "example": "confirmed", "mapping": "latest:ReservationConfirmed.status", "generated": false}
217
+ {"name": "reservationId", "type": "String", "example": "res-001"},
218
+ {"name": "customerId", "type": "String", "example": "cust-42"},
219
+ {"name": "bikeId", "type": "String", "example": "bike-17"},
220
+ {"name": "stationId", "type": "String", "example": "stn-03"},
221
+ {"name": "expiresAt", "type": "DateTime", "example": "2026-06-01T09:30:00Z", "mapping": "ReservationConfirmed"},
222
+ {"name": "status", "type": "String", "example": "confirmed", "mapping": "toStatus"},
223
+ {"name": "minutesLeft", "type": "Integer", "example": "25", "generated": true}
238
224
  ]
239
225
  }
240
226
  ```
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: eventmodeling-information-completeness
3
+ description: The rule for where every field's value comes from. Use it whenever a field is added, renamed or reviewed on any element (SCREEN, HTML_SCREEN, COMMAND, EVENT, READMODEL, AUTOMATION), whether in a full modeling run, a one-off /attributes or /place-element call, or a fix. Each field either matches a same-named field on a connected upstream element, maps to a differently named field or a connected event, or is marked generated where the information starts. Anything else is a gap to close. Not a step of its own. Other skills reference it instead of restating it.
4
+ ---
5
+
6
+ # Information Completeness
7
+
8
+ Information doesn't appear out of nowhere. Every field on every element either comes from an element **connected upstream** of it, or starts at this element. Check this **each time you add or rename a field**, before you write it. Don't leave it for a later completeness pass.
9
+
10
+ ## The four valid states of a field
11
+
12
+ | Where the value comes from | What to write | Example |
13
+ |---|---|---|
14
+ | A connected upstream element has a field **with the same name** | nothing: no `mapping`, the same name *is* the mapping | `BikeReserved.bikeId` ← `ReserveBike.bikeId` |
15
+ | A connected upstream element has it **under a different name** | `mapping: "<sourceFieldName>"` (a sub-field as `"<field>.<subfield>"`) | `ReserveBike.endTime` with `mapping: "returnTime"` |
16
+ | It comes from one specific connected **event** (or other connected element), e.g. because several upstream elements carry the field | `mapping: "<EventTitle>"`, the element's title on its own | `ReservationView.expiresAt` with `mapping: "ReservationConfirmed"` |
17
+ | **Nothing upstream** supplies it: manual input typed into a screen, a session value, a generated id or timestamp, a value computed at this element | `generated: true` | `HTML_SCREEN "Reserve a Bike".startTime`, `BikeReserved.reservationId` |
18
+
19
+ These are the forms the board resolves. The field picker offers the connected fields and the event titles, and the Data Trail follows them. Write the field name or the element title **bare**. Combined forms like `"BikeReserved.bikeId"`, `"user-input"`, `"session:x"` or `"derived:..."` resolve to nothing.
20
+
21
+ ## Upstream means inbound, per element type
22
+
23
+ Lineage always points to where the value comes **from**. It never points downstream.
24
+
25
+ | Element | Its upstream (where its fields can come from) |
26
+ |---|---|
27
+ | SCREEN / HTML_SCREEN | the READMODEL it displays. The COMMAND it submits is **downstream**: never map a screen field to it. |
28
+ | COMMAND | the SCREEN or AUTOMATION issuing it |
29
+ | EVENT | the COMMAND producing it |
30
+ | READMODEL | the EVENTs connected to it (`EVENT → READMODEL`) |
31
+ | AUTOMATION | the READMODEL (todo list) it reads |
32
+
33
+ So information typed into a form is `generated: true` **on the screen**. The command, event and read model then pick it up by the same name, or by a `mapping` where it's renamed. Contextual values a command's validation reads (current time or date, acting user, tenant) are `generated: true` on the **command**. Commands carry everything they need to validate (`eventmodeling-identifying-inputs` → "Command input is self-contained"), and the event takes a recorded time from there. Only an id the system creates when recording the fact is `generated: true` on the event. A count, sum or duration the projection calculates is `generated: true` on the read model.
34
+
35
+ ## How to check a field
36
+
37
+ 1. Look at the element's inbound neighbours, from the edges (`get_connected_nodes` `direction: "inbound"`, `includeFields: true`), or from the layout when the chapter is unwired.
38
+ 2. Pick the row above that matches. One neighbour with the same name, and nothing to write, is the common case.
39
+ 3. **No row fits → it's a gap.** The field is missing upstream, the connection is missing, or the element is missing. Close it: add the field upstream (and carry it through the chain the same way), add the connection, or add the element. Only when the source genuinely can't be decided from the model, apply the questioning rule.
40
+
41
+ `generated: true` means "this information starts here". It is never a way to say "I don't know where this comes from". Don't mark a field `generated` to make a gap go away, and don't invent a `generated` origin in the middle of a flow.
42
+
43
+ ## Planned sources that aren't on the board yet
44
+
45
+ While storyboarding, a displayed screen field is often meant to come from a read model that hasn't been designed yet. Write `mapping: "<ReadModelTitle>"` anyway and flag it as a gap. `eventmodeling-identifying-outputs` groups screen fields by that title to design the read model. Once the read model is connected, a same-named field needs no mapping, and a differently named one gets `mapping: "<readModelFieldName>"`. A displayed field is never marked `generated` just because its read model is missing.
46
+
47
+ ## Renames move the lineage too
48
+
49
+ Renaming a field breaks every downstream field that matched it by name, or that has `mapping: "<oldName>"`. In the same write, either rename those too or set their `mapping` to the new name. `/attributes` does this for a chain.
50
+
51
+ ## Worked example — reserving a bike
52
+
53
+ ```
54
+ HTML_SCREEN "Reserve a Bike" bikeId (generated), startTime (generated), returnTime (generated),
55
+ customerId (generated, from session), bikeCategory (mapping "AvailableBikeView")
56
+ COMMAND ReserveBike bikeId, startTime, customerId (same names, no mapping), endTime (mapping "returnTime"),
57
+ requestedAt (generated: current time, read by "startTime must be in the future")
58
+ EVENT BikeReserved bikeId, startTime, endTime, customerId (same names), reservationId (generated),
59
+ reservedAt (mapping "requestedAt")
60
+ READMODEL ActiveReservation reservationId, bikeId, startTime (same names), expiresAt (mapping "ReservationConfirmed"),
61
+ minutesLeft (generated)
62
+ HTML_SCREEN "Reservation" reservationId, startTime, expiresAt (same names, read model connected)
63
+ ```
64
+
65
+ Every field on the way traces back to a `generated` origin. Nothing appears without a source.
@@ -135,30 +135,9 @@ The result is a `{ roleName: actorRowId }` map, covering human roles only, used
135
135
 
136
136
  ## Mandatory Field Definitions on Screen Nodes
137
137
 
138
- > **CRITICAL: Every screen node MUST include `meta.fields` with a `mapping` on every field.** A screen without fields cannot show data lineage — it becomes impossible to verify that all displayed data has a source event or command. This applies regardless of which content type the screen renders as (HTML_SCREEN by default, or SCREEN when a sketch was explicitly requested).
138
+ > **CRITICAL: Every screen node MUST include `meta.fields`, and every field must have a known source.** A screen without fields cannot show data lineage — it becomes impossible to verify that all displayed data has a source event or command. This applies regardless of which content type the screen renders as (HTML_SCREEN by default, or SCREEN when a sketch was explicitly requested).
139
139
 
140
- There are two types of screens, and each type has a different `mapping` source:
141
-
142
- ### Command screen (input/action screen)
143
-
144
- An **input screen** captures data that will be sent as a command. Its fields map to the command they feed into.
145
-
146
- | Field type | `mapping` | `generated` | Example |
147
- |---|---|---|---|
148
- | User types a value | `"<CommandTitle>.<fieldName>"` | `false` | `"ReserveBike.bikeId"` |
149
- | Read from session | `"session:<fieldName>"` | `false` | `"session:customerId"` |
150
- | Pre-populated from a previous event | `"<EventTitle>.<fieldName>"` | `false` | `"BikeReserved.stationId"` |
151
- | Calculated for display only (not sent to command) | `"derived:<expression>"` | `true` | `"derived:sum(items.price)"` |
152
-
153
- ### View screen (output/read model screen)
154
-
155
- A **view screen** displays data read from a Read Model. Its fields map to the read model that serves the data. View screens are placed adjacent to their read model in Step 5 (Identifying Outputs) — during storyboarding, record the intended read model name as the source even if the read model hasn't been designed yet.
156
-
157
- | Field type | `mapping` | `generated` | Example |
158
- |---|---|---|---|
159
- | Displayed from a read model | `"<ReadModelTitle>.<fieldName>"` | `false` | `"ActiveReservationView.status"` |
160
- | Displayed from a direct event (no read model yet) | `"<EventTitle>.<fieldName>"` | `false` | `"BikeReserved.startTime"` |
161
- | Calculated/formatted on the client | `"derived:<expression>"` | `true` | `"derived:formatDuration(durationMinutes)"` |
140
+ Apply `eventmodeling-information-completeness` to every screen field as you add it. In short: input the user enters, session values and display-only calculations are `generated: true`. Displayed data names its read model (`mapping: "<ReadModelTitle>"`), even when that read model isn't designed yet, which Step 5 then groups by. A screen field never maps to the command it submits.
162
141
 
163
142
  ### Example: command screen fields
164
143
 
@@ -167,12 +146,12 @@ A **view screen** displays data read from a Read Model. Its fields map to the re
167
146
  "type": "HTML_SCREEN",
168
147
  "title": "Reserve a Bike",
169
148
  "fields": [
170
- {"name": "bikeId", "type": "String", "example": "bike-17", "mapping": "ReserveBike.bikeId"},
171
- {"name": "stationId", "type": "String", "example": "stn-03", "mapping": "ReserveBike.stationId"},
172
- {"name": "startTime", "type": "DateTime", "example": "2026-06-01T09:00:00Z", "mapping": "ReserveBike.startTime"},
173
- {"name": "endTime", "type": "DateTime", "example": "2026-06-01T17:00:00Z", "mapping": "ReserveBike.endTime"},
174
- {"name": "bikeCategory","type": "String", "example": "City Bike", "mapping": "AvailableBikeView.category"},
175
- {"name": "dailyRate", "type": "Decimal", "example": "0.10", "mapping": "AvailableBikeView.ratePerMinute"}
149
+ {"name": "bikeId", "type": "String", "example": "bike-17", "generated": true},
150
+ {"name": "stationId", "type": "String", "example": "stn-03", "generated": true},
151
+ {"name": "startTime", "type": "DateTime", "example": "2026-06-01T09:00:00Z", "generated": true},
152
+ {"name": "returnTime", "type": "DateTime", "example": "2026-06-01T17:00:00Z", "generated": true},
153
+ {"name": "bikeCategory","type": "String", "example": "City Bike", "mapping": "AvailableBikeView"},
154
+ {"name": "dailyRate", "type": "Decimal", "example": "0.10", "mapping": "ratePerMinute"}
176
155
  ]
177
156
  }
178
157
  ```
@@ -184,27 +163,17 @@ A **view screen** displays data read from a Read Model. Its fields map to the re
184
163
  "type": "HTML_SCREEN",
185
164
  "title": "Reservation Confirmed",
186
165
  "fields": [
187
- {"name": "reservationId", "type": "String", "example": "res-001", "mapping": "ActiveReservationView.reservationId"},
188
- {"name": "bikeName", "type": "String", "example": "City Bike — Gazelle", "mapping": "ActiveReservationView.bikeName"},
189
- {"name": "stationName", "type": "String", "example": "Central Park East", "mapping": "ActiveReservationView.stationName"},
190
- {"name": "startTime", "type": "DateTime", "example": "2026-06-01T09:00:00Z", "mapping": "ActiveReservationView.startTime"},
191
- {"name": "expiresAt", "type": "DateTime", "example": "2026-06-01T09:30:00Z", "mapping": "ActiveReservationView.expiresAt"},
192
- {"name": "estimatedCost", "type": "Decimal", "example": "48.00", "mapping": "derived:durationHours × AvailableBikeView.ratePerMinute × 60"}
166
+ {"name": "reservationId", "type": "String", "example": "res-001", "mapping": "ActiveReservationView"},
167
+ {"name": "bikeName", "type": "String", "example": "City Bike — Gazelle", "mapping": "ActiveReservationView"},
168
+ {"name": "stationName", "type": "String", "example": "Central Park East", "mapping": "ActiveReservationView"},
169
+ {"name": "startTime", "type": "DateTime", "example": "2026-06-01T09:00:00Z", "mapping": "ActiveReservationView"},
170
+ {"name": "expiresAt", "type": "DateTime", "example": "2026-06-01T09:30:00Z", "mapping": "ActiveReservationView"},
171
+ {"name": "estimatedCost", "type": "Decimal", "example": "48.00", "generated": true}
193
172
  ]
194
173
  }
195
174
  ```
196
175
 
197
- ### Connected-element rule (critical constraint)
198
-
199
- > **A field `mapping` may only reference an element that is connected to this SCREEN via a board connection arrow.**
200
- >
201
- > - A command screen may only map to: the COMMAND it submits, the SCREEN's own input (user-input), or a READ MODEL whose `READMODEL → SCREEN` arrow points to this screen.
202
- > - A view screen may only map to: the READ MODEL connected to it via a `READMODEL → SCREEN` arrow, or derived expressions.
203
- > - If a mapping references an element that is not yet connected — for example, `AvailableBikeView.category` when no such read model exists on the board — that is a **gap**. It signals either:
204
- > 1. A read model is missing and must be created in Step 5 (Identifying Outputs), or
205
- > 2. The connection arrow is missing and must be added.
206
- >
207
- > **When you encounter a mapping that cannot resolve to a connected element, write the mapping as-is but flag it as a gap in the completeness notes.** Do not invent connections that don't exist.
176
+ A read model named in a `mapping` but not on the board (or not connected to this screen) is a **gap** for Step 5 (Identifying Outputs) to close. Flag it in the completeness notes, and don't invent connections that don't exist.
208
177
 
209
178
  Every field must also set `"cardinality"` — use `"Single"` unless the field genuinely displays/collects a list of values (e.g. a repeated line-item row), in which case use `"List"`. Default to `"Single"` when unsure. Only add the fields the screen actually shows or captures — do not add speculative fields; enrich later via `/attributes`.
210
179
 
@@ -52,14 +52,20 @@ Skip this step entirely when creating a brand-new node (no `nodeId` given) — g
52
52
  Write normal, full-size HTML/CSS for each page — as if designing a real webpage, not a tiny thumbnail. The canvas node renders this at a real page width and visually scales it down to fit, so there is no need to shrink font sizes or padding to fit a small box; design at a realistic scale (e.g. 16px body text, generous padding) and let the node handle the shrink.
53
53
 
54
54
  Guidelines:
55
- - **Keep it simple — this is a view-only mockup, not a working app.** No JavaScript, no interactivity beyond what a static page can show (enforced server-side, see below). Keep the CSS compact: inline styles or a handful of Bulma classes are enough — don't write a large embedded `<style>` block or reinvent what Bulma already gives you for free.
55
+ - **Keep it simple — this is a view-only mockup, not a working app.** No JavaScript, no interactivity beyond what a static page can show (enforced server-side, see below). Keep the CSS compact: inline styles or a handful of the screen's framework classes (see "CSS framework" below; Bulma by default) are enough — don't write a large embedded `<style>` block or reinvent what the framework already gives you for free.
56
56
  - Each page is one complete, standalone HTML fragment — not a `data-step` div nested inside a shared blob. A multi-step flow (e.g. cart → payment → confirmation) is three separate pages in the array, each fully self-contained.
57
- - **Each page may be at most 6000 characters** (the full markup string, including inline styles). The server rejects a longer page with `400 HTML_SCREEN_PAGE_TOO_LONG` (`context: {pageIndex, length, maxLength}`) and saves nothing — the render call fails as a whole. Check each page's length before rendering; if one is over, trim it (fewer inline styles, Bulma classes instead of custom CSS, fewer repeated sample rows) or split the content into another page. Adding a mark also counts toward the limit, since it adds attributes to the page's markup.
57
+ - **Each page may be at most 6000 characters** (the full markup string, including inline styles). The server rejects a longer page with `400 HTML_SCREEN_PAGE_TOO_LONG` (`context: {pageIndex, length, maxLength}`) and saves nothing — the render call fails as a whole. Check each page's length before rendering; if one is over, trim it (fewer inline styles, framework classes instead of custom CSS, fewer repeated sample rows) or split the content into another page. Adding a mark also counts toward the limit, since it adds attributes to the page's markup.
58
58
  - Inline styles (`style="..."`) are the simplest way to keep each page self-contained.
59
59
  - No `<script>` tags, no inline event handlers (`onclick`, `onload`, ...), no `javascript:` URIs — these are stripped server-side from every page before persisting regardless of what's sent. This is a static visual mockup, not an interactive prototype.
60
60
  - A real page background (e.g. a light gray full-bleed background behind a centered white card) reads more realistically than a bare form floating on white.
61
61
  - Don't add `<html>`/`<head>`/`<body>` tags to a page — every page is a body-only fragment. The canvas wraps each page in its own `<html><head>` (stylesheet + resize script) `<body>...</body></html>` at render time, so anything sent is placed inside that generated `<body>`.
62
- - Bulma CSS (0.9.4) is loaded by default in that `<head>` — classes like `title`, `button`, `is-primary`, `field`/`control`/`input` etc. all work out of the box, no need to write custom CSS for standard form/layout components. Note headings need a size modifier too, e.g. `class="title is-1"` — a bare `title` class alone is always 2rem regardless of the tag (`h1` vs `h2` etc.).
62
+ - **CSS framework — one per screen.** Each screen loads exactly one CSS framework (or none) into every page's `<head>`, chosen by the user in the HTML editor's header and stored on the node as `cssFrameworks` (an array holding one id: `"bulma"`, `"bootstrap"` or `"tailwind"`; `[]` = none). A screen where nothing was chosen gets **Bulma** — that is the default for new screens. Write markup for the framework the screen uses, and never mix classes from several frameworks in one screen:
63
+ - **Bulma 0.9.4** (default) — classes like `title`, `button`, `is-primary`, `field`/`control`/`input` work out of the box. Note headings need a size modifier too, e.g. `class="title is-1"` — a bare `title` class alone is always 2rem regardless of the tag (`h1` vs `h2` etc.).
64
+ - **Bootstrap 5.3.3** — CSS only (no JS, so no working dropdowns/modals/collapse): `container`, `row`/`col-*`, `card`, `btn btn-primary`, `form-control`, `table`, etc.
65
+ - **Tailwind 3.4.16** — utility classes only (`flex`, `p-6`, `bg-gray-100`, `rounded-lg`, ...), built in the page by the Tailwind Play script, so arbitrary values like `w-[320px]` work too. Don't write `@apply`, a `tailwind.config`, or any `<script>`/`<style type="text/tailwindcss">`; stick to utility classes.
66
+ - **None** — the page is unstyled apart from the browser defaults, so use inline styles for everything.
67
+
68
+ When a screen already exists (Step 2), keep using whichever framework its existing pages use — read `cssFrameworks` from the node's data if it's returned, otherwise infer it from the classes in `meta.pages`. Don't switch frameworks while editing; changing it is the user's choice in the editor. When creating a new screen from scratch, use Bulma unless the user names another framework.
63
69
 
64
70
  ### Marks — only when the user explicitly asks for one
65
71
 
@@ -155,19 +161,10 @@ Expect `204 No Content` on success from either curl call. A `HTML_SCREEN_PAGE_TO
155
161
 
156
162
  ## Step 5 — Define field data lineage (mandatory)
157
163
 
158
- Every screen — new or updated — needs `meta.fields`: one entry per piece of data the screen displays or captures, each with a `mapping` naming where that data comes from. A screen with only a title and no fields is an empty placeholder from a data-lineage standpoint, even if the mockup itself looks complete.
164
+ Every screen, new or updated, needs `meta.fields`: one entry per piece of data the screen displays or captures, each with a known source per `eventmodeling-information-completeness`. Input, session values and display-only calculations are `generated: true`. Displayed data names its read model (`mapping: "<ReadModelTitle>"`), even if that read model isn't on the board yet. This skill doesn't create read models or connections, but naming the source is not optional. A screen with only a title and no fields is an empty placeholder from a data-lineage standpoint, even if the mockup itself looks complete.
159
165
 
160
166
  **For a brand-new screen** (Step 4's `create_screen` call), pass `fields` directly on that call instead of a separate `node:changed` here — one round trip instead of two. This step's separate `submit_node_events` call is still how you set/update fields on an **existing** screen (`render_screen` doesn't take `fields` either).
161
167
 
162
- | Field type | `mapping` | Example |
163
- |---|---|---|
164
- | User types a value, sent as a command | `"<CommandTitle>.<fieldName>"` | `"ReserveBike.bikeId"` |
165
- | Read from session | `"session:<fieldName>"` | `"session:customerId"` |
166
- | Displayed data, sourced from a read model | `"<ReadModelTitle>.<fieldName>"` | `"ActiveReservationView.status"` |
167
- | Calculated/formatted only for display | `"derived:<expression>"` | `"derived:formatDuration(durationMinutes)"` |
168
-
169
- Name the read model even if it doesn't exist as a board node yet — this skill only renders the screen, it does not create READMODEL nodes or connections (that's `eventmodeling-identifying-outputs` or `place-element`, if the model is taken that far). But naming the source is **not optional**: a screen displaying data should almost never have a field with no mapping. If you can't say which read model a displayed field comes from, that's a sign the model is missing something — not a reason to skip the field.
170
-
171
168
  If this node is one of several sharing the same visual base with different marks/highlights (see "Marked screens and field scoping" above), only list the fields that fall inside *this* node's highlighted area — not every field the shared screen shows.
172
169
 
173
170
  Set `cardinality` too (`"Single"` unless the field is a repeated/list value), then push the fields onto the node:
@@ -180,7 +177,7 @@ mcp__eventmodelers__submit_node_events {
180
177
  "id": "<event-uuid>", "eventType": "node:changed", "nodeId": "<NODE_ID>",
181
178
  "changedAttributes": ["meta.fields"],
182
179
  "meta": { "type": "HTML_SCREEN", "fields": [
183
- {"name": "status", "type": "String", "example": "confirmed", "mapping": "ActiveReservationView.status", "cardinality": "Single"}
180
+ {"name": "status", "type": "String", "example": "confirmed", "mapping": "ActiveReservationView", "cardinality": "Single"}
184
181
  ] }
185
182
  }],
186
183
  "compact": true
@@ -43,7 +43,7 @@ curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/events" \
43
43
  "id": "<event-uuid>", "eventType": "node:changed", "nodeId": "<NODE_ID>",
44
44
  "changedAttributes": ["meta.fields"],
45
45
  "meta": { "type": "HTML_SCREEN", "fields": [
46
- {"name": "status", "type": "String", "example": "confirmed", "mapping": "ActiveReservationView.status", "cardinality": "Single"}
46
+ {"name": "status", "type": "String", "example": "confirmed", "mapping": "ActiveReservationView", "cardinality": "Single"}
47
47
  ] }
48
48
  }]'
49
49
  ```
@@ -55,7 +55,7 @@ mcp__eventmodelers__place_element {
55
55
  }
56
56
  ```
57
57
 
58
- Pass `fields` whenever the element's attributes are already known — they are written by this same call, so don't follow a placement with a `submit_node_events` just to set them. Placing **more than one** element is more entries in the same `elements` array, applied in order so each entry sees the columns the previous one added — one call for a whole slice's or column run's worth instead of one per element. **Order the array by narrative, left to right, one slice after another** (screen → command → event → the read model/view screen that event feeds → next slice) — never grouped by `elementType` (all commands, then all read models): by-type order makes every read model land in an inserted column after the fact and breaks the visual story.
58
+ Pass `fields` whenever the element's attributes are already known — they are written by this same call, so don't follow a placement with a `submit_node_events` just to set them. Every field you pass needs a known source per `eventmodeling-information-completeness`. A field without one is a gap to close upstream, not a field to place as is. Placing **more than one** element is more entries in the same `elements` array, applied in order so each entry sees the columns the previous one added — one call for a whole slice's or column run's worth instead of one per element. **Order the array by narrative, left to right, one slice after another** (screen → command → event → the read model/view screen that event feeds → next slice) — never grouped by `elementType` (all commands, then all read models): by-type order makes every read model land in an inserted column after the fact and breaks the visual story.
59
59
 
60
60
  This tool finds or creates an empty cell in the correct lane and places the node in one call — it collapses the "resolve timeline → fetch columns → determine lane → check occupancy → create node" sequence (Steps 2–3, 4, 6, 7b below) into a single round trip. A `columnIndex` past the timeline's current column count is handled automatically (columns are added to reach it) — no need to pre-check the column count or catch an out-of-range error yourself. If `timelineId` is unknown, resolve it first via Step 2's MCP call. Pass `compact: true` for a smaller `{nodeId, cellName, columnIndex}` response (plus `connectedCount` if auto-connect wired an edge) when you don't need the full `lane`/`elementType`/`title`/`autoConnected` detail back. Go straight to Step 8 once it returns.
61
61
 
@@ -110,7 +110,7 @@ mcp__eventmodelers__render_screen {
110
110
 
111
111
  ## Step 5 — Define field data lineage (mandatory)
112
112
 
113
- Every screen — new or updated — needs `meta.fields`: one entry per piece of data the screen displays or captures, each with a `mapping` naming where that data comes from. A screen with only a title and no fields is an empty placeholder from a data-lineage standpoint, even once the wireframe is rendered. See the `html-screen` skill's "Step 5 — Define field data lineage" for the full `mapping`-format table (command/session/read-model/derived forms).
113
+ Every screen — new or updated — needs `meta.fields`: one entry per piece of data the screen displays or captures, each with a known source per `eventmodeling-information-completeness`. A screen with only a title and no fields is an empty placeholder from a data-lineage standpoint, even once the wireframe is rendered.
114
114
 
115
115
  Name the read model even if it doesn't exist as a board node yet — this skill only renders the screen, it does not create READMODEL nodes or connections. But naming the source is **not optional**: a screen displaying data should almost never have a field with no mapping. If you can't say which read model a displayed field comes from, that's a sign the model is missing something — not a reason to skip the field.
116
116
 
@@ -124,7 +124,7 @@ mcp__eventmodelers__submit_node_events {
124
124
  "id": "<event-uuid>", "eventType": "node:changed", "nodeId": "<NODE_ID>",
125
125
  "changedAttributes": ["meta.fields"],
126
126
  "meta": { "type": "SCREEN", "fields": [
127
- {"name": "status", "type": "String", "example": "confirmed", "mapping": "ActiveReservationView.status", "cardinality": "Single"}
127
+ {"name": "status", "type": "String", "example": "confirmed", "mapping": "ActiveReservationView", "cardinality": "Single"}
128
128
  ] }
129
129
  }],
130
130
  "compact": true
@@ -35,7 +35,7 @@ curl -s -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/events" \
35
35
  "id": "<event-uuid>", "eventType": "node:changed", "nodeId": "<NODE_ID>",
36
36
  "changedAttributes": ["meta.fields"],
37
37
  "meta": { "type": "SCREEN", "fields": [
38
- {"name": "status", "type": "String", "example": "confirmed", "mapping": "ActiveReservationView.status", "cardinality": "Single"}
38
+ {"name": "status", "type": "String", "example": "confirmed", "mapping": "ActiveReservationView", "cardinality": "Single"}
39
39
  ] }
40
40
  }]'
41
41
  ```
@@ -100,6 +100,9 @@ exactly those elements — nothing else about the rule loosens. **No skill overr
100
100
  when there is no chat to ask in (a prompt that did not come from one, a standalone turn): then change
101
101
  nothing and comment which status blocked it. Where there is a chat, never comment — ask with the snippet.
102
102
 
103
+ **Every field you add or rename must have a known source. This holds in every skill and every turn, not only in a full modeling run.**
104
+ Follow `eventmodeling-information-completeness` before writing any field. It defines the three valid sources: a same-named upstream field, a `mapping`, or `generated: true`. A field with none of these is a gap to close upstream, never one to place unmapped.
105
+
103
106
  **One board read, shared by the whole turn.** Orientation first — `get_board_outline`, or `get_nodes` with
104
107
  `projection: "line"` — to establish where the work actually is, and only for the chapter this turn is about.
105
108
  In a `standalone=on` session the `SESSION_START` warm-up gave you the chapter list but no chapter's contents,
@@ -33,8 +33,8 @@ Read `.build-kit/.slices/{context}/{slicename}/slice.json`. Extract, and use **o
33
33
  - `commands[].fields[]` → Command record fields, in order
34
34
  - `events[].fields[]` → Event record fields, in order
35
35
  - `specifications[]` (GWT scenarios) → one test method per scenario
36
- - Which command field(s) have `idAttribute: true` — these become the tag(s) the command's Query and
37
- the resulting event are both scoped to (see Step 1/Step 3)
36
+ - The DCB tags — **both** the command field(s) with `idAttribute: true` **and** every field with
37
+ `tag: true` become tag(s) the command's Query and the resulting event are both scoped to (see Step 1/Step 3)
38
38
  - `storylines[]` (optional, may be absent) → narrated walkthroughs with ordered `elements[]`
39
39
  "beats"; see Step 7b for how a COMMAND beat in one of these can add a supplementary test
40
40