@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.
Files changed (69) hide show
  1. package/package.json +1 -1
  2. package/shared/skills/learn-eventmodelers-api/SKILL.md +12 -10
  3. package/stacks/modeling-kit/templates/.claude/skills/add-next-slice/SKILL.md +2 -23
  4. package/stacks/modeling-kit/templates/.claude/skills/add-next-slice/references/api-fallback.md +11 -0
  5. package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/SKILL.md +6 -57
  6. package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/references/api-fallback.md +68 -0
  7. package/stacks/modeling-kit/templates/.claude/skills/attributes/SKILL.md +4 -61
  8. package/stacks/modeling-kit/templates/.claude/skills/attributes/references/api-fallback.md +39 -0
  9. package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/SKILL.md +9 -53
  10. package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/references/api-fallback.md +63 -0
  11. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-applying-conways-law/SKILL.md +9 -319
  12. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-applying-conways-law/references/examples.md +329 -0
  13. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/SKILL.md +23 -199
  14. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/references/api-fallback.md +97 -0
  15. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/references/examples.md +35 -0
  16. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/SKILL.md +13 -410
  17. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/references/api-fallback.md +22 -0
  18. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/references/examples.md +397 -0
  19. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-automation-chains/SKILL.md +132 -0
  20. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-automation-chains/references/api-fallback.md +21 -0
  21. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-event-models/SKILL.md +9 -236
  22. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-event-models/references/examples.md +257 -0
  23. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md +28 -302
  24. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/references/api-fallback.md +31 -0
  25. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/references/examples.md +216 -0
  26. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/SKILL.md +30 -343
  27. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/references/api-fallback.md +79 -0
  28. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/references/examples.md +282 -0
  29. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/SKILL.md +51 -400
  30. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/references/api-fallback.md +67 -0
  31. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/references/examples.md +273 -0
  32. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/SKILL.md +45 -152
  33. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/domain-patterns.md +49 -90
  34. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/patterns.md +64 -137
  35. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/SKILL.md +74 -65
  36. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/references/api-fallback.md +51 -0
  37. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/SKILL.md +1 -5
  38. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/references/api-fallback.md +10 -0
  39. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/SKILL.md +19 -36
  40. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/api-fallback.md +41 -0
  41. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/examples.md +12 -9
  42. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/patterns.md +1 -10
  43. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/SKILL.md +26 -332
  44. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/references/api-fallback.md +77 -0
  45. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/references/examples.md +271 -0
  46. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-translating-external-events/SKILL.md +9 -294
  47. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-translating-external-events/references/examples.md +306 -0
  48. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/SKILL.md +12 -11
  49. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/references/api-fallback.md +14 -0
  50. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/SKILL.md +6 -36
  51. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/references/api-fallback.md +14 -0
  52. package/stacks/modeling-kit/templates/.claude/skills/examples/SKILL.md +3 -110
  53. package/stacks/modeling-kit/templates/.claude/skills/examples/references/api-fallback.md +118 -0
  54. package/stacks/modeling-kit/templates/.claude/skills/handle-comment/SKILL.md +5 -25
  55. package/stacks/modeling-kit/templates/.claude/skills/handle-comment/references/api-fallback.md +35 -0
  56. package/stacks/modeling-kit/templates/.claude/skills/html-screen/SKILL.md +9 -44
  57. package/stacks/modeling-kit/templates/.claude/skills/html-screen/references/api-fallback.md +51 -0
  58. package/stacks/modeling-kit/templates/.claude/skills/place-element/SKILL.md +23 -183
  59. package/stacks/modeling-kit/templates/.claude/skills/place-element/references/api-fallback.md +193 -0
  60. package/stacks/modeling-kit/templates/.claude/skills/storyboard/SKILL.md +14 -81
  61. package/stacks/modeling-kit/templates/.claude/skills/storyboard/references/api-fallback.md +74 -0
  62. package/stacks/modeling-kit/templates/.claude/skills/storyboard-screen/SKILL.md +4 -45
  63. package/stacks/modeling-kit/templates/.claude/skills/storyboard-screen/references/api-fallback.md +44 -0
  64. package/stacks/modeling-kit/templates/.claude/skills/timeline/SKILL.md +19 -88
  65. package/stacks/modeling-kit/templates/.claude/skills/timeline/references/api-fallback.md +91 -0
  66. package/stacks/modeling-kit/templates/.claude/skills/update-prompt-status/SKILL.md +1 -9
  67. package/stacks/modeling-kit/templates/.claude/skills/update-prompt-status/references/api-fallback.md +14 -0
  68. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-integrating-legacy-systems/SKILL.md +0 -674
  69. package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/snapshotting.md +0 -204
@@ -0,0 +1,306 @@
1
+ # Translating External Events — Worked Examples
2
+
3
+ These are conceptual worked examples of the translation reasoning this skill applies — useful for understanding the reasoning, but the actual mechanics are the numbered Workflow steps in the main SKILL.md.
4
+
5
+ ## 1. Identify External Event Sources
6
+
7
+ ```
8
+ External System: Payment Gateway (Stripe)
9
+
10
+ Events received:
11
+ - charge.succeeded
12
+ - charge.failed
13
+ - charge.refunded
14
+ - charge.dispute.created
15
+
16
+ Example payload: charge.succeeded
17
+ {
18
+ "id": "ch_1234567890",
19
+ "amount": 15000,
20
+ "currency": "usd",
21
+ "customer": "cus_9876543210",
22
+ "status": "succeeded",
23
+ "created": 1640995200
24
+ }
25
+
26
+ External System: GPS Location Service (Google Maps)
27
+
28
+ Events received:
29
+ - location_update
30
+ - geofence_enter
31
+ - geofence_exit
32
+
33
+ Example payload: geofence_exit
34
+ {
35
+ "userId": "user-123",
36
+ "geoFenceId": "hotel-front-entrance",
37
+ "timestamp": 1640995200,
38
+ "latitude": 40.7128,
39
+ "longitude": -74.0060
40
+ }
41
+ ```
42
+
43
+ ## 2. Analyze Technical Representation
44
+
45
+ ```
46
+ External Event: charge.succeeded (Stripe)
47
+
48
+ Technical fields:
49
+ - id: UUID of charge in Stripe (not meaningful to us)
50
+ - amount: Integer cents (15000 = $150.00)
51
+ - currency: ISO code ("usd")
52
+ - customer: Stripe customer ID (not our customer ID)
53
+ - status: String indicating success
54
+ - created: Unix timestamp
55
+
56
+ Problems with using directly:
57
+ We don't use Stripe customer IDs (we have our own customer IDs)
58
+ Currency and amount require interpretation
59
+ Status is one field in their model, we care about the fact it succeeded
60
+ Stripe charge ID isn't the same as our order ID
61
+ We need to correlate back to our Order stream
62
+ ```
63
+
64
+ ## 3. Define Domain Translation Rules
65
+
66
+ ```
67
+ Translation: External charge.succeeded → Domain PaymentAuthorized
68
+
69
+ Mapping rules:
70
+ charge.id (Stripe) → paymentGatewayRef (store for reconciliation, don't use as primary)
71
+ charge.customer (Stripe) → Look up: Which of OUR customers has this Stripe ID?
72
+ charge.amount → paymentAmount (convert from cents)
73
+ charge.currency → paymentCurrency
74
+ created → timestamp
75
+ [NEED TO FIND] → orderId (Stripe doesn't tell us! This is critical—how do we know which order?)
76
+
77
+ Problem identified:
78
+ Stripe webhook comes with charge details but NOT our order ID.
79
+
80
+ Solutions:
81
+ A. Store Stripe charge ID in our Order when we initiate payment
82
+ When webhook arrives: charge.id → Look up in OrderPaymentReference
83
+ Find orderId → Create PaymentAuthorized event
84
+
85
+ B. Store custom metadata in Stripe charge
86
+ When creating charge: Include our orderId in metadata
87
+ When webhook arrives: Extract orderId from metadata
88
+
89
+ Choose A or B based on Stripe integration approach.
90
+ ```
91
+
92
+ ## 4. Handle Correlation
93
+
94
+ ```
95
+ Pattern: Correlation via Reference Tracking
96
+
97
+ Our system flow:
98
+ 1. Order created in our system: order-123
99
+ 2. We initiate payment with Stripe:
100
+ - Send amount, customer info
101
+ - Receive charge ID: ch_1234567890
102
+ - Store reference: OrderPaymentReference { orderId: order-123, stripeChargeId: ch_1234567890 }
103
+
104
+ When webhook arrives:
105
+ 1. Webhook: charge.succeeded { id: ch_1234567890, amount: 15000, ... }
106
+ 2. Look up: Find OrderPaymentReference where stripeChargeId = ch_1234567890
107
+ 3. Get orderId from reference
108
+ 4. Create PaymentAuthorized event: { orderId: order-123, amount: 150.00, ... }
109
+
110
+ Key insight: You must create the correlation bridge when initiating external action.
111
+ ```
112
+
113
+ ## 5. Define Translation Scenarios
114
+
115
+ ```
116
+ External Event: charge.succeeded
117
+ Trigger: Stripe webhook arrives with charge details
118
+ Precondition: OrderPaymentReference exists for this charge ID
119
+ Translation logic:
120
+ 1. Extract charge.id from webhook
121
+ 2. Look up OrderPaymentReference.orderId
122
+ 3. Validate order exists and is in Confirmed state
123
+ 4. Create domain event: PaymentAuthorized { orderId, amount, timestamp, ... }
124
+ Success: Domain event produced
125
+ Failure scenarios:
126
+ - Charge ID not found in references → Log error, don't produce event (manual review)
127
+ - Order not in Confirmed state → Log error, don't produce event
128
+ - Duplicate webhook → Idempotent handling (check if event already exists)
129
+
130
+ --- External Event: geofence_exit
131
+ Trigger: Guest leaves hotel area (GPS geofence)
132
+ Precondition: Guest has opted in to location tracking
133
+ Translation logic:
134
+ 1. Extract userId and geoFenceId from webhook
135
+ 2. Validate guest is currently in hotel
136
+ 3. Check geofence_exit is "hotel-front-entrance" (not just any geofence)
137
+ 4. Create domain event: GuestLeftHotel { guestId: userId, timestamp, ... }
138
+ Success: Domain event produced
139
+ Failure scenarios:
140
+ - Guest hasn't opted in → Don't produce event (respect privacy)
141
+ - Guest not checked in → Don't produce event (shouldn't be in geofence)
142
+ - Unknown geofence → Log error, don't produce event
143
+ ```
144
+
145
+ ## 6. Handle Duplicates and Ordering
146
+
147
+ ```
148
+ Problem: Stripe retries charge.succeeded webhook
149
+ Webhook 1: charge.succeeded { id: ch_123 } → Arrives at 10:00 AM
150
+ Webhook 2: charge.succeeded { id: ch_123 } → Arrives at 10:05 AM (retry)
151
+
152
+ Solution: Idempotent translation
153
+
154
+ Check before creating event:
155
+ 1. Extract external ID: ch_123
156
+ 2. Query: Does PaymentAuthorized event exist with paymentGatewayRef = ch_123?
157
+ 3. If yes: Do nothing (already processed)
158
+ 4. If no: Create event
159
+
160
+ This requires storing the external ID in the event:
161
+ PaymentAuthorized event {
162
+ orderId: order-123,
163
+ amount: 150.00,
164
+ paymentGatewayRef: ch_123, ← Store external ID for deduplication
165
+ ...
166
+ }
167
+ ```
168
+
169
+ ## 7. Handle Partial or Missing Information
170
+
171
+ ```
172
+ External Event: geofence_exit
173
+
174
+ Available data:
175
+ - userId
176
+ - geoFenceId
177
+ - timestamp
178
+ - latitude, longitude (raw GPS)
179
+
180
+ Missing data:
181
+ - Guest name (not in webhook payload)
182
+ - Reason for leaving (not tracked)
183
+ - Expected return time (not available)
184
+
185
+ Handling strategy:
186
+ A. Translation enriches from our system:
187
+ Domain event: GuestLeftHotel {
188
+ guestId: userId, ← From webhook
189
+ timestamp: ..., ← From webhook
190
+ guestName: "John Smith", ← Looked up from guest stream
191
+ roomNumber: "502", ← Looked up from guest stream
192
+ geoFenceId: "front-entrance" ← From webhook
193
+ }
194
+
195
+ B. Some data we don't need:
196
+ We ignore: latitude, longitude (we just care that guest left)
197
+
198
+ C. Some data we can infer:
199
+ We can assume: Guest is now outside hotel
200
+ Cleaning crew can visit room
201
+ ```
202
+
203
+ ## Legacy markdown structure (superseded — kept for reference only)
204
+
205
+ Older versions of this skill wrote the translation analysis as a standalone markdown document. The actual output mechanism today is placing each translated domain event on the board (see "Output Format" in the main SKILL.md) — this template is kept only so the shape of the information (what a complete translation write-up covers) stays documented somewhere.
206
+
207
+ ````markdown
208
+ # External Event Translation: [Domain Name]
209
+
210
+ ## External Systems & Events
211
+
212
+ ### System: [External System Name]
213
+
214
+ **Connection Type**: [Webhook/API polling/WebSocket/Streaming]
215
+
216
+ **Events Received**:
217
+ - event1_name
218
+ - event2_name
219
+ - event3_name
220
+
221
+ ---
222
+
223
+ ## Translation Rules
224
+
225
+ ### External Event: [Event Name]
226
+
227
+ **Source System**: [System name]
228
+
229
+ **Technical Representation**:
230
+ ```json
231
+ {
232
+ "field1": "value",
233
+ "field2": "value"
234
+ }
235
+ ```
236
+
237
+ **Domain Translation**:
238
+ | External Field | Our Field | Mapping | Notes |
239
+ |---|---|---|---|
240
+ | externalId | n/a | Stored for deduplication | Reference only |
241
+ | customer | [lookup] | Look up our customer ID | Must correlate |
242
+
243
+ **Correlation Method**:
244
+ [How do we link back to our domain entities?]
245
+
246
+ **Domain Event Produced**:
247
+ - Event Name: [EventName]
248
+ - Fields: [List with sources]
249
+
250
+ **Translation Logic**:
251
+ ```
252
+ 1. Extract from webhook
253
+ 2. Validate preconditions
254
+ 3. Enrich from our system
255
+ 4. Create domain event
256
+ ```
257
+
258
+ **Success Scenario**:
259
+ [What success looks like]
260
+
261
+ **Failure Scenarios**:
262
+ - Scenario 1: Consequence
263
+ - Scenario 2: Consequence
264
+
265
+ **Duplicate Handling**: [Idempotent strategy]
266
+
267
+ --- [Repeat for each external event]
268
+
269
+ ---
270
+
271
+ ## Correlation Reference
272
+
273
+ Track how external IDs map to our domain:
274
+
275
+ | Our Entity | External System | External ID Field | Storage | Lookup |
276
+ |---|---|---|---|---|
277
+ | Order | Stripe | charge.id | OrderPaymentReference | By charge ID |
278
+ | Guest | GPS Service | userId | Guest stream | By userId |
279
+
280
+ ---
281
+
282
+ ## Failure & Recovery
283
+
284
+ ### Webhook Arrives for Non-existent Order
285
+ **Symptom**: Stripe sends charge.succeeded for unknown order
286
+ **Cause**: Race condition or data inconsistency
287
+ **Detection**: OrderPaymentReference lookup returns nothing
288
+ **Recovery**: Log error, queue for manual review
289
+
290
+ ### Duplicate Webhooks
291
+ **Symptom**: Same webhook received multiple times
292
+ **Cause**: Stripe retry mechanism or network duplication
293
+ **Detection**: Domain event already exists with same externalRef
294
+ **Recovery**: Idempotent check prevents duplicate event creation
295
+
296
+ ---
297
+
298
+ ## Testing Recommendations
299
+
300
+ - [ ] Test happy path: External event → Correct domain event
301
+ - [ ] Test missing correlation: External event arrives before our order created
302
+ - [ ] Test duplicate: Same webhook processed twice
303
+ - [ ] Test invalid data: Webhook with missing required fields
304
+ - [ ] Test partial data: Webhook with some fields missing
305
+ - [ ] Test ordering: Multiple webhooks arrive out of order
306
+ ````
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: eventmodeling-validating-event-models
3
- description: "Step 9 of Event Modeling - Validate event-sourced models for completeness, consistency, and event sourcing principles. Ensures events are immutable facts, state projections are deterministic, and commands are pure. Identifies gaps and suggests improvements before code generation. Use when reviewing models before code generation. Do not use for: the structured 23-check production checklist (use eventmodeling-validating-event-models-checklist) or field-level completeness verification (use eventmodeling-checking-completeness)."
3
+ description: "Step 9 of Event Modeling - Validate event-sourced models for completeness, consistency, and event sourcing principles. Ensures events are immutable facts, state projections are deterministic, and commands are pure. Identifies gaps and suggests improvements before code generation. Use when reviewing models before code generation. Do not use for: the structured 17-check production checklist (use eventmodeling-validating-event-models-checklist) or field-level completeness verification (use eventmodeling-checking-completeness)."
4
4
  allowed-tools:
5
5
  - Write
6
6
  - Bash
@@ -33,16 +33,7 @@ mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "COMMAND" }
33
33
  mcp__eventmodelers__get_nodes { "boardId": "$BOARD_ID", "type": "READMODEL" }
34
34
  ```
35
35
 
36
- **Fallback (no MCP):**
37
-
38
- ```bash
39
- curl -s -H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" \
40
- "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=EVENT"
41
- curl -s -H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" \
42
- "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=COMMAND"
43
- curl -s -H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" \
44
- "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=READMODEL"
45
- ```
36
+ **Fallback (no MCP):** see `references/api-fallback.md` — "Board Context".
46
37
 
47
38
  After validation, use the `handle-comment` skill to post findings on the relevant nodes — `TASK` for critical violations that must be fixed, `QUESTION` for warnings and recommendations. (That skill already handles the `add_comment` MCP-vs-curl choice internally — no separate rewrite needed here.)
48
39
 
@@ -53,6 +44,16 @@ Ensures event-sourced models are complete, correct, and follow pure event sourci
53
44
 
54
45
  When given an event model, perform comprehensive validation:
55
46
 
47
+ ### 0. Automated structural pass — run `validate_model` first
48
+
49
+ Before the manual checks below, run the server-side checklist once per chapter:
50
+
51
+ ```
52
+ mcp__eventmodelers__validate_model { "boardId": "$BOARD_ID", "chapterId": "<chapterId>" }
53
+ ```
54
+
55
+ It returns a compact `findings` list (no node dumps) covering: unplaced nodes, backward arrows among the forward-only pairs (with the todo-list `EVENT → READMODEL` exception already applied), COMMANDs with zero or multiple issuers, READMODELs with no inbound EVENT, columns with more than one screen, and COMMAND/READMODEL columns with no SCENARIO. This replaces the per-type `get_nodes` scans and the `get_node` `projection: "edges"` spot-checks those manual checks would otherwise need — start from its `findings`, then use the sections below for the semantic checks it can't make (naming, immutability, field-source traceability, scenario coverage depth). A `verdict` of `PASS` on `validate_model` is necessary but not sufficient — still do the semantic pass.
56
+
56
57
  ### 1. Swimlane Completeness Check
57
58
 
58
59
  Verify each swimlane has:
@@ -0,0 +1,14 @@
1
+ # Validating Event Models — curl Fallback Calls
2
+
3
+ Only needed when MCP is not connected. Every call below has an MCP equivalent in the main SKILL.md — always prefer that.
4
+
5
+ ## Board Context
6
+
7
+ ```bash
8
+ curl -s -H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" \
9
+ "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=EVENT"
10
+ curl -s -H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" \
11
+ "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=COMMAND"
12
+ curl -s -H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" \
13
+ "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=READMODEL"
14
+ ```
@@ -45,15 +45,9 @@ mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "COMMAND" }
45
45
  mcp__eventmodelers__get_nodes { "boardId": "<BOARD_ID>", "type": "READMODEL" }
46
46
  ```
47
47
 
48
- **Fallback (no MCP):**
49
- ```bash
50
- curl -s -H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" \
51
- "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=EVENT"
52
- curl -s -H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" \
53
- "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=COMMAND"
54
- curl -s -H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" \
55
- "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=READMODEL"
56
- ```
48
+ **Fallback (no MCP):** see `references/api-fallback.md` — "Board Context".
49
+
50
+ Also run `validate_model` (`{boardId, chapterId}`) once per chapter up front — several checklist items (Command With Multiple Issuers in Phase 1, backward-arrow / event-flow in Phase 6, read-model-source distinctions in Phase 3) have a purely structural half the tool answers directly, so start each of those from its `findings` and spend the manual effort on the semantic judgement the tool can't make.
57
51
 
58
52
  Use the board nodes as the model input. After the checklist, use `handle-comment` to post `TASK` comments on nodes that fail checks (that skill covers the MCP `add_comment`/curl choice for comment-posting itself).
59
53
 
@@ -154,20 +148,8 @@ CalculationPerformed {
154
148
  **Why**: Calculations change multiple times as source data changes. Events are immutable.
155
149
 
156
150
  ### 2. Shared vs Handler-Owned State
157
- ```
158
- ANTI-PATTERN:
159
- One shared "OrderAggregate" class used by all handlers
160
- - ConfirmOrderHandler shares OrderAggregate state
161
- - ShipOrderHandler modifies same OrderAggregate
162
- - Result: Tight coupling, hard to parallelize
163
151
 
164
- CORRECT:
165
- Each handler owns its own [CommandHandler]State class
166
- - ConfirmOrderState (only ConfirmOrderHandler uses)
167
- - ShipOrderState (only ShipOrderHandler uses)
168
- - CancelOrderState (only CancelOrderHandler uses)
169
- - All reconstruct state from same events, but independently
170
- ```
152
+ The DDD-aggregate anti-pattern — one shared state class used by every handler — versus the correct pattern of one minimal, handler-owned state class per command. See `eventmodeling-designing-event-models`'s "Core Architectural Rule" for the full worked example (`OrderAggregate` vs. per-command state).
171
153
 
172
154
  **Why**: Each handler is a micro-slice. Separate state classes maintain isolation, enable parallel teams, prevent merge conflicts.
173
155
 
@@ -206,22 +188,10 @@ Reconstruct [CommandHandler]State on-demand
206
188
  **Why**: State is derived from events, never stored. Events are source of truth. This enables consistent replay, audit trails, and time-travel debugging.
207
189
 
208
190
  ### 5. Command With Multiple Issuers
209
- ```
210
- ANTI-PATTERN:
211
- FlagLoanOverdue (COMMAND) has two inbound edges:
212
- - "Flag Overdue Loans" (AUTOMATION, same column)
213
- - "Adjust Due Date" (SCREEN, previous column)
214
- - Result: unclear who/what actually triggers the command; validation and UI-vs-automation
215
- authority checks (e.g. Role & Actor Attribution) can no longer be answered
216
191
 
217
- CORRECT:
218
- FlagLoanOverdue (COMMAND) has exactly one inbound edge, from the AUTOMATION that owns it.
219
- If the SCREEN's user genuinely needs to trigger the same outcome, that's a second,
220
- distinctly-named command (or the SCREEN issuing it directly, with the automation removed) —
221
- not two issuers sharing one command.
222
- ```
192
+ A command is never issued by more than one thing — a COMMAND with 2+ inbound SCREEN/AUTOMATION edges hides which actor is actually responsible. See `place-element`'s Step 7c for the full check-and-fix mechanics (which edge to keep, how to remove the rest).
223
193
 
224
- **Why**: A command is never issued by more than one thing. Each command represents one specific trigger's decision to act — collapsing two triggers onto one command node hides which actor is actually responsible, and usually means either a naming/slice-boundary mistake or a stray manual/pre-existing edge (auto-connect itself now guards against this — see `learn-eventmodelers-api` §3). Fix by removing the extra edge via `set_connection` (`action: "remove"`), not by keeping both.
194
+ **Why**: Each command represents one specific trigger's decision to act — collapsing two triggers onto one command node usually means either a naming/slice-boundary mistake or a stray manual/pre-existing edge (auto-connect itself now guards against fresh occurrences — see `learn-eventmodelers-api` §3).
225
195
 
226
196
  ---
227
197
 
@@ -0,0 +1,14 @@
1
+ # Event Model Validation Checklist Skill — curl Fallback Calls
2
+
3
+ Only needed when MCP is not connected. Every call below has an MCP equivalent in the main SKILL.md — always prefer that.
4
+
5
+ ## Board Context
6
+
7
+ ```bash
8
+ curl -s -H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" \
9
+ "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=EVENT"
10
+ curl -s -H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" \
11
+ "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=COMMAND"
12
+ curl -s -H "x-token: $TOKEN" -H "x-board-id: $BOARD_ID" \
13
+ "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=READMODEL"
14
+ ```
@@ -45,93 +45,11 @@ mcp__eventmodelers__add_field_examples { "boardId": "$BOARD_ID", "cellName": "<t
45
45
 
46
46
  If this succeeds, skip straight to Step 6 (report back), describing the fields the tool reports as changed. Use the manual fallback flow below (Steps 2–5) only if MCP isn't connected.
47
47
 
48
- ### Fallback (no MCP) — Step 2: Resolve the element
49
-
50
- Try the resolution strategies in order until one succeeds.
51
-
52
- ### 2a — UUID
53
- If `target` looks like a UUID (pattern `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`), fetch it directly:
54
-
55
- ```bash
56
- curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$target" \
57
- -H "x-user-id: examples-skill"
58
- ```
59
-
60
- ### 2b — Name search
61
- If `target` is not a UUID, search by name:
62
-
63
- ```bash
64
- curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/events/search?name=$(python3 -c 'import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1]))' "$target")" \
65
- -H "x-user-id: examples-skill"
66
- ```
67
-
68
- Pick the best match (exact title match preferred; case-insensitive). If multiple matches exist, list them and ask the user to pick one.
69
-
70
- ### 2c — Cell name (spreadsheet-style, e.g. "B3")
71
- If `target` matches the pattern `[A-Z]+[0-9]+`:
72
-
73
- 1. Fetch all chapters on the board to find the relevant timeline:
74
-
75
- ```bash
76
- curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?type=CHAPTER" \
77
- -H "x-user-id: examples-skill"
78
- ```
79
-
80
- If multiple chapters exist, ask the user which one to use.
81
-
82
- 2. Fetch the chapter **fresh** to decode the grid — never use cached chapter data, as cells are updated frequently:
83
-
84
- ```bash
85
- curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/$CHAPTER_ID" \
86
- -H "x-user-id: examples-skill"
87
- ```
88
-
89
- From `meta.timelineData`, decode the cell name into a `cellId`:
90
- - Column letter(s) → 0-based column index (A=0, B=1, … Z=25, AA=26, …)
91
- - Row digit → 0-based row index (1→0, 2→1, …)
92
- - Find the matching column in `columns` and the matching row in `rows`.
93
- - Compute: **`CELL_ID = row.id + "-" + column.id`** (cell IDs are always `<rowId>-<columnId>`).
94
-
95
- 3. **Always fetch the cell live** to get the current node list — do not rely on the `nodeId` in the chapter's cell data, as it may be stale. No MCP equivalent: `get_nodes` only filters by `type`, not `cellId`:
96
-
97
- ```bash
98
- curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes?cellId=$CELL_ID" \
99
- -H "x-user-id: examples-skill"
100
- ```
101
-
102
- Use the first non-CHAPTER result (filter out CHAPTER type).
103
-
104
- If no element is found after all strategies, stop and tell the user what was tried.
105
-
106
- Save the resolved node as `TARGET_NODE` (full JSON including `id`, `meta`, `edges`).
48
+ **Fallback (no MCP):** see `references/api-fallback.md` "Step 2 Resolve the element".
107
49
 
108
50
  ---
109
51
 
110
- ### Fallback (no MCP) — Step 3: Load linked elements for context
111
-
112
- Collect nearby elements to understand the domain context and generate consistent examples. **Never fetch all board nodes.** Only fetch specific nodes you already have IDs for.
113
-
114
- ### 3a — Nodes from edges
115
- If `TARGET_NODE.edges` is non-empty, fetch each connected node individually by its ID:
116
-
117
- ```bash
118
- curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/<EDGE_NODE_ID>" \
119
- -H "x-user-id: examples-skill"
120
- ```
121
-
122
- Fetch all edge-connected nodes in parallel (one request per node ID).
123
-
124
- ### 3b — Nodes from the same column (cell-based resolution only)
125
- If you resolved `TARGET_NODE` via a cell name (Step 2c), you already have the full chapter `cells` array in memory. Use it — **no extra API call needed**:
126
-
127
- - Find all cells that share the same `colId` as `TARGET_NODE`'s cell.
128
- - Collect their `nodeId` values (skip the target itself and any cell without a `nodeId`).
129
- - Fetch each of those nodes individually by ID (in parallel):
130
-
131
- ```bash
132
- curl -s "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/<NODE_ID>" \
133
- -H "x-user-id: examples-skill"
134
- ```
52
+ **Fallback (no MCP):** see `references/api-fallback.md` "Step 3 Load linked elements for context".
135
53
 
136
54
  ### 3c — Read neighbour fields
137
55
  For each neighbour element collected above (COMMAND, EVENT, READMODEL), extract its `meta.fields` and their existing `example` values. This gives you a pool of consistent values to reuse — e.g. if a COMMAND already has `email: "jane@example.com"`, use the same email in the linked EVENT.
@@ -183,32 +101,7 @@ mcp__eventmodelers__submit_node_events {
183
101
  }
184
102
  ```
185
103
 
186
- **Fallback (no MCP at all)**build the payload with Python to avoid shell JSON-escaping issues, then POST it:
187
-
188
- ```bash
189
- python3 - <<EOF > /tmp/examples_payload.json
190
- import json, time, uuid
191
- payload = [{
192
- "id": str(uuid.uuid4()),
193
- "eventType": "node:changed",
194
- "nodeId": "<TARGET_NODE.id>",
195
- "boardId": "<BOARD_ID>",
196
- "timestamp": int(time.time() * 1000),
197
- "changedAttributes": ["meta.fields"],
198
- "meta": {
199
- "fields": <updated-fields-array as Python list>
200
- }
201
- }]
202
- print(json.dumps(payload))
203
- EOF
204
-
205
- curl -s -w "\n%{http_code}" -X POST "$BASE_URL/api/org/$ORG_ID/boards/$BOARD_ID/nodes/events" \
206
- -H "Content-Type: application/json" \
207
- -H "x-token: $TOKEN" \
208
- -H "x-board-id: $BOARD_ID" \
209
- -H "x-user-id: examples-skill" \
210
- --data-binary @/tmp/examples_payload.json
211
- ```
104
+ **Fallback (no MCP):** see `references/api-fallback.md`"Step 5 Write the update".
212
105
 
213
106
  Verify the response is HTTP 200. If it fails, report the error and stop.
214
107