@eventmodelers/cli 1.0.37 → 1.0.39
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 +10 -3
- package/stacks/modeling-kit/templates/.claude/skills/analyze-existing-model/SKILL.md +16 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-applying-conways-law/SKILL.md +3 -61
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/SKILL.md +24 -93
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/SKILL.md +2 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-core-rules/SKILL.md +192 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-automation-chains/SKILL.md +23 -7
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-event-models/SKILL.md +50 -223
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-designing-event-models/references/examples.md +30 -120
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md +3 -78
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-inputs/SKILL.md +3 -50
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-identifying-outputs/SKILL.md +3 -54
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-interview-protocol/SKILL.md +31 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/SKILL.md +27 -67
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/domain-patterns.md +15 -15
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-optimizing-stream-design/references/patterns.md +44 -52
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/SKILL.md +14 -15
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-plotting-events/SKILL.md +4 -0
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-slicing-event-models/SKILL.md +3 -15
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-storyboarding-events/SKILL.md +3 -50
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-translating-external-events/SKILL.md +4 -56
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models/SKILL.md +58 -97
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-validating-event-models-checklist/SKILL.md +84 -118
- package/stacks/modeling-kit/templates/.claude/skills/place-element/SKILL.md +15 -1
- package/stacks/modeling-kit/templates/.claude/skills/place-element/references/api-fallback.md +11 -1
- package/stacks/modeling-kit/templates/.claude/skills/wdyt/SKILL.md +8 -6
package/package.json
CHANGED
|
@@ -68,7 +68,7 @@ Server name: `eventmodelers`. Every tool takes `boardId` explicitly; none need `
|
|
|
68
68
|
|
|
69
69
|
**Not exposed via MCP at all** — always use REST/curl for these: §7 Config Import, §10 Snapshots, §11–12 User Management, §13 Utility (`/api/user`, swagger), and the rest of §14 Prompts (submission, claiming, deletion, realtime-token) — only the status-update endpoint has an MCP tool (`update_prompt_status`, used by the `update-prompt-status` skill); everything else in Prompts is an intentionally separate lifecycle the board-content MCP server doesn't otherwise own.
|
|
70
70
|
|
|
71
|
-
**Capabilities with no direct MCP filter** — e.g. REST's `GET .../nodes?cellId=<id>` (§3)
|
|
71
|
+
**Capabilities with no direct MCP filter** — e.g. REST's `GET .../nodes?cellId=<id>&timelineId=<id>` and `?colId=<id>&timelineId=<id>` (§3) have no equivalent params on `get_nodes`. Either call the REST endpoint directly, or get the same answer by calling `get_node` on the CHAPTER and reading `meta.timelineData.cells` (sparse array; a cell absent from it is empty) instead of asking the server to filter by cell/column.
|
|
72
72
|
|
|
73
73
|
---
|
|
74
74
|
|
|
@@ -379,8 +379,15 @@ interface NodeChangeEvent {
|
|
|
379
379
|
### GET `/api/org/:orgId/boards/:boardId/nodes`
|
|
380
380
|
List all nodes on a board.
|
|
381
381
|
|
|
382
|
-
**Query params**:
|
|
383
|
-
|
|
382
|
+
**Query params**:
|
|
383
|
+
- `type?: ElementType` — exact match.
|
|
384
|
+
- `cellId?: string` — return only the node occupying this timeline cell (format `<rowId>-<colId>`). Requires `timelineId`. Empty array if the cell is unoccupied.
|
|
385
|
+
- `colId?: string` — return every node occupying this column, across *all* rows of the timeline (e.g. to check for an existing COMMAND/READMODEL/SCREEN/AUTOMATION before placing one, since the server caps those at one per column even across separate lane rows). Requires `timelineId`. Combine with `type` to narrow to one element type.
|
|
386
|
+
- `timelineId?: string` — the CHAPTER node `cellId`/`colId` are resolved against. Required together with either of those two; `400` if omitted.
|
|
387
|
+
|
|
388
|
+
Cell/column occupancy lives only in the CHAPTER node's `meta.timelineData.cells`, never on the node rows themselves — this endpoint resolves `cellId`/`colId` against that timeline internally so callers don't have to fetch and parse the whole chapter node just to check occupancy.
|
|
389
|
+
|
|
390
|
+
**Response**: `200` — node record array. `400` — `cellId`/`colId` given without `timelineId`. `404` — `timelineId` doesn't reference an existing CHAPTER node with grid data.
|
|
384
391
|
|
|
385
392
|
---
|
|
386
393
|
|
|
@@ -119,6 +119,15 @@ Do not flag gaps that are clearly intentional (e.g. a slice named "Internal" wit
|
|
|
119
119
|
|
|
120
120
|
Check whether any EVENT, COMMAND, or READMODEL appears in zero slices (present on the board but not wired into any slice boundary). If the slicedata API does not expose this directly, skip this check and note it.
|
|
121
121
|
|
|
122
|
+
### 5f — Structural shapes
|
|
123
|
+
|
|
124
|
+
Check the element graph gathered in Step 4 against the four shapes defined in `eventmodeling-core-rules`'s **Structural Shapes** section (the bed, left chair, right chair, shelf) — read that section for the full definitions and thresholds; this step only covers how to report them here.
|
|
125
|
+
|
|
126
|
+
- **The bed** is a real anti-pattern — always report every instance found (a SCREEN wired to more than one COMMAND), by slice/screen name.
|
|
127
|
+
- **The left chair, right chair, and shelf** are candidates — only report an instance if, after reasoning about the actual events/fields/scenarios involved, the count still looks like it's doing more than one job in this domain. Don't report a raw count crossing the threshold on its own.
|
|
128
|
+
|
|
129
|
+
This is a read-only structural read like the rest of Step 5 — report findings, never fix them here.
|
|
130
|
+
|
|
122
131
|
---
|
|
123
132
|
|
|
124
133
|
## Step 6 — Report to the user
|
|
@@ -172,6 +181,13 @@ Total: <n> slices
|
|
|
172
181
|
|
|
173
182
|
---
|
|
174
183
|
|
|
184
|
+
### Structural Shapes
|
|
185
|
+
<always list every "the bed" instance found — screen name + the commands it fans into>
|
|
186
|
+
<list a left chair/right chair/shelf instance only if it held up after reasoning about the domain>
|
|
187
|
+
(none) if nothing found
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
175
191
|
### Summary
|
|
176
192
|
<2–4 sentences: overall model maturity, the most important gap or risk, one concrete suggestion>
|
|
177
193
|
```
|
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-applying-conways-law/SKILL.md
CHANGED
|
@@ -11,16 +11,14 @@ allowed-tools:
|
|
|
11
11
|
|
|
12
12
|
> **Before doing anything else**, invoke the `connect` skill — if not already connected — to resolve `TOKEN`, `BOARD_ID`, `ORG_ID`, and `BASE_URL`. Do not proceed until it has completed. Consult `learn-eventmodelers-api` only if you need to look up a specific endpoint or field this file doesn't cover — don't load it eagerly.
|
|
13
13
|
|
|
14
|
+
This step applies the shared element rules in **`eventmodeling-core-rules`** — read it once per session if you haven't already; it defines what a COMMAND/EVENT/READMODEL/SCREEN/AUTOMATION is, how each is named, and the anti-patterns to reject, so this step doesn't restate them.
|
|
15
|
+
|
|
14
16
|
## Interview Phase (Optional)
|
|
15
17
|
|
|
16
18
|
**When to Interview**: Skip if the user has already specified: existing team structure, team responsibilities, and autonomous boundary preferences. Interview when team structure is unclear or organizational alignment hasn't been discussed.
|
|
17
19
|
|
|
18
|
-
**Interview Strategy**: Understand team organization and decision-making to design system boundaries that teams can own independently. Misalignment here creates bottlenecks and tight coupling later.
|
|
19
|
-
|
|
20
20
|
### Critical Questions
|
|
21
21
|
|
|
22
|
-
When team structure or boundaries are unclear:
|
|
23
|
-
|
|
24
22
|
1. **Team Structure & Ownership** (Impact: Determines how many swimlanes/systems to create)
|
|
25
23
|
- Question: "How is your organization structured? (A) Single team owns everything, (B) Separate teams by domain (payments, inventory, etc.), (C) Separate teams by function (backend, frontend, etc.)"
|
|
26
24
|
- Why it matters: Team structure directly shapes system boundaries; aligning them reduces coordination overhead
|
|
@@ -36,63 +34,7 @@ When team structure or boundaries are unclear:
|
|
|
36
34
|
- Why it matters: External systems often become separate swimlanes; knowing which ones matters for boundary design
|
|
37
35
|
- Follow-up triggers: For each integration → ask "Who owns the integration—existing team or new team?"
|
|
38
36
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
**Conditional Entry**:
|
|
42
|
-
```
|
|
43
|
-
If user has provided:
|
|
44
|
-
- Clear team structure (who owns what)
|
|
45
|
-
- AND specified desired level of autonomy
|
|
46
|
-
- AND identified external integrations
|
|
47
|
-
|
|
48
|
-
Then: Skip interview, proceed directly to swimlanes
|
|
49
|
-
|
|
50
|
-
Else: Conduct interview
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
**Phase 1: Organization Assessment** (Questions 1-2)
|
|
54
|
-
- Understand team structure
|
|
55
|
-
- Determine autonomy expectations
|
|
56
|
-
- Establish boundary philosophy
|
|
57
|
-
|
|
58
|
-
**Phase 2: Integration Mapping** (Question 3)
|
|
59
|
-
- Identify external systems
|
|
60
|
-
- Plan integration boundaries
|
|
61
|
-
- Finalize swimlane count
|
|
62
|
-
|
|
63
|
-
### Capturing Interview Findings
|
|
64
|
-
|
|
65
|
-
Append findings to the project's event modeling file:
|
|
66
|
-
|
|
67
|
-
**File**: `.trogonai/interviews/[project-name]/EVENTMODELING.md`
|
|
68
|
-
|
|
69
|
-
Use Write tool to add/update this section:
|
|
70
|
-
|
|
71
|
-
```markdown
|
|
72
|
-
## 6. Conway's Law (eventmodeling-applying-conways-law)
|
|
73
|
-
|
|
74
|
-
### Team Structure
|
|
75
|
-
- Team 1: [Name] - Owns [domain]
|
|
76
|
-
- Team 2: [Name] - Owns [domain]
|
|
77
|
-
- Team 3: [Name] - Owns [domain]
|
|
78
|
-
|
|
79
|
-
### Autonomy Goals
|
|
80
|
-
[High / Moderate / Low]
|
|
81
|
-
|
|
82
|
-
### Swimlanes
|
|
83
|
-
- [Swimlane 1]: [Team] owns [events]
|
|
84
|
-
- [Swimlane 2]: [Team] owns [events]
|
|
85
|
-
- [Swimlane 3]: [Team] owns [events]
|
|
86
|
-
|
|
87
|
-
### Cross-Team Communication
|
|
88
|
-
- [Team A] → [Team B] via [event]
|
|
89
|
-
- [Team B] → [Team C] via [event]
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
Update Interview Trail:
|
|
93
|
-
```markdown
|
|
94
|
-
| 6 | eventmodeling-applying-conways-law | [today] | Swimlanes defined, team boundaries confirmed |
|
|
95
|
-
```
|
|
37
|
+
Follow **`eventmodeling-interview-protocol`** to run this interview and record its findings — label this step "**6. Conway's Law** (`eventmodeling-applying-conways-law`)". Findings should cover: team structure and ownership, autonomy goals, the resulting swimlanes (which team owns which events), and cross-team communication paths.
|
|
96
38
|
|
|
97
39
|
---
|
|
98
40
|
|
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-brainstorming-events/SKILL.md
CHANGED
|
@@ -11,18 +11,16 @@ allowed-tools:
|
|
|
11
11
|
|
|
12
12
|
> **Before doing anything else**, invoke the `connect` skill — if not already connected — to resolve `TOKEN`, `BOARD_ID`, `ORG_ID`, and `BASE_URL`. Do not proceed until it has completed. Consult `learn-eventmodelers-api` only if you need to look up a specific endpoint or field this file doesn't cover — don't load it eagerly.
|
|
13
13
|
|
|
14
|
+
This step applies the shared element rules in **`eventmodeling-core-rules`** — read it once per session if you haven't already; it defines what a COMMAND/EVENT/READMODEL/SCREEN/AUTOMATION is, how each is named, and the anti-patterns to reject, so this step doesn't restate them.
|
|
15
|
+
|
|
14
16
|
Prefer `mcp__eventmodelers__*` tools when available (registered by the `connect` skill) — the curl blocks below are the fallback for sessions without MCP connected.
|
|
15
17
|
|
|
16
18
|
## Interview Phase (Optional)
|
|
17
19
|
|
|
18
|
-
**When to Interview**: Skip if the user has provided detailed, well-documented requirements (written user stories, feature specs, business rules). Interview when requirements are vague, incomplete, or
|
|
19
|
-
|
|
20
|
-
**Interview Strategy**: Ensure requirements are complete and team understands domain well enough to brainstorm comprehensively. Identify hidden complexity areas upfront.
|
|
20
|
+
**When to Interview**: Skip if the user has provided detailed, well-documented requirements (written user stories, feature specs, business rules) and named who understands the domain. Interview when requirements are vague, incomplete, or domain expertise is uncertain.
|
|
21
21
|
|
|
22
22
|
### Critical Questions
|
|
23
23
|
|
|
24
|
-
When requirements need clarification:
|
|
25
|
-
|
|
26
24
|
1. **Requirements Completeness** (Impact: Determines if brainstorm is likely to be exhaustive)
|
|
27
25
|
- Question: "How complete are your requirements? Do you have: (A) Written user stories/specs, (B) Documented business rules, (C) Rough list, (D) Just verbal descriptions?"
|
|
28
26
|
- Why it matters: Incomplete requirements cause missed events; complete requirements enable comprehensive brainstorm
|
|
@@ -43,76 +41,7 @@ When requirements need clarification:
|
|
|
43
41
|
- Why it matters: Business rules often generate specific events; documenting them prevents overlooking state changes
|
|
44
42
|
- Follow-up triggers: For each rule → ask "When this rule is violated, what event signals that?"
|
|
45
43
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
**Conditional Entry**:
|
|
49
|
-
```text
|
|
50
|
-
If user has provided:
|
|
51
|
-
- Written requirements or user stories (not just verbal)
|
|
52
|
-
- AND documented business rules or constraints
|
|
53
|
-
- AND named domain experts who will participate
|
|
54
|
-
|
|
55
|
-
Then: Skip interview, proceed directly to brainstorming
|
|
56
|
-
|
|
57
|
-
Else: Conduct interview
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
**Phase 1: Requirements Assessment** (Questions 1-2)
|
|
61
|
-
- Gauge requirements completeness
|
|
62
|
-
- Confirm domain expertise available
|
|
63
|
-
- Adjust brainstorm scope accordingly
|
|
64
|
-
|
|
65
|
-
**Phase 2: Complexity Mapping** (Questions 3-4)
|
|
66
|
-
- Identify areas needing deep exploration
|
|
67
|
-
- Document rules that may generate events
|
|
68
|
-
- Plan brainstorm focus areas
|
|
69
|
-
|
|
70
|
-
### Capturing Interview Findings
|
|
71
|
-
|
|
72
|
-
Append findings to the project's event modeling file:
|
|
73
|
-
|
|
74
|
-
**File**: `.trogonai/interviews/[project-name]/EVENTMODELING.md`
|
|
75
|
-
|
|
76
|
-
Use Write tool to add/update this section:
|
|
77
|
-
|
|
78
|
-
```markdown
|
|
79
|
-
## 2. Brainstormed Events (eventmodeling-brainstorming-events)
|
|
80
|
-
|
|
81
|
-
### Requirements Assessment
|
|
82
|
-
[From Q1: Written requirements? Documented rules?]
|
|
83
|
-
|
|
84
|
-
### Domain Expertise
|
|
85
|
-
[From Q2: Who understands domain? Available for participation?]
|
|
86
|
-
|
|
87
|
-
### Role Catalog
|
|
88
|
-
#### Human Roles
|
|
89
|
-
- [Role 1]: [Description] → Actions: [list]
|
|
90
|
-
- [Role 2]: [Description] → Actions: [list]
|
|
91
|
-
#### System Actors
|
|
92
|
-
- [Actor 1]: [Description] → Triggers: [list]
|
|
93
|
-
|
|
94
|
-
### Event Streams (Stream Roots)
|
|
95
|
-
- Stream: [Name] (Identity: [id field])
|
|
96
|
-
- Events: [Event1, Event2, Event3]
|
|
97
|
-
- State changes: [State transitions]
|
|
98
|
-
|
|
99
|
-
### Business Rules & Constraints
|
|
100
|
-
[From Q3 & Q4]
|
|
101
|
-
- Rule 1: [Statement] → [Events it generates]
|
|
102
|
-
- Rule 2: [Statement] → [Events it generates]
|
|
103
|
-
- Constraint 1: [Limitation]
|
|
104
|
-
|
|
105
|
-
### Brainstorming Focus Areas
|
|
106
|
-
- [Focus area 1]
|
|
107
|
-
- [Focus area 2]
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
Update Interview Trail:
|
|
111
|
-
```markdown
|
|
112
|
-
| 2 | eventmodeling-brainstorming-events | [today] | Event streams, business rules, constraints |
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
This section feeds into subsequent steps (plotting, storyboarding, etc.)
|
|
44
|
+
Follow **`eventmodeling-interview-protocol`** to run this interview and record its findings — label this step "**2. Brainstormed Events** (`eventmodeling-brainstorming-events`)". Findings should cover: requirements completeness, domain expertise available, the Role Catalog, entities/event timelines identified, business rules & constraints, and brainstorming focus areas — this feeds directly into plotting and storyboarding.
|
|
116
45
|
|
|
117
46
|
---
|
|
118
47
|
|
|
@@ -149,6 +78,13 @@ After completing the analysis, partition the full event list into groups where e
|
|
|
149
78
|
|
|
150
79
|
If all events belong to a single flow, one timeline is correct — do not split artificially.
|
|
151
80
|
|
|
81
|
+
**Divergent journey vs. a decision point — do not confuse the two.** A group of events sometimes contains a branch, and the branch's nature decides whether it gets its own chapter or stays inside this one:
|
|
82
|
+
|
|
83
|
+
- **Divergent journey → its own chapter.** The actor makes a different choice *before* the process even starts, and everything downstream differs as a result (e.g. "Checkout with saved card" vs. "Checkout as guest" — different screens, different commands, arguably a different Role Catalog entry). Group these as separate workflows in Step 1, not as one group with a fork in it.
|
|
84
|
+
- **Decision point → stays in this one chapter.** A single trigger — one command's outcome, or one automation's rule — resolves to one of several mutually exclusive results, and the rest of the process is otherwise the same story (e.g. `PaymentAuthorized` succeeds or fails, `OrderConfirmed` vs. `OrderCancelled` after the same confirm action). Keep this as one group; `eventmodeling-plotting-events`'s "Identify Alternative Paths" step is exactly where this branch gets shown, as sibling paths within the same timeline — it never means a second chapter.
|
|
85
|
+
|
|
86
|
+
If unsure which one you're looking at, ask: "does this branch start a genuinely different story, or does it just decide how *this* story ends?" A different story is a new chapter; a different ending is a branch inside this one.
|
|
87
|
+
|
|
152
88
|
### 2. Create one chapter per group
|
|
153
89
|
|
|
154
90
|
For each group, create a chapter on the board **before placing any events**. Reuse an existing chapter if one already matches the workflow name.
|
|
@@ -304,7 +240,7 @@ An event left without a chapter and cell reference will never appear in any time
|
|
|
304
240
|
|
|
305
241
|
**Use swimlanes sparingly — a swimlane exists for exactly one purpose: marking where integration with another system happens. Nothing else justifies one.** Not a different actor, not a different role, not visual grouping, not "an explicit business rule" in the abstract. Every chapter starts with, and in the common case keeps, a single default swimlane holding all of this bounded context's own domain events. Before adding a lane, check whether an existing lane already covers the element's type. If yes, place the element in that lane.
|
|
306
242
|
|
|
307
|
-
**The only valid reason to create a second `swimlane`-type lane: another system's own events cross into this chapter as integration triggers for a translation automation** (see `eventmodeling-designing-automation-chains`, Step 4b). Label it for that system and place its trigger events there — never fold them into this chapter's own event swimlane (they are not this bounded context's domain facts) and never treat them as an informal "signal" with no EVENT node at all. An external EVENT may only ever open the *translation* automation's todo list — never the todo list of the automation that does the actual domain work; that automation is triggered solely by the internal event the translation automation produces (Step 4b covers the full
|
|
243
|
+
**The only valid reason to create a second `swimlane`-type lane: another system's own events cross into this chapter as integration triggers for a translation automation** (see `eventmodeling-designing-automation-chains`, Step 4b). Label it for that system and place its trigger events there — never fold them into this chapter's own event swimlane (they are not this bounded context's domain facts) and never treat them as an informal "signal" with no EVENT node at all. An external EVENT may only ever open the *translation* automation's todo list — never the todo list of the automation that does the actual domain work; that automation is triggered solely by the internal event the translation automation produces, and only exists at all when reacting to that internal event genuinely requires a new decision (Step 4b covers the full translation chain, and when a worker automation belongs after it).
|
|
308
244
|
|
|
309
245
|
**Never** add a swimlane for any other reason — not a new actor, not a new role, not visual grouping. Human roles get their own **actor** lane during Step 3 (Storyboarding) — a different row type entirely — never a new swimlane here.
|
|
310
246
|
|
|
@@ -346,18 +282,18 @@ This catalog feeds directly into:
|
|
|
346
282
|
- **Step 7 (Scenarios)**: Scenarios reference roles by name
|
|
347
283
|
- **Step 8 (Completeness)**: Verify every role has at least one command path
|
|
348
284
|
|
|
349
|
-
### 2. Identify
|
|
350
|
-
Identify the main entities
|
|
285
|
+
### 2. Identify Entities (Event Timelines)
|
|
286
|
+
Identify the main entities whose story will be told as a timeline of events:
|
|
351
287
|
- User/Account
|
|
352
288
|
- Order
|
|
353
289
|
- Payment
|
|
354
290
|
- Shipment
|
|
355
291
|
- etc.
|
|
356
292
|
|
|
357
|
-
For each
|
|
293
|
+
For each entity, note:
|
|
358
294
|
- Name (use domain language, not technical terms)
|
|
359
295
|
- Identity key (what uniquely identifies instances: orderId, paymentId, customerId, etc.)
|
|
360
|
-
- What commands will affect it
|
|
296
|
+
- What commands will affect it
|
|
361
297
|
|
|
362
298
|
### 3. Identify Business Processes
|
|
363
299
|
Map out critical workflows:
|
|
@@ -410,13 +346,13 @@ Present findings in this structure (include facilitation notes for future worksh
|
|
|
410
346
|
- Triggers: [What events/commands it initiates]
|
|
411
347
|
- Communication: [Webhooks / Event-driven / API]
|
|
412
348
|
|
|
413
|
-
##
|
|
414
|
-
List each
|
|
415
|
-
- **
|
|
416
|
-
- **
|
|
417
|
-
- **
|
|
349
|
+
## Entities (Event Timelines)
|
|
350
|
+
List each entity and its identity:
|
|
351
|
+
- **Entity**: Review (Identity: reviewId)
|
|
352
|
+
- **Entity**: SellerResponse (Identity: responseId)
|
|
353
|
+
- **Entity**: Seller (Identity: sellerId)
|
|
418
354
|
|
|
419
|
-
Note: These are just the logical groupings of events
|
|
355
|
+
Note: These are just the logical groupings of events — the story each entity's timeline tells.
|
|
420
356
|
|
|
421
357
|
## Business Processes
|
|
422
358
|
1. **Process Name**: Description
|
|
@@ -439,10 +375,6 @@ Note: These are just the logical groupings of events. The STATE needed for each
|
|
|
439
375
|
## Output Format
|
|
440
376
|
Present analysis in a clear markdown structure that can be directly used by the eventmodeling-designing-event-models skill.
|
|
441
377
|
|
|
442
|
-
## Core Architectural Rule
|
|
443
|
-
|
|
444
|
-
**NEVER use a DDD Aggregate pattern for state design** — every command handler must have its own minimal state projection derived from events. This is non-negotiable. See `eventmodeling-designing-event-models`'s "Core Architectural Rule" for the full anti-pattern/correct-pattern worked example (`OrderAggregate` vs. per-command state) — the rule applies from this very first step, not just once design begins.
|
|
445
|
-
|
|
446
378
|
## Key Principles
|
|
447
379
|
- Use **domain language**, not technical terms
|
|
448
380
|
- Focus on **what** happens, not **how** it's implemented
|
|
@@ -452,9 +384,8 @@ Present analysis in a clear markdown structure that can be directly used by the
|
|
|
452
384
|
- **Collaborative Process**: This is a group brainstorm, not a solo analysis
|
|
453
385
|
- **Rapid Iteration**: Capture quickly, refine later
|
|
454
386
|
- **Gentle Filtering**: Introduce "state-changing events" concept conversationally, not as rigid rule
|
|
455
|
-
- **Event Sourcing Mindset**: Think in terms of immutable events
|
|
456
|
-
- **Defer
|
|
457
|
-
- **Command State Isolation**: Each command handler has its own state shape. Different commands = different state interfaces.
|
|
387
|
+
- **Event Sourcing Mindset**: Think in terms of immutable events grouped by entity, not upfront attribute lists
|
|
388
|
+
- **Defer Detail**: Don't list all entity attributes upfront — commands, preconditions, and read models get worked out in the design step
|
|
458
389
|
|
|
459
390
|
## Best Practices for Requirements Analysis
|
|
460
391
|
|
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-checking-completeness/SKILL.md
CHANGED
|
@@ -10,6 +10,8 @@ allowed-tools:
|
|
|
10
10
|
|
|
11
11
|
> **Before doing anything else**, invoke the `connect` skill — if not already connected — to resolve `TOKEN`, `BOARD_ID`, `ORG_ID`, and `BASE_URL`. Do not proceed until it has completed. Consult `learn-eventmodelers-api` only if you need to look up a specific endpoint or field this file doesn't cover — don't load it eagerly.
|
|
12
12
|
|
|
13
|
+
This step applies the shared element rules in **`eventmodeling-core-rules`** — read it once per session if you haven't already; it defines what a COMMAND/EVENT/READMODEL/SCREEN/AUTOMATION is, how each is named, and the anti-patterns to reject, so this step doesn't restate them.
|
|
14
|
+
|
|
13
15
|
Prefer `mcp__eventmodelers__*` tools when available (registered by the `connect` skill) — the curl blocks below are the fallback for sessions without MCP connected.
|
|
14
16
|
|
|
15
17
|
## Board Context
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: eventmodeling-core-rules
|
|
3
|
+
description: Shared, foundational rules for every event-modeling step — what a COMMAND/EVENT/READMODEL/SCREEN/AUTOMATION is, how each is named, and the anti-patterns to reject. Not a step of its own — referenced upfront by every other eventmodeling-* skill instead of each restating its own copy. Do not use standalone.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Event Modeling — Core Rules
|
|
7
|
+
|
|
8
|
+
These rules apply to every step of event modeling, regardless of which step's SKILL.md you're currently following. Read this once per session — a step's own file only states what's specific to that step, not these fundamentals.
|
|
9
|
+
|
|
10
|
+
## Modes
|
|
11
|
+
|
|
12
|
+
Which step you're in sets the posture — don't mix them.
|
|
13
|
+
|
|
14
|
+
- **Modeling Mode** — `brainstorming-events`, `plotting-events`, `storyboarding-events`, `identifying-inputs`, `identifying-outputs`, `designing-automation-chains`, `elaborating-scenarios`, `translating-external-events`: explore and build. Capture the process as completely as you can. A naming slip or an incomplete precondition is something to flag and move past, not a reason to stall the whole step — don't over-correct early.
|
|
15
|
+
- **Critic Mode** — `validating-event-models`, `validating-event-models-checklist`, `checking-completeness`, `wdyt`, `optimizing-stream-design`: review what's already there. Apply every rule in this file strictly. Surface every violation, gap, and inconsistency — don't soften a finding because the model "mostly works." Post findings as comments (`handle-comment`) rather than silently fixing them, unless the step explicitly says to fix.
|
|
16
|
+
|
|
17
|
+
Don't mix them in one pass: a modeling step is not the place to run a full rules audit, and a critic step is not the place to quietly add missing structure on the model's behalf instead of flagging it.
|
|
18
|
+
|
|
19
|
+
## Lanes — What Lives Where
|
|
20
|
+
|
|
21
|
+
Every column on a timeline has (at most) four rows, and each element type belongs in exactly one:
|
|
22
|
+
|
|
23
|
+
| Lane type | Elements allowed | What it represents |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| `actor` | SCREEN, AUTOMATION | Who or what initiates — a human via a screen, or a system via an automation |
|
|
26
|
+
| `interaction` | COMMAND, READMODEL | The business intent going in (COMMAND), or the query result coming out (READMODEL) |
|
|
27
|
+
| `swimlane` | EVENT | The business fact recorded — which system/entity's own story this event belongs to |
|
|
28
|
+
| `spec` | SCENARIO | The Given/When/Then behavior spec for this column's COMMAND or READMODEL |
|
|
29
|
+
|
|
30
|
+
An EVENT never shares a column with another EVENT even across different swimlane rows — one column is one moment in time. See `place-element` for the full placement mechanics; this table is only "what belongs where," not "how to place it."
|
|
31
|
+
|
|
32
|
+
## Slices
|
|
33
|
+
|
|
34
|
+
A **slice** is the thinnest possible vertical cut through the model — exactly one COMMAND, one READMODEL, or one AUTOMATION's command, never combined:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
state-change slice = SCREEN/AUTOMATION → COMMAND → EVENT(s)
|
|
38
|
+
state-view slice = EVENT(s) → READMODEL → SCREEN/AUTOMATION
|
|
39
|
+
automation slice = EVENT(s) → AUTOMATION → COMMAND → EVENT(s)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
A slice never mixes a COMMAND and a READMODEL — those are two different slice types (`state-change` and `state-view`), even when they belong to the same feature. Slices are independently deployable and only communicate with each other via events.
|
|
43
|
+
|
|
44
|
+
Slices are not placed directly — they're **derived** from a completed model. Every COMMAND on the board implies a `state-change` slice, every READMODEL implies a `state-view` slice, every AUTOMATION implies an `automation` slice. `eventmodeling-slicing-event-models` makes that implicit structure explicit on the board once modeling is done; it never invents a slice that isn't already implied by an element that's there.
|
|
45
|
+
|
|
46
|
+
## Elements
|
|
47
|
+
|
|
48
|
+
### EVENT
|
|
49
|
+
- Represents a business fact that already happened.
|
|
50
|
+
- Naming: past tense, business language.
|
|
51
|
+
- Valid: `OrderPlaced`, `PaymentAuthorized`, `UserRegistered`
|
|
52
|
+
- Invalid: `SidebarOpened`, `RequestCompleted`, `ApiCalled` — these describe machinery or UI state, not business facts
|
|
53
|
+
- Immutable — never modify or delete an event once created; a correction is a new event, not an edit.
|
|
54
|
+
- Contains only captured facts. No computed or derived values — those belong in a READMODEL.
|
|
55
|
+
|
|
56
|
+
### COMMAND
|
|
57
|
+
- Represents business intent — what an actor wants to do.
|
|
58
|
+
- Can be rejected; only succeeds if its documented preconditions hold.
|
|
59
|
+
- Naming: imperative, business language.
|
|
60
|
+
- Valid: `PlaceOrder`, `ConfirmPayment`, `CancelSubscription`
|
|
61
|
+
- Invalid: `LoadOrders`, `FetchData`, `OpenDialog` — these are queries or UI-only actions, not business intent
|
|
62
|
+
- Issued by exactly one SCREEN or AUTOMATION — never two. See `place-element`'s Step 7c for the mechanical check/fix; this rule is stated here, the fix lives there.
|
|
63
|
+
- Produces one or more EVENTs on success, or is rejected with no event.
|
|
64
|
+
|
|
65
|
+
### READMODEL
|
|
66
|
+
- A projection built from events, shaped for a specific query.
|
|
67
|
+
- Naming: noun phrase, business language, named for the data — not the machinery that produces it.
|
|
68
|
+
- Valid: `OrderSummary`, `InvoiceList`
|
|
69
|
+
- Invalid: `OrderSummaryProjector`, `InvoiceListRepository` — implementation detail leaking into the model
|
|
70
|
+
- Optional — nothing requires a READMODEL to exist for a COMMAND or a downstream consumer.
|
|
71
|
+
- Never drives COMMAND validation directly — a COMMAND is checked against its own documented preconditions, not a read model.
|
|
72
|
+
|
|
73
|
+
### SCREEN
|
|
74
|
+
- Represents what a user sees and can act on.
|
|
75
|
+
- Naming: what a user would call it, not its internals.
|
|
76
|
+
- Valid: `Dashboard`, `OrderOverviewPage`
|
|
77
|
+
- Invalid: `OrderOverviewComponent`, `ProjectorView`
|
|
78
|
+
|
|
79
|
+
### AUTOMATION
|
|
80
|
+
- Represents an automated actor — a processor, scheduler, or system reacting to events.
|
|
81
|
+
- Naming: what it does, in business language.
|
|
82
|
+
- Valid: `BillingScheduler`, `InventoryReserver`
|
|
83
|
+
- Invalid: `OrderServiceHandler` — implementation-flavored, not a business actor's name
|
|
84
|
+
|
|
85
|
+
## Causality
|
|
86
|
+
|
|
87
|
+
- Every EVENT traces to the COMMAND that produced it.
|
|
88
|
+
- Every COMMAND traces to the SCREEN or AUTOMATION that issued it — exactly one.
|
|
89
|
+
- A process starts with a read (SCREEN + READMODEL) or an AUTOMATION reacting to an existing EVENT — never with an unmotivated COMMAND.
|
|
90
|
+
|
|
91
|
+
## Role Catalog
|
|
92
|
+
|
|
93
|
+
Every event model needs one, established before brainstorming events (it's the first mandatory step) — without it, downstream steps have no way to say *who* does what:
|
|
94
|
+
|
|
95
|
+
- **Human roles** (Customer, Seller, Admin...) and **system actors** (Payment Gateway, Scheduler...), each with: a name in domain language, a one-line description, its key actions, and what it explicitly cannot do.
|
|
96
|
+
- No COMMAND may be attributed to a generic "User" — it must name a specific role or actor from the catalog.
|
|
97
|
+
- Every COMMAND is issued by exactly one role/actor (this is the same rule as the COMMAND single-issuer rule above, stated from the Role Catalog's side).
|
|
98
|
+
- Every human role needs at least one COMMAND and one READMODEL — a role in the catalog that never acts and never sees anything is decorative, not modeled.
|
|
99
|
+
|
|
100
|
+
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.
|
|
101
|
+
|
|
102
|
+
## Field Lineage
|
|
103
|
+
|
|
104
|
+
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.**
|
|
105
|
+
|
|
106
|
+
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.
|
|
107
|
+
|
|
108
|
+
## Connections Read Forward
|
|
109
|
+
|
|
110
|
+
A connection either goes **downward within the same column** (actor → interaction → swimlane → spec) or **forward to a later column** — never backward (right-to-left). This is why elements that belong together — a SCREEN and the READMODEL it queries, a SCREEN/AUTOMATION and the COMMAND it issues, a COMMAND and the EVENT it produces — should share a column whenever possible: the moment they don't, a backward arrow becomes a real risk.
|
|
111
|
+
|
|
112
|
+
When an element's natural column is already occupied by something else, insert a new column immediately before or after (whichever keeps every connection forward) rather than wiring across the gap. See `place-element` for the mechanical insertion rules and each step's own placement section for where "before" vs. "after" applies.
|
|
113
|
+
|
|
114
|
+
## Translation Chain
|
|
115
|
+
|
|
116
|
+
An AUTOMATION reacting to an event from another system — external (a webhook/API) or second-swimlane (another team's own timeline) — needs a two-stage shape, never a direct reaction:
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
external EVENT → todo-list READMODEL → translation AUTOMATION + COMMAND → internal EVENT → worker automation's own todo list
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
- The external/second-swimlane EVENT opens only a *translation* automation's todo list — never a *worker* automation's todo list directly.
|
|
123
|
+
- A translation automation's own todo list is opened by that external event and is never closed — it doesn't track "done," it only relays.
|
|
124
|
+
- Its resulting COMMAND produces the internal EVENT the domain actually works with — that's the moment external data becomes a business fact in this domain's own language, ready for a worker automation (or anything else) to react to.
|
|
125
|
+
|
|
126
|
+
Every AUTOMATION, translation or worker, needs its own todo-list READMODEL (opened by triggering events, closed by completion events) — no "pure relay" exemption. See `eventmodeling-designing-automation-chains` for the todo-list pattern and board placement mechanics, and `eventmodeling-translating-external-events` for mapping the external payload's raw fields to the internal EVENT's domain fields (correlation, idempotency, duplicate handling).
|
|
127
|
+
|
|
128
|
+
## Open Questions vs. Decided Failures
|
|
129
|
+
|
|
130
|
+
Two different things are easy to conflate, and conflating them corrupts completeness checking:
|
|
131
|
+
|
|
132
|
+
- **Open question** — something genuinely undecided. Post it as a `QUESTION`-type comment (`handle-comment`) on the relevant node. It stays open until someone actually answers it — resolving means answering, not deleting the comment.
|
|
133
|
+
- **Decided failure** — a rejection or error case whose behavior is already decided (e.g. "payment fails → reject, no event"). Model it as a SCENARIO with `expectError: true` and an `errorDescription` (see `eventmodeling-elaborating-scenarios`), never as a comment. It's permanent, specified behavior, not something waiting on an answer.
|
|
134
|
+
|
|
135
|
+
A decided failure left as a lingering `QUESTION` comment looks unresolved when it isn't. When counting completeness (`eventmodeling-checking-completeness`), only a genuinely unanswered `QUESTION` counts as a gap — a decided failure path needs its `expectError` scenario, not a comment standing in for one.
|
|
136
|
+
|
|
137
|
+
## Offline-First Thinking
|
|
138
|
+
|
|
139
|
+
Model the business process as it would work without any software first, then translate to elements:
|
|
140
|
+
|
|
141
|
+
- How would this work manually, with people, on paper?
|
|
142
|
+
- Who acts, and what triggers each action?
|
|
143
|
+
- What information do they need before they can act?
|
|
144
|
+
|
|
145
|
+
Question any step that exists only because of the system — a loading spinner, a cache refresh, a session check is not a business step and does not belong on the timeline. This is the discipline behind the Anti-Patterns below; the anti-patterns are what it looks like when this principle is skipped.
|
|
146
|
+
|
|
147
|
+
## Anti-Patterns
|
|
148
|
+
|
|
149
|
+
- **Data-loading commands**: `LoadOrders`/`FetchData` are queries, not business intent — model as a READMODEL, not a COMMAND.
|
|
150
|
+
- **UI-interaction events**: `SidebarOpened`/`ButtonClicked` are UI state, not business facts — don't model them unless the business genuinely cares.
|
|
151
|
+
- **Technical events**: `ApiCalled`/`ResponseReceived` describe machinery, not an outcome — find the business fact underneath.
|
|
152
|
+
- **Calculated events**: an event whose value is recomputed as source data changes (a running total, an average) is a READMODEL, not an EVENT.
|
|
153
|
+
|
|
154
|
+
## Structural Shapes
|
|
155
|
+
|
|
156
|
+
Beyond the local anti-patterns above, four recurring **connection shapes** are worth checking whenever the model's wiring is visible — a fan-out or fan-in count crossing a rough threshold. Only one is a real anti-pattern regardless of context; the other three are **candidates** — worth checking against the business context, not automatically wrong. Reason about the actual events/fields/scenarios involved before flagging a candidate; if the domain justifies the count, it's not a violation. These are internal shorthand names for spotting the shape — never surface "bed", "left chair", "right chair", or "shelf" in anything shown to a business stakeholder (a board comment, a report); describe the concern in plain terms instead.
|
|
157
|
+
|
|
158
|
+
- **The bed (real anti-pattern — always flag)** — one SCREEN wired to more than one COMMAND. A screen is where the user has already committed to one decision, so it should trigger exactly one command; wiring several to it means the choice is being made somewhere invisible to the model.
|
|
159
|
+
- **The left chair (candidate)** — one COMMAND resulting in more than two EVENTs. May mean the command is doing more than one job, or it may be a single business outcome that legitimately fans out. Check whether the outcomes always happen together or could happen independently before treating it as a concern.
|
|
160
|
+
- **The right chair (candidate)** — one READMODEL built from more than three EVENTs. May mean the view is answering more than one question at once, or it may be one coherent picture that genuinely needs that many sources. Check whether the fields shown belong to a single thing the user is checking before treating it as a concern.
|
|
161
|
+
- **The shelf (candidate)** — one slice with noticeably more SCENARIOs than the others on the same timeline (a rough outlier, not a fixed threshold — compare against the typical count for that timeline). May mean the step is quietly covering ground that belongs to a separate step, or it may just be genuinely more complex. Check what the extra scenarios actually cover before treating it as a concern.
|
|
162
|
+
|
|
163
|
+
## Flow & Causality
|
|
164
|
+
|
|
165
|
+
Model causality, not strict sequence:
|
|
166
|
+
|
|
167
|
+
- A timeline starts with a **state-view** (READMODEL feeding a SCREEN) or an **automation** reacting to an EVENT already on the board — never with an unmotivated COMMAND.
|
|
168
|
+
- Every COMMAND must be traceable to a trigger: a SCREEN (a user decision) or an AUTOMATION (a system reaction).
|
|
169
|
+
- A `state-change` slice must not follow another `state-change` slice without a trigger in between (a new SCREEN or AUTOMATION) — two COMMANDs in a row with nothing issuing the second one is a gap, not a shortcut.
|
|
170
|
+
|
|
171
|
+
Valid transitions between slices:
|
|
172
|
+
- state-view → state-change
|
|
173
|
+
- state-change → state-view
|
|
174
|
+
- state-change → automation
|
|
175
|
+
- automation → state-change
|
|
176
|
+
- automation → state-view
|
|
177
|
+
|
|
178
|
+
## Quick Self-Check
|
|
179
|
+
|
|
180
|
+
Before treating a model as done, verify:
|
|
181
|
+
|
|
182
|
+
- [ ] Every EVENT is past tense and describes a business fact, not machinery or UI state
|
|
183
|
+
- [ ] Every COMMAND is imperative business intent, not a data-loading query
|
|
184
|
+
- [ ] Every READMODEL is named for its data, not its implementation
|
|
185
|
+
- [ ] No EVENT holds a computed/aggregated value — that belongs in a READMODEL
|
|
186
|
+
- [ ] Every COMMAND has exactly one issuer (one inbound SCREEN or AUTOMATION edge)
|
|
187
|
+
- [ ] Every COMMAND traces back to a SCREEN (user decision) or AUTOMATION (system reaction)
|
|
188
|
+
- [ ] The timeline starts with a state-view or an automation reacting to an event — not a bare COMMAND
|
|
189
|
+
- [ ] No two `state-change`/`state-change` slices are chained without a new trigger between them
|
|
190
|
+
- [ ] No SCREEN is wired to more than one COMMAND (the bed — see Structural Shapes)
|
|
191
|
+
|
|
192
|
+
This is the fast pass — the full, deeper check is `eventmodeling-validating-event-models-checklist`.
|
|
@@ -11,6 +11,8 @@ allowed-tools:
|
|
|
11
11
|
|
|
12
12
|
> **Before doing anything else**, invoke the `connect` skill — if not already connected — to resolve `TOKEN`, `BOARD_ID`, `ORG_ID`, and `BASE_URL`. Do not proceed until it has completed. Consult `learn-eventmodelers-api` only if you need to look up a specific endpoint or field this file doesn't cover — don't load it eagerly.
|
|
13
13
|
|
|
14
|
+
This step applies the shared element rules in **`eventmodeling-core-rules`** — read it once per session if you haven't already; it defines what a COMMAND/EVENT/READMODEL/SCREEN/AUTOMATION is, how each is named, and the anti-patterns to reject, so this step doesn't restate them.
|
|
15
|
+
|
|
14
16
|
Prefer `mcp__eventmodelers__*` tools when available (registered by the `connect` skill) — the curl blocks below are the fallback for sessions without MCP connected.
|
|
15
17
|
|
|
16
18
|
This is **Step 4b**. It runs immediately after `eventmodeling-identifying-inputs` (Step 4) places an AUTOMATION and its COMMAND, and before `eventmodeling-identifying-outputs` (Step 5) designs any screen-facing read model. Designing an automation's todo list — and, when it applies, its translation chain — while the automation is still fresh from Step 4 is what prevents the alternative: discovering the gap only once Step 5's own verification pass runs, after commands and connections already built on top of the incomplete shape.
|
|
@@ -39,15 +41,27 @@ Even an automation that looks like a "pure signal relay" still has a todo list
|
|
|
39
41
|
|
|
40
42
|
**There is no such thing as an invisible or informal "signal" — a trigger is always a real EVENT node**, placed in a second swimlane when it belongs to another system (see `eventmodeling-brainstorming-events`'s swimlane rule).
|
|
41
43
|
|
|
42
|
-
**An automation can only ever be directly triggered by an internal event — never by another system's event.** A "trigger" arriving from a second swimlane is not itself the thing that drives your domain's work; it first has to be *translated* into an internal event. Do not model this as one automation whose todo list is opened by the external EVENT and that also does the real work (e.g. an automation reading a todo list opened by `ReservationRequested` from another system's swimlane and directly issuing `ReserveCopy`) — that lets an external system trigger domain work with no translation step, which this model doesn't allow. **Apply this now, in Step 4b** — Conway's Law (Step 6) only confirms the boundary, it doesn't introduce the chain.
|
|
44
|
+
**An automation can only ever be directly triggered by an internal event — never by another system's event.** A "trigger" arriving from a second swimlane is not itself the thing that drives your domain's work; it first has to be *translated* into an internal event. Do not model this as one automation whose todo list is opened by the external EVENT and that also does the real work (e.g. an automation reading a todo list opened by `ReservationRequested` from another system's swimlane and directly issuing `ReserveCopy`) — that lets an external system trigger domain work with no translation step, which this model doesn't allow. **Apply this now, in Step 4b** — Conway's Law (Step 6) only confirms the boundary, it doesn't introduce the chain. Every integration trigger from another team's system needs a **translation automation** — that part is never optional:
|
|
43
45
|
|
|
44
46
|
1. **Translation automation** — converts the external fact into an internal one; the other system's own decision logic is out of scope. Its todo list is opened by the external EVENT — the one and only place an external EVENT may open a todo list. **It is not closed by the internal EVENT its own command produces, and gets no backward arrow.** A translation automation's job is an instantaneous, always-succeeding relay: there is no real work-in-progress window between "signal arrived" and "internal fact recorded" worth modeling as an open/close lifecycle, unlike a worker automation that can genuinely have pending items. This read model exists only to satisfy "every automation reads from a todo list," not to accumulate and drain a queue — so it stays open-ended: fed by the external EVENT, read by the automation, never closed.
|
|
45
47
|
- **Three separate columns**, left to right: `[external EVENT] → [todo-list READMODEL] → [AUTOMATION + COMMAND + internal EVENT]`. Never crammed into one or two — the "one EVENT per column" rule applies here too.
|
|
46
48
|
- **Name the internal EVENT for its business meaning, not the transport** — usually the same name as the external EVENT (e.g. external `CopyReserved` → internal `CopyReserved`; the swimlane already shows which is which), never a mechanical `<X>SignalReceived`/`<X>RequestReceived` suffix. Same for the automation/command: `Record Reservation`/`RecordReservation`, not `Record Reservation Signal`/`RecordReservationSignal`.
|
|
47
|
-
- Its command and event carry no business decision — they only exist to produce the internal fact
|
|
48
|
-
|
|
49
|
+
- Its command and event carry no business decision — they only exist to produce the internal fact a worker automation would need, *if one is even needed* (see below).
|
|
50
|
+
|
|
51
|
+
### Does a worker automation come after it, or does the chain stop there?
|
|
52
|
+
|
|
53
|
+
The translation automation never decides anything — it only records a fact. Whether something needs to happen *after* that fact depends on whether there is still a decision left to make:
|
|
54
|
+
|
|
55
|
+
- **Build a worker automation only when reacting to the internal EVENT requires a genuinely new decision on this side** — an invariant to check, a choice to make, data to compute that the internal EVENT doesn't already carry. That decision is what earns its own COMMAND, which is what earns the automation that issues it (see item 2 below for its shape).
|
|
56
|
+
- **Do not build one when the internal EVENT already *is* the business-relevant fact.** If the external system already made every decision involved, and the "next" EVENT you were about to model would just restate data the internal EVENT already carries under a different name (same identity, no new field, no invariant checked, no choice made) — stop after the translation automation. That is not a second stage of the same chain; it is no chain at all, just a fact recorded once. Anything else that needs this fact reads the internal EVENT directly through a state-view READMODEL projection — it does not get an automated command reaction bolted on solely to keep a fixed "two automations" shape.
|
|
57
|
+
|
|
58
|
+
*Example*: an external `ReservationPlaced` opens a translation chain and already carries `reservationId`, `copyId`, and `memberId` — the copy to reserve was chosen upstream, by the other system. Translating it into an internal `ReservationPlaced` is still required (per the rule above — an automation may never react to the external event directly). But adding a second `ReservationsToApply` todo list + `Reserve Copy` automation + `CopyReserved` event on top of it is redundant: `CopyReserved` doesn't decide anything `ReservationPlaced` didn't already decide, it's the same fact under a different name. A read model showing "reserved copies" should project directly off the internal `ReservationPlaced` event, not off a manufactured second automation stage.
|
|
59
|
+
|
|
60
|
+
2. **Worker automation** (only when the check above says one is needed) — the one that does the actual work (a real domain decision the process is about, e.g. checking copy availability before issuing `ReserveCopy`). Its todo list is opened **only** by the internal EVENT the translation automation produced (this chapter's own swimlane) — never by the external EVENT directly — and closed by whatever event marks that work done. Unlike the translation stage, the worker's todo list keeps its normal open/close accumulator shape, because real pending work can sit there (it's the point where actual domain decisions happen).
|
|
61
|
+
|
|
62
|
+
**Two chained automations only when there genuinely are two decisions** — the translation (none, by definition) and the worker (a real one). Whether a worker stage exists at all is a judgment call driven by whether that second decision actually exists, not a fixed shape to stamp onto every cross-swimlane trigger.
|
|
49
63
|
|
|
50
|
-
|
|
64
|
+
When a worker automation *is* warranted, wire the todo lists differently for each automation in the chain: for the translation automation, the external EVENT (second swimlane) **opens** the row and nothing closes it; for the worker automation, the internal EVENT **opens** its row and its own resulting EVENT **closes** it (backward arrow, per the exemption below). `EVENT → READMODEL` connections from both swimlanes are unaffected by which swimlane the event sits in.
|
|
51
65
|
|
|
52
66
|
## Fields on a todo-list read model
|
|
53
67
|
|
|
@@ -116,8 +130,9 @@ Run `validate_model` (`{boardId, chapterId}`) first — with the chain placed `a
|
|
|
116
130
|
1. Does it have an incoming `READMODEL → AUTOMATION` connection to a todo-list read model? An AUTOMATION is **never** exempt — if not, design and wire it now.
|
|
117
131
|
2. Is its todo list opened by an internal event only, **unless it is itself a translation automation** (which is the one case a second-swimlane event may open a todo list)? If a worker automation's todo list is opened directly by another system's event, that automation is missing its translation chain — split it into translation + worker automations per the rule above.
|
|
118
132
|
3. If it's a **translation** automation, does its todo list have no closing edge — i.e. no connection from its own resulting internal EVENT back to its own todo-list read model? If one exists, remove it: a translation automation's todo list is never closed.
|
|
119
|
-
4. If it's a **worker** automation, does its
|
|
120
|
-
5.
|
|
133
|
+
4. If it's a **worker** automation sitting right after a translation automation, does its own COMMAND make a genuinely new decision the internal EVENT didn't already carry? If its fields and logic just restate the internal EVENT's own data under a new name, it's a redundant stage — remove the worker automation, its todo list, and its resulting event, and have downstream consumers project a state-view READMODEL directly off the internal EVENT instead.
|
|
134
|
+
5. If it's a real worker automation, does its todo list have a proper closing edge from its own resulting event? If not, add it.
|
|
135
|
+
6. Does the todo-list read model's field set avoid a `status` flag (membership in the list is the state)?
|
|
121
136
|
|
|
122
137
|
List the result (connected / chain-resolved) for every automation checked — this is the evidence that `eventmodeling-identifying-outputs`'s later per-node verification pass (which re-checks automations defensively) finds nothing left to do here.
|
|
123
138
|
|
|
@@ -125,7 +140,8 @@ List the result (connected / chain-resolved) for every automation checked — th
|
|
|
125
140
|
|
|
126
141
|
- [ ] Every AUTOMATION has an incoming `READMODEL → AUTOMATION` connection to a todo-list read model — no exemption, even for a simple relay
|
|
127
142
|
- [ ] No automation's todo list is opened directly by another system's (second-swimlane) EVENT unless that automation is itself the translation automation
|
|
128
|
-
- [ ] Every externally-triggered automation
|
|
143
|
+
- [ ] Every externally-triggered automation has a translation automation ahead of it — never triggered by the external EVENT directly
|
|
144
|
+
- [ ] A worker automation exists after the translation stage only when it makes a genuinely new decision — not merely to keep a fixed "two automations" shape when the internal EVENT already is the business-relevant fact
|
|
129
145
|
- [ ] Every **worker**-stage todo-list read model's opening and closing events are identified, including the automation's own resulting event as a closing event
|
|
130
146
|
- [ ] Every **translation**-stage todo-list read model has an opening event (the external EVENT) and **no closing event at all** — no backward arrow from its own internal EVENT back to its own todo list
|
|
131
147
|
- [ ] No todo-list read model uses a `status` field instead of list membership
|