@eventmodelers/cli 1.0.36 → 1.0.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/shared/skills/learn-eventmodelers-api/SKILL.md +12 -10
- package/stacks/modeling-kit/templates/.claude/skills/add-next-slice/SKILL.md +2 -23
- package/stacks/modeling-kit/templates/.claude/skills/add-next-slice/references/api-fallback.md +11 -0
- package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/SKILL.md +6 -57
- package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/references/api-fallback.md +68 -0
- package/stacks/modeling-kit/templates/.claude/skills/attributes/SKILL.md +4 -61
- package/stacks/modeling-kit/templates/.claude/skills/attributes/references/api-fallback.md +39 -0
- package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/SKILL.md +9 -53
- package/stacks/modeling-kit/templates/.claude/skills/discover-storyboard/references/api-fallback.md +63 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-applying-conways-law/SKILL.md +9 -319
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-applying-conways-law/references/examples.md +329 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/SKILL.md +23 -199
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/references/api-fallback.md +97 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/references/examples.md +35 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/SKILL.md +13 -410
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/references/api-fallback.md +22 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/references/examples.md +397 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-automation-chains/SKILL.md +132 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-automation-chains/references/api-fallback.md +21 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-event-models/SKILL.md +9 -236
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-event-models/references/examples.md +257 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md +28 -302
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/references/api-fallback.md +31 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/references/examples.md +216 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/SKILL.md +30 -343
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/references/api-fallback.md +79 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/references/examples.md +282 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/SKILL.md +51 -400
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/references/api-fallback.md +67 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/references/examples.md +273 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/SKILL.md +45 -152
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/domain-patterns.md +49 -90
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/patterns.md +64 -137
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/SKILL.md +74 -65
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/references/api-fallback.md +51 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/SKILL.md +1 -5
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/references/api-fallback.md +10 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/SKILL.md +19 -36
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/api-fallback.md +41 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/examples.md +12 -9
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/references/patterns.md +1 -10
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/SKILL.md +26 -332
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/references/api-fallback.md +77 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/references/examples.md +271 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-translating-external-events/SKILL.md +9 -294
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-translating-external-events/references/examples.md +306 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/SKILL.md +12 -11
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/references/api-fallback.md +14 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/SKILL.md +6 -36
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/references/api-fallback.md +14 -0
- package/stacks/modeling-kit/templates/.claude/skills/examples/SKILL.md +3 -110
- package/stacks/modeling-kit/templates/.claude/skills/examples/references/api-fallback.md +118 -0
- package/stacks/modeling-kit/templates/.claude/skills/handle-comment/SKILL.md +5 -25
- package/stacks/modeling-kit/templates/.claude/skills/handle-comment/references/api-fallback.md +35 -0
- package/stacks/modeling-kit/templates/.claude/skills/html-screen/SKILL.md +9 -44
- package/stacks/modeling-kit/templates/.claude/skills/html-screen/references/api-fallback.md +51 -0
- package/stacks/modeling-kit/templates/.claude/skills/place-element/SKILL.md +23 -183
- package/stacks/modeling-kit/templates/.claude/skills/place-element/references/api-fallback.md +193 -0
- package/stacks/modeling-kit/templates/.claude/skills/storyboard/SKILL.md +14 -81
- package/stacks/modeling-kit/templates/.claude/skills/storyboard/references/api-fallback.md +74 -0
- package/stacks/modeling-kit/templates/.claude/skills/storyboard-screen/SKILL.md +4 -45
- package/stacks/modeling-kit/templates/.claude/skills/storyboard-screen/references/api-fallback.md +44 -0
- package/stacks/modeling-kit/templates/.claude/skills/timeline/SKILL.md +19 -88
- package/stacks/modeling-kit/templates/.claude/skills/timeline/references/api-fallback.md +91 -0
- package/stacks/modeling-kit/templates/.claude/skills/update-prompt-status/SKILL.md +1 -9
- package/stacks/modeling-kit/templates/.claude/skills/update-prompt-status/references/api-fallback.md +14 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-integrating-legacy-systems/SKILL.md +0 -674
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/snapshotting.md +0 -204
|
@@ -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
|
+
````
|
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/SKILL.md
CHANGED
|
@@ -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
|
|
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
|
-
|
|
50
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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**:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|